feat(compiler): add compiler-only type pipeline for range iterator storage#51
Merged
Conversation
- Document the design risk analysis for the compiler-only type branch, including sealed-interface risks, fact sources reviewed, and recommended boundaries. - Outline the implementation plan covering target boundary, scope, cross-module contract, and acceptance criteria.
…erType protocol - Add GdccForRangeIterType to GdType sealed permits - Define stable internal name, LIR-only text, C storage/init/destroy helpers - Fail-fast on godot_* default helpers: pack/unpack, default-value, engine ABI - Render C storage as gdcc_for_range_iter and destroy as gdcc_for_range_iter_destroy - Reject compiler-only types in frontend ordinary boundary and writeback analysis - Add positive protocol tests and negative family/helper barrier tests Affected packages: type, backend/c/gen, frontend/sema/lowering
- Add use-site aware type parsing/serialization for LIR XML: only function `<variables>` may carry `compiler::GdccForRangeIter`; signal/property/parameter/ return/capture surfaces fail-fast on compiler-only type leak. - Reject unknown `compiler::...` grammar with explicit error instead of guessing Object type. - Cover source-facing resolvers/registries and LIR round-trip with new tests. - Mark stage 2 of frontend_gdcompiler_type_plan as completed.
…into semantic facts and lowering boundaries - Reject compiler-only types in expression type publish/resolve and inferred-local backfill paths - Reject compiler-only resolved initializer in local slot stabilization - Reject compiler-only types in condition published facts and condition normalization - Reject compiler-only source/target in frontend boundary materialization - Add tests covering all new guard points
…ct sources - Insert Phase 4 (GdCompilerType sealed interface + GdccForRangeIterType migration) into the plan, renumber subsequent phases 5→9 - Update risk analysis: GdCompilerType is now a planned requirement, not an open question - Add compiler-only type category and compatibility boundary to gdcc_type_system.md - Document compiler::<Name> grammar for backend-owned locals in gdcc_low_ir.md - Add compiler-only storage naming rules (gdcc_* over godot_*) to gdcc_c_backend.md
- Introduce sealed interface for compiler-only storage types with shared protocol and design invariants - Migrate existing concrete type to the new abstraction, removing duplicated defaults - Update all consumer-side type checks across frontend, backend, and LIR to use the abstraction - Add contract tests and update existing tests to anchor abstraction membership - Sync implementation plan status
…uards - Cover property store, subscript, call arg, and return paths - Verify fail-fast behavior across all frontend boundary sources - Ensure each leak site produces the expected diagnostic context
…ABI surfaces - reject compiler-only type on property, signal, function param/return and lambda capture - hidden functions share the same restriction as public ones in MVP - integrate validator into CCodegen.generate() before backend synthesis - update phase-5 plan status and add tests for validator and codegen integration
…pare-block scaffolding - Replace IllegalStateException with CallIntrinsicInsn for compiler-only local initialization in prepare block - Add intrinsic that emits gdcc_*_init(&var) with type validation for range iterator storage - Register intrinsic in manager so compiler-only init helpers are routed through intrinsic dispatch - Add tests covering init, assign, destruct and CALL_GLOBAL rejection for compiler-only types - Update phase-6 plan status and document completed scope
…and refactor prepare-block init - add gdcc.for_range_iter.init/should_continue/next/get with C runtime helpers and contract validation - rework default storage init to callAssign-based no-arg intrinsic with result-type enforcement - register all range-iterator intrinsics in manager and reject compiler-only type on non-range calls - document intrinsic contracts in LIR spec and mark phase-7 closure in implementation plan - add tests covering parser/serializer roundtrip, registry dispatch, codegen output and boundary rejection
…s and consolidate validation utilities - Add centralized rejectCompilerOnly guards in InsnGenSupport and wire them into all instruction generators for receiver, operand, argument, result target and Variant boundary checks - Unify outward metadata, typed container leaf and call wrapper paths in CGenHelper under explicit compiler-only rejection - Extract shared LirTypeUseSite enum from duplicated parser/serializer definitions and consolidate requireNonCompilerOnly logic into TypeCheckUtil utility - Delete private redundant reject/require methods across BackendMethodCallResolver, LirPublicAbiValidator and CGenHelper - Document phase-8 completion in compiler-type plan and add targeted negative tests covering every guarded code path
…hs and sync docs - Add negative tests for compiler-only variable rejection in construct_builtin, own/release and argument address-of paths - Gate compiler-only type from builtin constructor synthesis and object lifecycle paths - Document compiler-only type contracts, LIR-only grammar boundaries and assignability exclusion in type/low-ir/runtime-lib specs - Record phase-9 completion in compiler-type plan with doc diff summary - Add parser guard tests for malformed compiler:: grammar variants
…t conventions to explicit protocol - Add isPassedByPointerInC/getCCopyHelperName/isDirectStructAssignmentSafe/validateCStorageContract triad to GdCompilerType interface - Migrate CGenHelper, CBodyBuilder, CBodyBuilderAliasSafetySupport and CCodegen to read explicit protocol over empty-string heuristics - Implement explicit protocol in GdccForRangeIterType to lock in direct-assignment and pointer-parameter behavior - Add fail-fast validation preventing silent fallback to godot_* copy paths for future compiler-only types - Extend test coverage for compiler-only assignment, return, destruct, alias-safety edges and protocol contract assertions
- Remove source-level assertions that tightly couple tests to C helper implementation text - Clarify zero-step fallback as always-terminating with step=1, document as programming error - Drop unused imports
- Replace the planning document with the implementation document as the canonical reference for compiler-only type - Update the four range-iterator intrinsic sections in the LIR spec to point at the new doc - Archive the planning doc now that all phases are completed and recorded - Tighten the doc comment of the compiler-only range-iterator type to reflect its post-MVP status - Keep the new implementation document untracked as the durable source of truth
There was a problem hiding this comment.
Pull request overview
This PR introduces a compiler-only type lane (GdCompilerType) and wires an initial concrete implementation (GdccForRangeIterType) end-to-end across frontend analysis/lowering, LIR parsing/serialization/validation, and the C backend (including new gdcc.for_range_iter.* intrinsics and gdcc_* runtime helpers). The goal is to keep lowered for range(...) iterator state in backend-owned storage while preventing compiler-only types from leaking into source-facing typing facts or ABI-facing surfaces.
Changes:
- Add
GdCompilerType+GdccForRangeIterTypeand central “leak guard” helpers to reject compiler-only types at source-facing / ABI-facing boundaries. - Extend LIR XML typing to support
compiler::<Name>only for function-local variables, plus addLirPublicAbiValidatorto enforce ABI surface rules. - Add C backend support for compiler-only storage (prepare-block init, destroy, direct-assignment contract) and range iterator intrinsics/runtime helpers, with broad regression tests and docs.
Reviewed changes
Copilot reviewed 81 out of 81 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/test/java/gd/script/gdcc/type/GdCompilerTypeTest.java | Contract tests for compiler-only type protocol defaults and helper naming. |
| src/test/java/gd/script/gdcc/type/GdccForRangeIterTypeTest.java | Concrete type tests ensuring stable protocol and non-user-facing classification. |
| src/test/java/gd/script/gdcc/scope/resolver/ScopeTypeResolverTest.java | Ensures compiler-only texts/names don’t resolve as declared/source-facing types. |
| src/test/java/gd/script/gdcc/scope/ClassRegistryTypeMetaTest.java | Ensures compiler-only types don’t publish type-meta. |
| src/test/java/gd/script/gdcc/lir/validation/LirPublicAbiValidatorTest.java | Validates rejection of compiler-only types on ABI-like LIR surfaces. |
| src/test/java/gd/script/gdcc/lir/parser/SimpleLirBlockInsnSerializerTest.java | Serializer coverage for range iterator intrinsic call shapes. |
| src/test/java/gd/script/gdcc/lir/parser/SimpleLirBlockInsnParserTest.java | Parser round-trip and operand validation for range iterator intrinsics. |
| src/test/java/gd/script/gdcc/lir/parser/DomLirSerializerTest.java | XML serializer uses compiler::... grammar for locals and rejects ABI leakage. |
| src/test/java/gd/script/gdcc/lir/parser/DomLirParserTest.java | XML parser accepts compiler-only locals only; rejects other surfaces and malformed grammar. |
| src/test/java/gd/script/gdcc/frontend/sema/FrontendDeclaredTypeSupportTest.java | Declared type resolution warns/falls back for compiler-only texts. |
| src/test/java/gd/script/gdcc/frontend/sema/analyzer/support/FrontendVariantBoundaryCompatibilityTest.java | Frontend typed boundary rejects compiler-only types. |
| src/test/java/gd/script/gdcc/frontend/sema/analyzer/FrontendTypeCheckAnalyzerTest.java | Condition typing fails fast if compiler-only fact leaks. |
| src/test/java/gd/script/gdcc/frontend/sema/analyzer/FrontendLocalTypeStabilizationAnalyzerTest.java | Local stabilization fails fast on compiler-only initializer publication. |
| src/test/java/gd/script/gdcc/frontend/sema/analyzer/FrontendExprTypeAnalyzerTest.java | Prevents publishing compiler-only types into expressionTypes(). |
| src/test/java/gd/script/gdcc/frontend/lowering/pass/body/FrontendBodyLoweringSessionTest.java | Boundary materialization rejects compiler-only source/target. |
| src/test/java/gd/script/gdcc/frontend/lowering/FrontendWritableTypeWritebackSupportTest.java | Writeback analysis rejects compiler-only carrier types. |
| src/test/java/gd/script/gdcc/frontend/lowering/FrontendLoweringBodyInsnPassTest.java | Lowering fails fast for multiple compiler-only leak vectors (condition, stores, calls, return). |
| src/test/java/gd/script/gdcc/backend/c/gen/IndexStoreInsnGenTest.java | Backend index store rejects compiler-only operands pre-pack/writeback. |
| src/test/java/gd/script/gdcc/backend/c/gen/IndexLoadInsnGenTest.java | Backend index load rejects compiler-only result targets. |
| src/test/java/gd/script/gdcc/backend/c/gen/GdccForRangeIterIntrinsicTest.java | End-to-end C intrinsic generation contract tests for iterator operations. |
| src/test/java/gd/script/gdcc/backend/c/gen/CStorePropertyInsnGenTest.java | Property store rejects compiler-only values early. |
| src/test/java/gd/script/gdcc/backend/c/gen/CPackUnpackVariantInsnGenTest.java | Variant pack/unpack reject compiler-only source/target. |
| src/test/java/gd/script/gdcc/backend/c/gen/COwnReleaseObjectInsnGenTest.java | Own/release object paths reject compiler-only variables. |
| src/test/java/gd/script/gdcc/backend/c/gen/COperatorInsnGenTest.java | Operator paths reject compiler-only operands/result targets. |
| src/test/java/gd/script/gdcc/backend/c/gen/CLoadStaticInsnGenTest.java | Static loads reject compiler-only result targets. |
| src/test/java/gd/script/gdcc/backend/c/gen/CLoadPropertyInsnGenTest.java | Property loads reject compiler-only result targets. |
| src/test/java/gd/script/gdcc/backend/c/gen/CIntrinsicManagerTest.java | Intrinsic registry includes range iterator intrinsics and raw init intrinsic. |
| src/test/java/gd/script/gdcc/backend/c/gen/CGenHelperTest.java | Validates C helper rendering for compiler-only types and rejects Variant/metadata paths. |
| src/test/java/gd/script/gdcc/backend/c/gen/CDestructInsnGenTest.java | Destruct emits gdcc_*_destroy for compiler-only values (explicit + finally). |
| src/test/java/gd/script/gdcc/backend/c/gen/CConstructInsnGenTest.java | Builtin constructors reject compiler-only targets (avoid inventing godot_*). |
| src/test/java/gd/script/gdcc/backend/c/gen/CCodegenTest.java | Codegen enforces ABI validator; prepare-block emits compiler-only init. |
| src/test/java/gd/script/gdcc/backend/c/gen/CBodyBuilderPhaseCTest.java | Ensures & passing, prepare-block assignment semantics, and return semantics for compiler-only types. |
| src/test/java/gd/script/gdcc/backend/c/gen/CBodyBuilderAliasSafetySupportTest.java | Alias-safety rules for compiler-only direct-assignment overwrite. |
| src/test/java/gd/script/gdcc/backend/c/gen/CAssignInsnGenTest.java | Assignment generation uses direct struct assignment for compiler-only types. |
| src/test/java/gd/script/gdcc/backend/c/gen/CallMethodInsnGenTest.java | Dynamic call paths reject compiler-only args/result targets before Variant pack/unpack. |
| src/test/java/gd/script/gdcc/backend/c/gen/CallIntrinsicInsnGenTest.java | CALL_INTRINSIC dispatch supports range iterator intrinsic family. |
| src/test/java/gd/script/gdcc/backend/c/gen/CallGlobalInsnGenTest.java | Global calls reject compiler-only args and ensure raw init helper isn’t exposed as a utility. |
| src/test/java/gd/script/gdcc/backend/c/gen/binding/EngineMethodAbiCodecTest.java | ABI codec rejects compiler-only types. |
| src/main/java/gd/script/gdcc/util/TypeCheckUtil.java | Shared helper to reject compiler-only types with consistent error messaging. |
| src/main/java/gd/script/gdcc/type/GdType.java | Extends sealed hierarchy to permit compiler-only types. |
| src/main/java/gd/script/gdcc/type/GdCompilerType.java | Defines the compiler-only type protocol (LIR text + C storage/init/copy/destroy contract). |
| src/main/java/gd/script/gdcc/type/GdccForRangeIterType.java | Concrete compiler-only range iterator storage type. |
| src/main/java/gd/script/gdcc/lir/validation/LirPublicAbiValidator.java | Central validator preventing compiler-only types on ABI-like LIR surfaces. |
| src/main/java/gd/script/gdcc/lir/parser/DomLirSerializer.java | Emits compiler::... for locals; rejects compiler-only types on other surfaces. |
| src/main/java/gd/script/gdcc/lir/parser/DomLirParser.java | Parses compiler-only grammar only where allowed; rejects unknown/malformed compiler-only texts. |
| src/main/java/gd/script/gdcc/lir/LirTypeUseSite.java | Encodes which LIR type use-sites permit compiler-only types. |
| src/main/java/gd/script/gdcc/frontend/sema/analyzer/support/FrontendVariantBoundaryCompatibility.java | Rejects compiler-only types at ordinary frontend boundary decision points. |
| src/main/java/gd/script/gdcc/frontend/sema/analyzer/FrontendTypeCheckAnalyzer.java | Condition fact validation rejects compiler-only types. |
| src/main/java/gd/script/gdcc/frontend/sema/analyzer/FrontendLocalTypeStabilizationAnalyzer.java | Prevents compiler-only types from stabilizing ordinary locals. |
| src/main/java/gd/script/gdcc/frontend/sema/analyzer/FrontendExprTypeAnalyzer.java | Prevents compiler-only types from being published/backfilled into expression/local facts. |
| src/main/java/gd/script/gdcc/frontend/lowering/pass/body/FrontendCfgNodeInsnLoweringProcessors.java | Condition normalization rejects compiler-only types. |
| src/main/java/gd/script/gdcc/frontend/lowering/pass/body/FrontendBodyLoweringSession.java | Boundary materialization rejects compiler-only source/target types. |
| src/main/java/gd/script/gdcc/frontend/lowering/FrontendWritableTypeWritebackSupport.java | Writeback support rejects compiler-only types. |
| src/main/java/gd/script/gdcc/backend/c/gen/intrinsic/CForRangeIterRawInitIntrinsic.java | Prepare-block intrinsic for default compiler-only iterator storage initialization. |
| src/main/java/gd/script/gdcc/backend/c/gen/intrinsic/CForRangeIterIntrinsic.java | Implements gdcc.for_range_iter.* intrinsic family and type/arity validation. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/StorePropertyInsnGen.java | Rejects compiler-only receiver/value early on property stores. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/PackUnpackVariantInsnGen.java | Uses shared unpack helper and rejects compiler-only targets/sources. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/OperatorInsnGen.java | Rejects compiler-only operands/result targets for operator paths. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/LoadStaticInsnGen.java | Rejects compiler-only result targets for static loads. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/LoadPropertyInsnGen.java | Rejects compiler-only receiver/result targets; uses shared unpack helper. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/InsnGenSupport.java | Centralizes compiler-only rejection and shared Variant unpack helper. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/IndexStoreInsnGen.java | Rejects compiler-only index-store operands; uses shared unpack helper for writeback. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/IndexLoadInsnGen.java | Rejects compiler-only index-load operands/targets; uses shared unpack helper. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/DestructInsnGen.java | Ensures compiler-only values are destroyed via helper name rendering. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/CallMethodInsnGen.java | Rejects compiler-only receiver/args/results; uses shared unpack helper for dynamic results. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/CallGlobalInsnGen.java | Rejects compiler-only args/results (fixed + vararg) for globals/utilities. |
| src/main/java/gd/script/gdcc/backend/c/gen/insn/BackendMethodCallResolver.java | Rejects compiler-only receiver/args before method resolution. |
| src/main/java/gd/script/gdcc/backend/c/gen/CIntrinsicManager.java | Registers new range iterator intrinsic functions. |
| src/main/java/gd/script/gdcc/backend/c/gen/CGenHelper.java | Adds compiler-only rendering/validation hooks for C names, init/copy/destroy, and metadata/Variant boundaries. |
| src/main/java/gd/script/gdcc/backend/c/gen/CCodegen.java | Runs ABI validator pre-synthesis; emits compiler-only prepare-block init intrinsics. |
| src/main/java/gd/script/gdcc/backend/c/gen/CBodyBuilderAliasSafetySupport.java | Extends alias-safety logic to respect compiler-only direct-assignment vs copy-helper semantics. |
| src/main/java/gd/script/gdcc/backend/c/gen/CBodyBuilder.java | Enforces compiler-only constraints in default values, argument passing, copy/assign/return logic. |
| src/main/java/gd/script/gdcc/backend/c/gen/binding/EngineMethodAbiCodec.java | Rejects compiler-only types when producing engine ABI descriptors. |
| src/main/c/codegen/include_451/gdcc/gdcc_intrinsic.h | Adds compiler-only range iterator storage struct and runtime helper implementations. |
| doc/module_impl/frontend/frontend_gdcompiler_type_implementation.md | Long-lived spec describing compiler-only type boundaries and contracts across the pipeline. |
| doc/gdcc_type_system.md | Documents compiler-only types as a separate non-source-facing type family. |
| doc/gdcc_runtime_lib.md | Documents new compiler-only runtime helpers in gdcc_intrinsic.h. |
| doc/gdcc_low_ir.md | Documents compiler::<Name> grammar and where it’s permitted in LIR XML. |
| doc/gdcc_lir_intrinsic.md | Adds documentation for gdcc.for_range_iter.* intrinsics and their contracts. |
| doc/gdcc_c_backend.md | Notes compiler-only storage types must render via explicit gdcc_* helpers (no godot_* fallback). |
| doc/analysis/gdcompiler_type_design_risk_analysis.md | Design/risk analysis documenting leak risks and mitigation strategy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- remove duplicated `gd.script.gdcc.scope.*` import - remove duplicated `gd.script.gdcc.type.*` import - keep the change limited to import cleanup for review feedback
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
Introduce
GdCompilerTypeas a compiler-only storage abstraction and wire the first concreteGdccForRangeIterTypethrough frontend guards, LIR validation, and C backend intrinsics. This keeps loweredfor range(...)iterator state on backend-owned paths while preventing compiler-only types from leaking into source-facing or ABI-facing surfaces.What changed
GdCompilerTypeandGdccForRangeIterType, plus shared type-check helpers and updated type-system/runtime/backend docs.compiler::<Name>local-variable grammar, parser/serializer updates, use-site handling, and a newLirPublicAbiValidatorthat rejects compiler-only types on ABI-like surfaces.gdcc.for_range_iter.*intrinsics, and guards that seal ordinary codegen paths against compiler-only types.Why
for range(...)iterator state needs an internal storage type that is not a source-facing GDScript type and should not be modeled asVariant.godot_*naming or ABI surfaces, which is incorrect and hard to diagnose.GdCompilerTypemakes future compiler-owned storage types extend the same leak guards, LIR rules, lifecycle handling, and backend naming rules.Affected packages/files
gd.script.gdcc.type:GdCompilerType,GdccForRangeIterType,GdTypegd.script.gdcc.frontend.*: semantic analyzers, lowering session/processors, writeback supportgd.script.gdcc.lir.*: parser/serializer, type use-site handling,LirPublicAbiValidatorgd.script.gdcc.backend.c.*andsrc/main/c/codegen/include_451/gdcc/gdcc_intrinsic.hdoc/gdcc_type_system.md,doc/gdcc_low_ir.md,doc/gdcc_c_backend.md,doc/module_impl/frontend/frontend_gdcompiler_type_implementation.mdValidation
.\gradlew.bat test --tests gd.script.gdcc.type.GdCompilerTypeTest --tests gd.script.gdcc.frontend.lowering.FrontendLoweringBodyInsnPassTest --tests gd.script.gdcc.lir.validation.LirPublicAbiValidatorTest --tests gd.script.gdcc.backend.c.gen.GdccForRangeIterIntrinsicTest --no-daemon --info --console=plain.\gradlew.bat classes --no-daemon --info --console=plainResult:
BUILD SUCCESSFULRisks / Notes
Key behaviors covered (Optional)
compiler::GdccForRangeIterround-trips only on function-local variables and is rejected on ABI-like LIR surfaces.Variantor declared-type behavior.gdcc_*storage/init/destroy/intrinsic helpers and rejects compiler-only values on ordinary Godot runtime/codegen paths.Diff stats (Optional)
Breaking changes (Optional)
Related docs (Optional)
doc/gdcc_type_system.mddoc/gdcc_low_ir.mddoc/gdcc_c_backend.mddoc/module_impl/frontend/frontend_gdcompiler_type_implementation.md