Repository navigation
fix(checker): file-qualified diagnostics, autoload span identities and preludes, checker cost - #1921
Merged
nahime0 merged 8 commits intoOct 24, 2026
Conversation
Contributor
[Medium impact] The PR appears safe to merge; no new blocking issue was established. SummaryThe PR extends file-qualified diagnostics, probes autoloaded files for needed preludes, preserves promoted method variadics, and replaces repeated checker scans with indexes.
|
…d preludes, checker cost Diagnostics: - Each autoloaded file gets its own source id, and the resolver of its includes no longer rewinds the counter, so spans of different files no longer collide (two autoloaded files with a retype at the same line and column no longer fail with "Cannot re-bind $x here"). - Span::register_source_path / source_path map ids to files; print_error and print_warning use it when the diagnostic carries no file, so every positioned diagnostic is now `kind[file:line:col]`, entry file included. - Class schema errors (circular inheritance, final class, duplicate class, unknown parent) point at the class declaration instead of Span::dummy(). Preludes: autoload::prelude_probe resolves names and runs the autoload on a copy of the program, so pdo, mysqli, tz, image, hash, curl, xml and var_export are injected when only an autoloaded file uses them. The probe rewinds every counter it advanced. Method variadics: widen_method_variadic_element only widens, so a collector a first-class callable promoted to array<mixed> is not narrowed back to array<string> by a direct string call (the callable path then stored mixed values into a string collector: "heap memory exhausted"). Cost (3400-class program, 290 s -> 62 s user time, same result): indexes for method-implementation users, property sharers, loop-storage scopes and class descendants (new static), and exact-key lookups before the case-insensitive scans in check_builtin, has_function_decl_folded and folded_map_key.
The entry file's source path was registered only after include resolution succeeded, so a missing require in the entry file still printed the file-less error[line:col]. Source id 0 is never handed out again by the resolver, so the path is now registered before it runs.
JGamboa
force-pushed
the
fix/checker-driver-cost-and-diagnostics
branch
from
October 21, 2026 06:04
35cec0d to
36f728c
Compare
… and checker indexes The compiler guides now cover what the README already showed: the kind[file:line:col] diagnostic format in docs/compiling, the autoload prelude probe and per-file source identities in the pipeline guide, and the span-to-file registry and class-table lookup indexes in the type checker internals. Stale error[line:col] examples are updated.
The probe description now sits in its own bullet after func-args instead of extending the resolve/prelude entry, whose closing sentence main has since rewritten.
nahime0
added a commit
to JGamboa/elephc
that referenced
this pull request
Oct 24, 2026
Autoloaded class files were lexed with the entry file's source identity 0, and the resolver of their includes rewound the identity counter, so a statement at the same line and column of two files was one key in every span-keyed checker map. A readonly write guard of one class fired on a plain write of another (illegalstudio#1445), and same-position retypes in two class files were refused as ambiguous. Each autoloaded file now takes a fresh identity, and its includes resolve through a continuing resolver entry; only the root resolution rewinds the counter. The approach comes from illegalstudio#1921 by @JGamboa.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four checker-driver fixes found while checking a large Composer project (a Laravel 13
application, ~3400 classes). They share the checker driver, pipeline and source-identity infrastructure. The shared
source-identity fix has also landed on main through #2038; this PR preserves it while
extending entry-file diagnostics, prelude discovery and checker optimizations.
file:line:coldiagnostics and distinct span identities for autoloaded files.Autoloaded files were lexed with source id 0 (the entry file's), and the resolver of their
includes rewound the id counter, so spans of different files collided as map keys. Each
autoloaded file now gets its own source id (
resolve_collecting_includes_continuing), andan id -> path registry (
Span::register_source_path/Span::source_path) letsprint_error/print_warningname the file when the error itself carries none. Classschema errors (circular inheritance, final class, duplicate class, unknown parent) point at
the class declaration instead of
Span::dummy().Visible effect besides the format: two autoloaded class files with a retyping assignment at
the same line and column no longer fail with "Cannot re-bind $x here" (Give Span a file identity so cross-file same-position collisions stop being compile errors #778).
Post-autoload preludes. The pdo, mysqli, tz, image, hash, curl, xml and var_export
preludes were decided on the entry program before name resolution, but the Composer
autoload loads vendor classes later: an autoloaded class typed against
PDOorCurlHandlefailed withUnknown type: PDOunless--with-*was passed.autoload::prelude_proberesolves names and runs the autoload on a copy of the program,runs each prelude's detector over the result, and the pipeline injects the groups it finds
as if the entry used them. The probe restores thread-local source IDs and anonymous-class numbering. Process-global
synthetic spans and value-include temporaries stay monotonic, so a discarded probe cannot
recycle identities allocated by another compiler thread.
Monotonic method variadics. A first-class callable (
$r->is(...)) promotes an untypedmethod variadic's collector to
array<mixed>; a later direct call with a string narrowedit back to
array<string>, becausewider_type_syntacticletsStrabsorbMixed. Thecallable path then stored mixed values into a string collector, and the program aborted
with "heap memory exhausted".
widen_method_variadic_elementonly widens.Quadratic work removed (profiled on the 3400-class program; release user time
290 s -> 62 s, same result except union member order, which HashMap order already varied):
update_method_return_typeandupdate_method_callable_return_metadatawalked everyclass for each method body: an index (implementing class, method, static) -> classes that
dispatch to it;
propagate_constructor_arg_typewalked every class per argument of eachnew: an index(property, declaring class) -> classes sharing it;
loop_storage_typeswas cleared with aretainover the whole map per method, functionand closure: a scope -> loop spans index;
check_builtinscanned every declared function on each builtin call: the cheap conditiongoes first, and
has_function_decl_folded/folded_map_keytry the exact key beforethe scan;
validate_late_bound_constructor_targetswalked the parent chain from every class at eachnew static: a direct-children index (class_and_descendants).The indexes are keyed by
classes.len(): implementation and property maps are only writtenwhile classes are registered, which also changes the table size.
Closes #1445
Fixes the autoload part of #778 (autoloaded files no longer share span identities). The rest
of #778 (a retyping file
required twice, file identity inSpanequality) is not addressedhere, so #778 should stay open.
Left out
widen_override_untyped_method_params. This PR is independent ofthe chained-call PR (fix(checker): widen untyped params reached through call-result receivers #1919), which adds that function; once both land, a follow-up switches
it to
class_and_descendantstoo.ELEPHC_CHECKER_TRACE) of the original branch.coreandbcmathpreludes are not probed through the autoload closure (they postdate theoriginal change); an autoloaded file that is the only user of
WeakMaporBcMath\Numberis still not covered.
column still print only one of the two "changes type" warnings (the program compiles and
runs correctly).
Tests
After merging main
30f1305e23, 14 exact local unit, CLI and codegen tests pass:temporaries, preserved source/anonymous-class numbering, span-to-file registration, and
the source file taking precedence over an entry-file diagnostic tag.
autoload-closure warnings, PDO/curl prelude detection and same-position retypes.
anonymous classes in includes and the original readonly-write span collision from Give autoloaded files distinct span identities #1445.
The three new regressions for global counter rollback and early warnings were verified
failing on
4666dae39before the fixes. All focused commands ran through the sharedbuild queue, with one test matched per filter. The library, CLI and selected test binaries
compile without warnings;
git diff --checkpasses.Refs #1886
AI assistance
This change was written with substantial help from Claude Code (Anthropic). I reviewed it,
checked the expected outputs against php 8.5, and remain responsible for it.
🤖 Generated with Claude Code