Skip to content
Next Next commit
Shadow StaticType, ThisType, GenericStaticType, ObjectWithoutClassTyp…
…e, ObjectShapeType and NonexistentParentClassType natively

StaticType (TypeWithClassName + SubtractableType, delegating to its
ObjectType through the object's class entry so the GenericStaticType and
ThisType overrides hold), its two children, ObjectWithoutClassType,
ObjectShapeType and NonexistentParentClassType. Four more trait
registrars; the twins' closures become Closures over internal holder
classes. The type-family differential gains the object-ish subjects over
real reflections (68k observations). The Windows source list in
config.w32, duplicated by the previous merge, is regenerated from src/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
  • Loading branch information
ondrejmirtes and claude committed Sep 21, 2026
commit de8d809b5c37ac47cc30f7541e19f7ef4d561b9e
6 changes: 6 additions & 0 deletions build/PHPStan/Build/TurboAttributeCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Turbo\ShadowedByTurboExtension;
use RecursiveDirectoryIterator;
Expand Down Expand Up @@ -122,6 +125,9 @@ final class TurboAttributeCollector
'netteStrings' => Strings::class,
'netteRegexpException' => RegexpException::class,
'constExprFloatNode' => ConstExprFloatNode::class,
'thisTypeNode' => ThisTypeNode::class,
'objectShapeNode' => ObjectShapeNode::class,
'objectShapeItemNode' => ObjectShapeItemNode::class,
];

private string $realRoot;
Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/MissingPropertyFromReflectionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace PHPStan\Reflection;

use Exception;
use PHPStan\Turbo\ReferencedByTurboExtension;
use function sprintf;

#[ReferencedByTurboExtension(key: 'missingPropertyFromReflectionException')]
final class MissingPropertyFromReflectionException extends Exception
{

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
use PHPStan\Reflection\PropertiesClassReflectionExtension;
use PHPStan\Reflection\PropertyReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\MixedType;

// autoTag: false - wired explicitly in ClassReflectionExtensionRegistry, must not be tagged
#[AutowiredService(autoTag: false)]
#[ReferencedByTurboExtension(key: 'universalObjectCratesClassReflectionExtension')]
final class UniversalObjectCratesClassReflectionExtension
implements PropertiesClassReflectionExtension
{
Expand Down
2 changes: 2 additions & 0 deletions src/Type/Accessory/HasPropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\AcceptsResult;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CompoundType;
Expand Down Expand Up @@ -35,6 +36,7 @@
use function sprintf;

#[InstanceofDeprecated(insteadUse: 'Type::hasProperty()')]
#[ReferencedByTurboExtension(key: 'hasPropertyType')]
class HasPropertyType implements AccessoryType, CompoundType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Generic/GenericObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\AcceptsResult;
use PHPStan\Type\CompoundType;
use PHPStan\Type\ErrorType;
Expand All @@ -32,6 +33,7 @@

/** @api */
#[InstanceofDeprecated]
#[ReferencedByTurboExtension(key: 'genericObjectType')]
class GenericObjectType extends ObjectType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Generic/GenericStaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\CompoundType;
use PHPStan\Type\IsSuperTypeOfResult;
use PHPStan\Type\NeverType;
Expand All @@ -21,6 +22,7 @@
use function count;

/** @api */
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../../turbo-ext/src/GenericStaticType.cpp')]
class GenericStaticType extends StaticType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Generic/TemplateTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Type\Generic;

use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\ErrorType;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\NarrowedSubjectType;
Expand All @@ -12,6 +13,7 @@
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\VerbosityLevel;

#[ReferencedByTurboExtension(key: 'templateTypeHelper')]
final class TemplateTypeHelper
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Generic/TemplateTypeVarianceMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace PHPStan\Type\Generic;

use PHPStan\Turbo\ReferencedByTurboExtension;
use function array_key_exists;

/**
* @api
*/
#[ReferencedByTurboExtension(key: 'templateTypeVarianceMap')]
final class TemplateTypeVarianceMap
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/NonexistentParentClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Traits\NonArrayTypeTrait;
Expand All @@ -26,6 +27,7 @@
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;

#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/NonexistentParentClassType.cpp')]
class NonexistentParentClassType implements Type
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/ObjectShapePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use stdClass;

#[ReferencedByTurboExtension(key: 'objectShapePropertyReflection')]
final class ObjectShapePropertyReflection implements ExtendedPropertyReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/ObjectShapeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Accessory\HasPropertyType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantStringType;
Expand All @@ -38,6 +39,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isObject() and Type::hasProperty()')]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/ObjectShapeType.cpp')]
class ObjectShapeType implements Type
{

Expand Down
4 changes: 2 additions & 2 deletions src/Type/ObjectWithoutClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
use PHPStan\Type\Traits\NonGenericTypeTrait;
Expand All @@ -14,7 +14,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isObject()')]
#[ReferencedByTurboExtension(key: 'objectWithoutClassType')]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/ObjectWithoutClassType.cpp')]
class ObjectWithoutClassType implements SubtractableType
{

Expand Down
4 changes: 2 additions & 2 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\GenericClassStringType;
Expand All @@ -29,7 +29,7 @@
use function sprintf;

/** @api */
#[ReferencedByTurboExtension(key: 'staticType')]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/StaticType.cpp')]
class StaticType implements TypeWithClassName, SubtractableType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/ThisType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Constant\ConstantStringType;
use function sprintf;

/** @api */
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/ThisType.cpp')]
class ThisType extends StaticType
{

Expand Down
3 changes: 3 additions & 0 deletions src/Type/TypeTraverser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace PHPStan\Type;

use PHPStan\Turbo\ReferencedByTurboExtension;

#[ReferencedByTurboExtension(key: 'typeTraverser')]
final class TypeTraverser
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/TypeWithClassName.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Type;

use PHPStan\Reflection\ClassReflection;
use PHPStan\Turbo\ReferencedByTurboExtension;

/**
* A Type that represents an object with a known class name.
Expand All @@ -22,6 +23,7 @@
* @api-do-not-implement
*/
#[InstanceofDeprecated(insteadUse: 'Type::getObjectClassNames() or Type::getObjectClassReflections()')]
#[ReferencedByTurboExtension(key: 'typeWithClassName')]
interface TypeWithClassName extends Type
{

Expand Down
2 changes: 1 addition & 1 deletion turbo-ext/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if (PHP_PHPSTAN_TURBO != "no") {
// directory by splitting on backslashes only — with forward slashes the
// objects compile flat while the link list expects the subpath
EXTENSION("phpstan_turbo",
"src\\main.cpp src\\support.cpp src\\AcceptsResult.cpp src\\ArenaCache.cpp src\\BooleanType.cpp src\\ClassStringType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IsSuperTypeOfResult.cpp src\\MixedType.cpp src\\NeverType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NullType.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StrictMixedType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinatorCache.cpp src\\TypeTraits.cpp src\\VoidType.cpp",
"src\\main.cpp src\\support.cpp src\\AcceptsResult.cpp src\\ArenaCache.cpp src\\BooleanType.cpp src\\ClassStringType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\GenericStaticType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IsSuperTypeOfResult.cpp src\\MixedType.cpp src\\NeverType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NonexistentParentClassType.cpp src\\NullType.cpp src\\ObjectShapeType.cpp src\\ObjectWithoutClassType.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StaticType.cpp src\\StrictMixedType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\ThisType.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinatorCache.cpp src\\TypeTraits.cpp src\\VoidType.cpp",
true,
flags);
ADD_SOURCES(configure_module_dirname + "/src/parser",
Expand Down
4 changes: 2 additions & 2 deletions turbo-ext/src/ClassStringType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class ClassStringType
static zend_long isClassString() { return PT_TRI_YES; }

/* new ObjectWithoutClassType() */
static zv::Val getClassStringObjectType() { return pt_type_new(PT_CLASS_OBJECT_WITHOUT_CLASS_TYPE, 0, NULL); }
static zv::Val getObjectTypeOrClassStringObjectType() { return pt_type_new(PT_CLASS_OBJECT_WITHOUT_CLASS_TYPE, 0, NULL); }
static zv::Val getClassStringObjectType() { return pt_type_new_object_without_class_type(); }
static zv::Val getObjectTypeOrClassStringObjectType() { return pt_type_new_object_without_class_type(); }

/* new IdentifierTypeNode('class-string') */
static zv::Val toPhpDocNode()
Expand Down
4 changes: 2 additions & 2 deletions turbo-ext/src/ConstantStringType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ class ConstantStringType
zv::Val genericType = pt_type_call(Z_OBJ_P(type), PT_LC("getgenerictype"), 0, NULL);
if (UNEXPECTED(genericType.isUndef())) return zv::Val();
if (zv::Ref(genericType.raw()).instanceOf(pt_ce_mixed_type)) return pt_type_is_super_type_of_result(PT_TRI_MAYBE);
bool isStatic;
if (UNEXPECTED(!pt_type_instanceof(genericType.raw(), PT_CLASS_STATIC_TYPE, isStatic))) return zv::Val();
/* $genericType instanceof StaticType — the shadowing class */
bool isStatic = zv::Ref(genericType.raw()).instanceOf(pt_ce_static_type);
if (isStatic) {
genericType = pt_type_call(Z_OBJ_P(genericType.raw()), PT_LC("getstaticobjecttype"), 0, NULL);
if (UNEXPECTED(genericType.isUndef())) return zv::Val();
Expand Down
10 changes: 5 additions & 5 deletions turbo-ext/src/GenericClassStringType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class GenericClassStringType
if (UNEXPECTED(other == NULL)) return zv::Val();
objectType = zv::Val::copyOf(zv::Ref(other));
} else if (instanceof_function(typeCe, pt_ce_class_string_type)) {
objectType = pt_type_new(PT_CLASS_OBJECT_WITHOUT_CLASS_TYPE, 0, NULL);
objectType = pt_type_new_object_without_class_type();
} else if (instanceof_function(typeCe, pt_ce_string_type)) {
return pt_type_accepts_result(PT_TRI_MAYBE);
} else {
Expand Down Expand Up @@ -176,8 +176,8 @@ class GenericClassStringType
zv::Val genericType = zv::Val::copyOf(zv::Ref(ownType));
if (zv::Ref(genericType.raw()).instanceOf(pt_ce_mixed_type)) return pt_type_is_super_type_of_result(PT_TRI_YES);

bool isStatic;
if (UNEXPECTED(!pt_type_instanceof(genericType.raw(), PT_CLASS_STATIC_TYPE, isStatic))) return zv::Val();
/* $genericType instanceof StaticType — the shadowing class */
bool isStatic = zv::Ref(genericType.raw()).instanceOf(pt_ce_static_type);
if (isStatic) {
genericType = pt_type_call(Z_OBJ_P(genericType.raw()), PT_LC("getstaticobjecttype"), 0, NULL);
if (UNEXPECTED(genericType.isUndef())) return zv::Val();
Expand Down Expand Up @@ -220,7 +220,7 @@ class GenericClassStringType
return callOnType(PT_LC("issupertypeof"), 1, other);
}
if (instanceof_function(typeCe, pt_ce_class_string_type)) {
zv::Val objectWithoutClass = pt_type_new(PT_CLASS_OBJECT_WITHOUT_CLASS_TYPE, 0, NULL);
zv::Val objectWithoutClass = pt_type_new_object_without_class_type();
if (UNEXPECTED(objectWithoutClass.isUndef())) return zv::Val();
return callOnType(PT_LC("issupertypeof"), 1, objectWithoutClass.raw());
}
Expand Down Expand Up @@ -294,7 +294,7 @@ class GenericClassStringType
typeToInfer = pt_type_call(Z_OBJ_P(typeToInfer.raw()), PT_LC("getbound"), 0, NULL);
if (UNEXPECTED(typeToInfer.isUndef())) return zv::Val();
}
zv::Val objectWithoutClass = pt_type_new(PT_CLASS_OBJECT_WITHOUT_CLASS_TYPE, 0, NULL);
zv::Val objectWithoutClass = pt_type_new_object_without_class_type();
if (UNEXPECTED(objectWithoutClass.isUndef())) return zv::Val();
zv::Args args{typeToInfer.raw(), objectWithoutClass.raw()};
typeToInfer = pt_type_call_static(PT_CLASS_TYPE_COMBINATOR, PT_LC("intersect"), 2, args);
Expand Down
Loading