Skip to content

Shadow the combinator, template and late-resolvable types natively - #6506

Merged
ondrejmirtes merged 9 commits into
2.3.xfrom
turbo-type-kernel-generics
Sep 22, 2026
Merged

ondrejmirtes merged 9 commits into
2.3.xfrom
turbo-type-kernel-generics

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Moves the combinator, the template-type machinery and the late-resolvable types into the native extension, completing the Type kernel.

What goes native

  • TypeTraverser, VerbosityLevel, RecursionGuard, FiniteTypeSet, the kernel's helpers.
    • TypeTraverser hands out real [$this, 'method'] arrays and keeps a TypeTraverserCallable as the object.
    • VerbosityLevel keeps its singletons in the twin's static properties.
    • RecursionGuard keys the twin's static $context the way PHP does.
  • ErrorType and the small Type classes over the native parents: CircularTypeAliasErrorType, AbsorbedTemplateArgumentType, NonAcceptingNeverType, the two StringType children, and ResourceType, plus the static helpers TypeUtils and TypehintHelper.
  • TypeCombinator: union(), intersect(), remove() and the null/falsey helpers, with every private helper of the twin as a C++ member. On a miss, TypeCombinatorCache computes through the native doUnion(), doIntersect() and doRemove() directly.
  • TemplateTypeVariance, TemplateTypeVarianceMap, TemplateTypeMap, TemplateTypeScope, TemplateTypeReference, TemplateTypeHelper. The singletons stay in the twins' static properties. TemplateTypeHelper runs its five traversal closures as native callback bodies through the native TypeTraverser.
  • The late-resolvable family: KeyOfType, ValueOfType, OffsetAccessType, ClassConstantAccessType, NewObjectType, ConditionalType, ConditionalTypeForParameter, LateResolvableArrayShapeType, UnresolvedTemplateArgumentType, and later TemplateKeyOfType. They share one LateResolvableTypeTrait registrar, which memoizes resolve() in the trait's $result slot.
  • The Template*Type family. Nineteen classes are declared over their native parents, with the TemplateTypeTrait as a registrar. Alongside them go the two template-type strategies, TemplateTypeFactory (which instantiates the native classes directly) and TypeProjectionHelper.
  • ConstantArrayTypeBuilder, UnionTypeHelper, ConstantTypeHelper, StaticTypeFactory, TypeResult, CallableTypeHelper, GetTemplateTypeType. UnionTypeHelper sorts through zend_hash_sort with the twin's comparator.

Every class the native code used to reach through the class-reference table is now called through its exported native entry, and those table keys are removed.

Tests

turbo-ext/tests/type-family.php gains the combinator, the helpers and the late-resolvable family, about 160k more observations. smoke.php compares them between the PHP twins and the activated extension.

Verification

On this commit, with the extension loaded and active:

  • side-by-side.php, signature-parity.php and smoke.php pass.
  • make tests, make phpstan, make cs and make lint are clean.
  • A strict clang build and a strict g++-15 build (-Wall -Wextra -Werror) both pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_017MvPby652L7wUqGAHEiEcN

ondrejmirtes and others added 9 commits September 22, 2026 08:55
…t natively

The Type-kernel helpers: TypeTraverser hands out real [$this, 'method']
arrays and keeps a TypeTraverserCallable as the object (its traverse()
called from mapInternal(), as the twin's wrapping closure does);
VerbosityLevel holds its singletons in the twin's static properties and
runs getRecommendedLevelByType()'s two closures as native callback bodies;
RecursionGuard keys the twin's static $context the way PHP does and feeds
the TypeCombinatorCache memo guard; FiniteTypeSet builds the identity keys
natively. The four class-map keys are gone (TypeTraverserCallable gains one)
and every native use goes through the exported pt_* helpers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
…tType, NonAcceptingNeverType, StringAlwaysAcceptingObjectWithToStringType, StringNeverAcceptingObjectWithToStringType, ResourceType, TypeUtils and TypehintHelper natively

The small Type classes over the native parents: the ErrorType family
(the $reason slot after MixedType's two, describe() through MixedType's
exported body, the two children inheriting the constructor), the
non-accepting never over NeverType, the two StringType children asking the
reflection provider for a native __toString(), the stateless ResourceType
over the shared trait registrars, and the two static helper classes.
The errorType, absorbedTemplateArgumentType and typeUtils class-map keys
are gone (35 native sites use the exported class entries and constructors);
seven keys the helpers need are added. The type-family differential gains
three sections (8k observations) and a reflection fixture.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
The static combinator: union(), intersect(), remove() and the null/falsey
helpers with every private helper of the twin as C++ members, the twin's
$cacheEnabled slot routing through the memo, and TypeCombinatorCache
computing a miss through the native doUnion()/doIntersect()/doRemove()
directly. The typeCombinator class-map key is gone: the 37 native call
sites go through pt_type_combinator_call()/_call_spread() (support.h);
turboExtensionEnabler, templateTypeFactory and templateBenevolentUnionType
keys added. pt_type_result_trinary() now starts its rv slot UNDEF (the
release of an unwritten slot crashed on a PHP-twin result object). The
type-family differential gains 110k combinator observations.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
…, TemplateTypeScope, TemplateTypeReference and TemplateTypeHelper natively

The template-type helper classes: TemplateTypeVariance keeps its five
singletons in the twin's static $registry and runs isValidVariance()'s
probe chain natively; the two maps hold their empty singletons in the
twins' static $empty and walk the type arrays natively, crossing into PHP
only for TypeCombinator, TypeUtils::toBenevolentUnion() and map()'s
callback; TemplateTypeScope and TemplateTypeReference are plain slot
holders; TemplateTypeHelper runs the twin's five traversal closures as
native callback bodies through the native TypeTraverser. The four
class-map keys are gone (ParametersAcceptor and NonAcceptingNeverType
gain one each), every native use goes through the exported pt_* helpers
and side-by-side.php now recognizes a method named static().

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
…ype, NewObjectType, ConditionalType, ConditionalTypeForParameter, LateResolvableArrayShapeType and UnresolvedTemplateArgumentType natively

The late-resolvable family over one shared LateResolvableTypeTrait registrar
(pt_type_trait_late_resolvable: the trait's $result slot declared behind the
class's own properties, resolve() memoized there, one frame-named forwarding
handler for the hundred `$this->resolve()->x()` bodies, the compound
reversals and isSuperTypeOfDefault() exported for the conditional types),
each class as its own thin .cpp: key-of/value-of (the enum path over the
backing values), offset and class-constant access, new<>, the two
conditional types with native callbacks for their normalized branches, the
array shape collapsing through ConstantArrayTypeBuilder, and the
observation-pass marker with its frame-named delegate forwards. The
unresolvedTemplateArgumentType class-map key is gone (two native sites use
the exported class entry); three vendored phpdoc-parser node keys are added.
The type-family differential gains one section (~40k observations).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
…teTypeParameterStrategy, TemplateTypeFactory and TypeProjectionHelper natively

TemplateArrayType, TemplateBenevolentUnionType, TemplateBooleanType, TemplateConstantArrayType, TemplateConstantIntegerType, TemplateConstantStringType, TemplateFloatType, TemplateGenericObjectType, TemplateIntegerType, TemplateIntersectionType, TemplateIterableType, TemplateMixedType, TemplateNullType, TemplateObjectShapeType, TemplateObjectType, TemplateObjectWithoutClassType, TemplateStrictMixedType, TemplateStringType and TemplateUnionType are declared over their native parents with the TemplateTypeTrait as a pt_type_trait_template_type() registrar (the six trait properties become the class's last slots; $this-dispatch through Z_OBJCE for the non-final TemplateObjectWithoutClassType). The two strategies are final natives implementing TemplateTypeStrategy; TemplateTypeFactory::create() dispatches on the bound's class entry and instantiates the native classes directly, TypeProjectionHelper::describe() is a plain static port.

Class-map keys removed: templateArrayType, templateBenevolentUnionType, templateIterableType, templateMixedType, templateStrictMixedType, templateUnionType, templateTypeFactory, typeProjectionHelper; added at the end: templateTypeReference, keyOfType, templateKeyOfType (TemplateKeyOfType stays PHP over the unshadowed KeyOfType and is built through the class map).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy
… StaticTypeFactory, TypeResult, CallableTypeHelper and GetTemplateTypeType natively

The remaining Type-namespace helper classes: the builder keeps the twin's
eleven slots and folds keys and values through the native TypeCombinator;
UnionTypeHelper sorts through zend_hash_sort with the twin's comparator
(stable, TypeError on a non-Type member); ConstantTypeHelper maps values
through the native builder; StaticTypeFactory memoizes its four types in
request-scoped globals; TypeResult is a readonly slot pair;
CallableTypeHelper combines the parameter and return-type results
natively; GetTemplateTypeType is the first late-resolvable port, with the
LateResolvableTypeTrait registrar (pt_type_trait_late_resolvable) and the
$result memo helper appended to TypeTraits. The four class-map keys
(constantArrayTypeBuilder, unionTypeHelper, staticTypeFactory,
callableTypeHelper) are gone, ARRAY_COUNT_LIMIT is a shared native
constant, every native use goes through the exported pt_* helpers, and
reg.h gains builders for a bool typed property and a public readonly
class-typed property.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSCEwuXjDzsNw2wSNsDW6D
Final class over the native KeyOfType with the TemplateTypeTrait and
UndecidedComparisonCompoundTypeTrait registrars; the constructor runs
KeyOfType's on $bound->getType() and its own getResult() re-templates the
bound's getResult() through the native TemplateTypeFactory entry point (the
inherited resolve() reaches it through the object's class entry). The
templateKeyOfType class-map key is gone: TemplateTypeFactory instantiates
pt_template_key_of_type_new() directly like its siblings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy
@ondrejmirtes
ondrejmirtes force-pushed the turbo-type-kernel-generics branch from 36000ba to 6d24e75 Compare September 22, 2026 06:55
@ondrejmirtes
ondrejmirtes merged commit 6d24e75 into 2.3.x Sep 22, 2026
186 of 187 checks passed
@ondrejmirtes
ondrejmirtes deleted the turbo-type-kernel-generics branch September 22, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant