Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Shadow NeverType, MixedType and StrictMixedType natively
NeverType (CompoundType), MixedType (CompoundType + SubtractableType with
the subtracted-type logic; a proper native constructor so the PHP
subclasses ErrorType and TemplateMixedType keep calling parent::__construct)
and StrictMixedType. Three more trait registrars; the identity closures the
twins hand to the callback prototype reflections are Closures over an
internal static method. The type-family differential gains 30 subjects
including the PHP subclasses over the native parents (55k observations).

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 7cfedfbdc3cae38de3077d249b22b69d1b833bc8
2 changes: 2 additions & 0 deletions src/Reflection/Dummy/DummyClassConstantReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use stdClass;
use function sprintf;

#[ReferencedByTurboExtension(key: 'dummyClassConstantReflection')]
final class DummyClassConstantReflection implements ClassConstantReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Reflection/Dummy/DummyMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\Reflection\TrivialParametersAcceptor;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Type;
use stdClass;

#[ReferencedByTurboExtension(key: 'dummyMethodReflection')]
final class DummyMethodReflection implements ExtendedMethodReflection
{

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

#[ReferencedByTurboExtension(key: 'dummyPropertyReflection')]
final class DummyPropertyReflection implements ExtendedPropertyReflection
{

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
use PHPStan\Reflection\ExtendedParametersAcceptor;
use PHPStan\Reflection\Php\ExtendedDummyParameter;
use PHPStan\Reflection\ResolvedMethodReflection;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function array_map;

#[ReferencedByTurboExtension(key: 'callbackUnresolvedMethodPrototypeReflection')]
final class CallbackUnresolvedMethodPrototypeReflection implements UnresolvedMethodPrototypeReflection
{

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
use PHPStan\Reflection\Dummy\ChangedTypePropertyReflection;
use PHPStan\Reflection\ExtendedPropertyReflection;
use PHPStan\Reflection\ResolvedPropertyReflection;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Type;

#[ReferencedByTurboExtension(key: 'callbackUnresolvedPropertyPrototypeReflection')]
final class CallbackUnresolvedPropertyPrototypeReflection implements UnresolvedPropertyPrototypeReflection
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Accessory/AccessoryArrayListType.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\ArrayType;
use PHPStan\Type\BooleanType;
Expand All @@ -31,6 +32,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isList()')]
#[ReferencedByTurboExtension(key: 'accessoryArrayListType')]
class AccessoryArrayListType implements CompoundType, AccessoryType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/Accessory/OversizedArrayType.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\ArrayType;
use PHPStan\Type\BooleanType;
Expand All @@ -29,6 +30,7 @@
use PHPStan\Type\VerbosityLevel;

#[InstanceofDeprecated(insteadUse: 'Type::isOversizedArray()')]
#[ReferencedByTurboExtension(key: 'oversizedArrayType')]
class OversizedArrayType implements CompoundType, AccessoryType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Rules\Arrays\AllowedArrayKeysTypes;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryArrayListType;
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
Expand Down Expand Up @@ -51,6 +52,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isArray() or Type::getArrays()')]
#[ReferencedByTurboExtension(key: 'arrayType')]
class ArrayType implements Type
{

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

use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Generic\TemplateTypeMap;
use function count;

/** @api */
#[ReferencedByTurboExtension(key: 'benevolentUnionType')]
class BenevolentUnionType extends UnionType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/CallableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use PHPStan\Reflection\Php\DummyParameter;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\TemplateType;
Expand All @@ -48,6 +49,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isCallable() and Type::getCallableParametersAcceptors()')]
#[ReferencedByTurboExtension(key: 'callableType')]
class CallableType implements CompoundType, CallableParametersAcceptor
{

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

namespace PHPStan\Type\Generic;

use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\AcceptsResult;
use PHPStan\Type\IsSuperTypeOfResult;
use PHPStan\Type\MixedType;
use PHPStan\Type\StrictMixedType;
use PHPStan\Type\Type;

/** @api */
#[ReferencedByTurboExtension(key: 'templateMixedType')]
final class TemplateMixedType extends MixedType implements TemplateType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/IterableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateMixedType;
Expand All @@ -26,6 +27,7 @@

/** @api */
#[InstanceofDeprecated(insteadUse: 'Type::isIterable()')]
#[ReferencedByTurboExtension(key: 'iterableType')]
class IterableType implements CompoundType
{

Expand Down
4 changes: 2 additions & 2 deletions src/Type/MixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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\AccessoryArrayListType;
use PHPStan\Type\Accessory\AccessoryDecimalIntegerStringType;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
Expand All @@ -46,7 +46,7 @@
use function sprintf;

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

Expand Down
4 changes: 2 additions & 2 deletions src/Type/NeverType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
Expand All @@ -24,7 +24,7 @@
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;

/** @api */
#[ReferencedByTurboExtension(key: 'neverType')]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/NeverType.cpp')]
class NeverType implements CompoundType
{

Expand Down
2 changes: 2 additions & 0 deletions src/Type/StrictMixedType.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\Constant\ConstantBooleanType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\TemplateMixedType;
Expand All @@ -25,6 +26,7 @@
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;

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

Expand Down
3 changes: 3 additions & 0 deletions src/Type/SubtractableType.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: 'subtractableType')]
interface SubtractableType 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\\ConstantBooleanType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ConditionalExpressionHolder.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\GenericClassStringType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IsSuperTypeOfResult.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinatorCache.cpp src\\TypeTraits.cpp src\\FloatType.cpp src\\ConstantFloatType.cpp src\\NullType.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\\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",
true,
flags);
ADD_SOURCES(configure_module_dirname + "/src/parser",
Expand Down
4 changes: 2 additions & 2 deletions turbo-ext/src/ConstantBooleanType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class ConstantBooleanType
bool v = false;
if (UNEXPECTED(!value(v))) return zv::Val();
if (v) return falsey();
return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
return pt_type_new_never_type();
}

/* new MixedType() for true, StaticTypeFactory::falsey() for false */
Expand All @@ -99,7 +99,7 @@ class ConstantBooleanType
{
bool v = false;
if (UNEXPECTED(!value(v))) return zv::Val();
if (v) return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
if (v) return pt_type_new_never_type();
return truthy();
}

Expand Down
6 changes: 2 additions & 4 deletions turbo-ext/src/ConstantStringType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,7 @@ class ConstantStringType
if (instanceof_function(typeCe, pt_ce_generic_class_string_type)) {
zv::Val genericType = pt_type_call(Z_OBJ_P(type), PT_LC("getgenerictype"), 0, NULL);
if (UNEXPECTED(genericType.isUndef())) return zv::Val();
bool isMixed;
if (UNEXPECTED(!pt_type_instanceof(genericType.raw(), PT_CLASS_MIXED_TYPE, isMixed))) return zv::Val();
if (isMixed) return pt_type_is_super_type_of_result(PT_TRI_MAYBE);
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();
if (isStatic) {
Expand Down Expand Up @@ -617,7 +615,7 @@ class ConstantStringType
}

if (isSameClass) {
zv::Val never = pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
zv::Val never = pt_type_new_never_type();
if (UNEXPECTED(never.isUndef())) return zv::Val();
return classNameToObjectTypeResult(never.raw(), false);
}
Expand Down
14 changes: 4 additions & 10 deletions turbo-ext/src/GenericClassStringType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,7 @@ class GenericClassStringType
zval *ownType = this->type();
if (UNEXPECTED(ownType == NULL)) return zv::Val();
zv::Val genericType = zv::Val::copyOf(zv::Ref(ownType));
bool isMixed;
if (UNEXPECTED(!pt_type_instanceof(genericType.raw(), PT_CLASS_MIXED_TYPE, isMixed))) return zv::Val();
if (isMixed) return pt_type_is_super_type_of_result(PT_TRI_YES);
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();
Expand Down Expand Up @@ -396,7 +394,7 @@ class GenericClassStringType
if (UNEXPECTED(classReflection.isUndef())) return zv::Val();
zv::Val isFinal = pt_type_call(Z_OBJ_P(classReflection.raw()), PT_LC("isfinal"), 0, NULL);
if (UNEXPECTED(isFinal.isUndef())) return zv::Val();
if (zend_is_true(isFinal.raw()) && zend_is_identical(classNameZv, removedValue.raw())) return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
if (zend_is_true(isFinal.raw()) && zend_is_identical(classNameZv, removedValue.raw())) return pt_type_new_never_type();

zv::Val allowedSubTypes = pt_type_call(Z_OBJ_P(classReflection.raw()), PT_LC("getallowedsubtypes"), 0, NULL);
if (UNEXPECTED(allowedSubTypes.isUndef())) return zv::Val();
Expand All @@ -406,9 +404,7 @@ class GenericClassStringType
zv::Args args{generic.raw(), objectTypeToRemove.raw()};
zv::Val remainingType = pt_type_call_static(PT_CLASS_TYPE_COMBINATOR, PT_LC("remove"), 2, args);
if (UNEXPECTED(remainingType.isUndef())) return zv::Val();
bool isNever;
if (UNEXPECTED(!pt_type_instanceof(remainingType.raw(), PT_CLASS_NEVER_TYPE, isNever))) return zv::Val();
if (isNever) return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
if (zv::Ref(remainingType.raw()).instanceOf(pt_ce_never_type)) return pt_type_new_never_type();

zv::Val equal = pt_type_call(Z_OBJ_P(remainingType.raw()), PT_LC("equals"), 1, generic.raw());
if (UNEXPECTED(equal.isUndef())) return zv::Val();
Expand All @@ -429,9 +425,7 @@ class GenericClassStringType
zv::Args args{generic.raw(), objectTypeToRemove.raw()};
zv::Val remainingType = pt_type_call_static(PT_CLASS_TYPE_COMBINATOR, PT_LC("remove"), 2, args);
if (UNEXPECTED(remainingType.isUndef())) return zv::Val();
bool isNever;
if (UNEXPECTED(!pt_type_instanceof(remainingType.raw(), PT_CLASS_NEVER_TYPE, isNever))) return zv::Val();
if (isNever) return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
if (zv::Ref(remainingType.raw()).instanceOf(pt_ce_never_type)) return pt_type_new_never_type();

return create(remainingType.raw());
}
Expand Down
2 changes: 1 addition & 1 deletion turbo-ext/src/IntegerRangeType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ class IntegerRangeType
return pt_type_new(PT_CLASS_CONST_TYPE_NODE, 1, constExpr.raw());
}

static zv::Val never() { return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL); }
static zv::Val never() { return pt_type_new_never_type(); }

static zv::Val integer()
{
Expand Down
2 changes: 1 addition & 1 deletion turbo-ext/src/IntegerType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class IntegerType
}
if (!lowerPart.isUndef()) return lowerPart;
if (!upperPart.isUndef()) return upperPart;
return pt_type_new(PT_CLASS_NEVER_TYPE, 0, NULL);
return pt_type_new_never_type();
}

static zv::Val getFiniteTypes() { return zv::Val(zv::Arr::empty()); }
Expand Down
Loading