Skip to content
Prev Previous commit
Next Next commit
Shadow ExpressionResult natively
Ports PHPStan\Analyser\ExpressionResult (29 methods) to turbo-ext/src/
ExpressionResult.cpp. The state lives in the twin's property slots in
declaration order and the constructor keeps the twin's exact arginfo, so
Nette still generates ExpressionResultFactory from it; the type/specify/
create callbacks, MutatingScope and the extensions collection are called
through the engine, while the Type queries, late-resolvable resolution and
the void->null projection go through the native Type ops and TypeTraverser.
VariableFlowBuilder now reads a result's flow through the new
pt_expression_result_variable_flow() slot reader instead of a method call.
Adds the class-map keys typeSpecifierContext, voidToNullTraverser,
issetabilityResolution, issetabilityLinkInfo and nullsafeMethodCall, and
lets reg::callableArg() be nullable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy
  • Loading branch information
ondrejmirtes and claude committed Sep 22, 2026
commit 1183b25856245ae72cf9e179522b61c412b0da48
2 changes: 2 additions & 0 deletions build/PHPStan/Build/TurboAttributeCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\NullsafeMethodCall;
use PhpParser\Node\Expr\NullsafePropertyFetch;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
Expand Down Expand Up @@ -166,6 +167,7 @@ final class TurboAttributeCollector
'conditionalTypeNode' => ConditionalTypeNode::class,
'conditionalTypeForParameterNode' => ConditionalTypeForParameterNode::class,
'reflectionEnum' => ReflectionEnum::class,
'nullsafeMethodCall' => NullsafeMethodCall::class,
'staticPropertyFetch' => StaticPropertyFetch::class,
'listExpr' => List_::class,
];
Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/ExpressionResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\DependencyInjection\ExtensionsCollection;
use PHPStan\DependencyInjection\GenerateFactory;
use PHPStan\ShouldNotHappenException;
use PHPStan\Turbo\ShadowedByTurboExtension;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\ExpressionTypeResolverExtension;
use PHPStan\Type\Type;
Expand All @@ -22,6 +23,7 @@
use function spl_object_id;

#[GenerateFactory(interface: ExpressionResultFactory::class)]
#[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/ExpressionResult.cpp')]
final class ExpressionResult
{

Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/IssetabilityLinkInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Rules\Properties\FoundPropertyReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Type;

/**
Expand All @@ -15,6 +16,7 @@
* (IssetabilityResolution::isSet) and the rule (PHPStan\Rules\IssetCheck) read the
* facts instead of re-walking and re-resolving.
*/
#[ReferencedByTurboExtension(key: 'issetabilityLinkInfo')]
final class IssetabilityLinkInfo
{

Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/IssetabilityResolution.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Analyser;

use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\Type;

/**
Expand All @@ -11,6 +12,7 @@
* the fold via isSet() and the rule (PHPStan\Rules\IssetCheck) renders messages
* from the same links - neither re-walks the AST nor re-resolves types.
*/
#[ReferencedByTurboExtension(key: 'issetabilityResolution')]
final class IssetabilityResolution
{

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

namespace PHPStan\Analyser\Traverser;

use PHPStan\Turbo\ReferencedByTurboExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverserCallable;
use PHPStan\Type\UnionType;

#[ReferencedByTurboExtension(key: 'voidToNullTraverser')]
final class VoidToNullTraverser implements TypeTraverserCallable
{

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

use PHPStan\ShouldNotHappenException;
use PHPStan\Turbo\ReferencedByTurboExtension;

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

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\\AbsorbedTemplateArgumentType.cpp src\\AcceptsResult.cpp src\\AccessoryArrayListType.cpp src\\AccessoryDecimalIntegerStringType.cpp src\\AccessoryLiteralStringType.cpp src\\AccessoryLowercaseStringType.cpp src\\AccessoryNonEmptyStringType.cpp src\\AccessoryNonFalsyStringType.cpp src\\AccessoryNumericStringType.cpp src\\AccessoryUppercaseStringType.cpp src\\ArenaCache.cpp src\\ArrayType.cpp src\\BenevolentUnionType.cpp src\\BooleanType.cpp src\\CallableType.cpp src\\CallableTypeHelper.cpp src\\CallbackUnresolvedMethodPrototypeReflection.cpp src\\CallbackUnresolvedPropertyPrototypeReflection.cpp src\\CalledOnTypeUnresolvedMethodPrototypeReflection.cpp src\\CalledOnTypeUnresolvedPropertyPrototypeReflection.cpp src\\CircularTypeAliasErrorType.cpp src\\ClassConstantAccessType.cpp src\\ClassReflectionAccess.cpp src\\ClassStringType.cpp src\\ClosureType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConditionalType.cpp src\\ConditionalTypeForParameter.cpp src\\ConstantArrayType.cpp src\\ConstantArrayTypeBuilder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ConstantTypeHelper.cpp src\\EnumCaseObjectType.cpp src\\ErrorType.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FiniteTypeSet.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\GenericObjectType.cpp src\\GenericStaticType.cpp src\\GetTemplateTypeType.cpp src\\HasMethodType.cpp src\\HasOffsetType.cpp src\\HasOffsetValueType.cpp src\\HasPropertyType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IntersectionType.cpp src\\IsSuperTypeOfResult.cpp src\\IterableType.cpp src\\KeyOfType.cpp src\\LateResolvableArrayShapeType.cpp src\\LruCache.cpp src\\MixedType.cpp src\\NativeParameterReflection.cpp src\\NeverType.cpp src\\NewObjectType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NonAcceptingNeverType.cpp src\\NonEmptyArrayType.cpp src\\NonexistentParentClassType.cpp src\\NullType.cpp src\\ObjectShapeType.cpp src\\ObjectType.cpp src\\ObjectWithoutClassType.cpp src\\OffsetAccessType.cpp src\\OversizedArrayType.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\RecursionGuard.cpp src\\ReflectionAccess.cpp src\\ResourceType.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StaticType.cpp src\\StaticTypeFactory.cpp src\\StrictMixedType.cpp src\\StringAlwaysAcceptingObjectWithToStringType.cpp src\\StringNeverAcceptingObjectWithToStringType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TemplateArrayType.cpp src\\TemplateBenevolentUnionType.cpp src\\TemplateBooleanType.cpp src\\TemplateConstantArrayType.cpp src\\TemplateConstantIntegerType.cpp src\\TemplateConstantStringType.cpp src\\TemplateFloatType.cpp src\\TemplateGenericObjectType.cpp src\\TemplateIntegerType.cpp src\\TemplateIntersectionType.cpp src\\TemplateIterableType.cpp src\\TemplateKeyOfType.cpp src\\TemplateMixedType.cpp src\\TemplateNullType.cpp src\\TemplateObjectShapeType.cpp src\\TemplateObjectType.cpp src\\TemplateObjectWithoutClassType.cpp src\\TemplateStrictMixedType.cpp src\\TemplateStringType.cpp src\\TemplateTypeArgumentStrategy.cpp src\\TemplateTypeFactory.cpp src\\TemplateTypeHelper.cpp src\\TemplateTypeMap.cpp src\\TemplateTypeParameterStrategy.cpp src\\TemplateTypeReference.cpp src\\TemplateTypeScope.cpp src\\TemplateTypeVariance.cpp src\\TemplateTypeVarianceMap.cpp src\\TemplateUnionType.cpp src\\ThisType.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinator.cpp src\\TypeCombinatorCache.cpp src\\TypeOps.cpp src\\TypeProjectionHelper.cpp src\\TypeResult.cpp src\\TypeTraits.cpp src\\TypeTraverser.cpp src\\TypeUtils.cpp src\\TypehintHelper.cpp src\\UnionType.cpp src\\UnionTypeHelper.cpp src\\UnresolvableTypeHelper.cpp src\\UnresolvedTemplateArgumentType.cpp src\\ValueOfType.cpp src\\VariableFlow.cpp src\\VariableFlowBuilder.cpp src\\VariableLivenessResolver.cpp src\\VerbosityLevel.cpp src\\VoidType.cpp src\\VolatileExpressionHelper.cpp",
"src\\main.cpp src\\support.cpp src\\AbsorbedTemplateArgumentType.cpp src\\AcceptsResult.cpp src\\AccessoryArrayListType.cpp src\\AccessoryDecimalIntegerStringType.cpp src\\AccessoryLiteralStringType.cpp src\\AccessoryLowercaseStringType.cpp src\\AccessoryNonEmptyStringType.cpp src\\AccessoryNonFalsyStringType.cpp src\\AccessoryNumericStringType.cpp src\\AccessoryUppercaseStringType.cpp src\\ArenaCache.cpp src\\ArrayType.cpp src\\BenevolentUnionType.cpp src\\BooleanType.cpp src\\CallableType.cpp src\\CallableTypeHelper.cpp src\\CallbackUnresolvedMethodPrototypeReflection.cpp src\\CallbackUnresolvedPropertyPrototypeReflection.cpp src\\CalledOnTypeUnresolvedMethodPrototypeReflection.cpp src\\CalledOnTypeUnresolvedPropertyPrototypeReflection.cpp src\\CircularTypeAliasErrorType.cpp src\\ClassConstantAccessType.cpp src\\ClassReflectionAccess.cpp src\\ClassStringType.cpp src\\ClosureType.cpp src\\CombinationsHelper.cpp src\\ConditionalExpressionHolder.cpp src\\ConditionalType.cpp src\\ConditionalTypeForParameter.cpp src\\ConstantArrayType.cpp src\\ConstantArrayTypeBuilder.cpp src\\ConstantBooleanType.cpp src\\ConstantFloatType.cpp src\\ConstantIntegerType.cpp src\\ConstantStringType.cpp src\\ConstantTypeHelper.cpp src\\EnumCaseObjectType.cpp src\\ErrorType.cpp src\\ExpressionResult.cpp src\\ExpressionResultStorage.cpp src\\ExpressionTypeHolder.cpp src\\FiniteTypeSet.cpp src\\FloatType.cpp src\\GenericClassStringType.cpp src\\GenericObjectType.cpp src\\GenericStaticType.cpp src\\GetTemplateTypeType.cpp src\\HasMethodType.cpp src\\HasOffsetType.cpp src\\HasOffsetValueType.cpp src\\HasPropertyType.cpp src\\IntegerRangeType.cpp src\\IntegerType.cpp src\\IntersectionType.cpp src\\IsSuperTypeOfResult.cpp src\\IterableType.cpp src\\KeyOfType.cpp src\\LateResolvableArrayShapeType.cpp src\\LruCache.cpp src\\MixedType.cpp src\\NativeParameterReflection.cpp src\\NeverType.cpp src\\NewObjectType.cpp src\\NodeScanner.cpp src\\NodeTraverser.cpp src\\NonAcceptingNeverType.cpp src\\NonEmptyArrayType.cpp src\\NonexistentParentClassType.cpp src\\NullType.cpp src\\ObjectShapeType.cpp src\\ObjectType.cpp src\\ObjectWithoutClassType.cpp src\\OffsetAccessType.cpp src\\OversizedArrayType.cpp src\\PharForkGuard.cpp src\\PhpFileCleaner.cpp src\\RecursionGuard.cpp src\\ReflectionAccess.cpp src\\ResourceType.cpp src\\ScopeContext.cpp src\\ScopeOps.cpp src\\Shadow.cpp src\\StaticType.cpp src\\StaticTypeFactory.cpp src\\StrictMixedType.cpp src\\StringAlwaysAcceptingObjectWithToStringType.cpp src\\StringNeverAcceptingObjectWithToStringType.cpp src\\StringType.cpp src\\SymbolFinderInFiles.cpp src\\TemplateArrayType.cpp src\\TemplateBenevolentUnionType.cpp src\\TemplateBooleanType.cpp src\\TemplateConstantArrayType.cpp src\\TemplateConstantIntegerType.cpp src\\TemplateConstantStringType.cpp src\\TemplateFloatType.cpp src\\TemplateGenericObjectType.cpp src\\TemplateIntegerType.cpp src\\TemplateIntersectionType.cpp src\\TemplateIterableType.cpp src\\TemplateKeyOfType.cpp src\\TemplateMixedType.cpp src\\TemplateNullType.cpp src\\TemplateObjectShapeType.cpp src\\TemplateObjectType.cpp src\\TemplateObjectWithoutClassType.cpp src\\TemplateStrictMixedType.cpp src\\TemplateStringType.cpp src\\TemplateTypeArgumentStrategy.cpp src\\TemplateTypeFactory.cpp src\\TemplateTypeHelper.cpp src\\TemplateTypeMap.cpp src\\TemplateTypeParameterStrategy.cpp src\\TemplateTypeReference.cpp src\\TemplateTypeScope.cpp src\\TemplateTypeVariance.cpp src\\TemplateTypeVarianceMap.cpp src\\TemplateUnionType.cpp src\\ThisType.cpp src\\TrinaryLogic.cpp src\\TrustedTypes.cpp src\\TypeCombinator.cpp src\\TypeCombinatorCache.cpp src\\TypehintHelper.cpp src\\TypeOps.cpp src\\TypeProjectionHelper.cpp src\\TypeResult.cpp src\\TypeTraits.cpp src\\TypeTraverser.cpp src\\TypeUtils.cpp src\\UnionType.cpp src\\UnionTypeHelper.cpp src\\UnresolvableTypeHelper.cpp src\\UnresolvedTemplateArgumentType.cpp src\\ValueOfType.cpp src\\VariableFlow.cpp src\\VariableFlowBuilder.cpp src\\VariableLivenessResolver.cpp src\\VerbosityLevel.cpp src\\VoidType.cpp src\\VolatileExpressionHelper.cpp",
true,
flags);
ADD_SOURCES(configure_module_dirname + "/src/parser",
Expand Down
Loading