Skip to content

feat(compiler): implement GDScript function parameter default values from semantics to C codegen - #71

Merged
SuperIceCN merged 6 commits into
masterfrom
feat/fontend/param_default
Sep 4, 2026
Merged

feat(compiler): implement GDScript function parameter default values from semantics to C codegen#71
SuperIceCN merged 6 commits into
masterfrom
feat/fontend/param_default

Conversation

@Iridium-Zero

Copy link
Copy Markdown
Contributor

Summary

Implement source-function parameter default values (func f(a, b = 5):) end-to-end across frontend semantics, lowering, LIR, and C backend binding. Omitted trailing arguments are completed by hidden synthetic default functions; dynamic and engine-side calls use argc-aware callee wrappers rather than bind-time Variant defaults.

What changed

  • Frontend sema (FrontendParameterDefaultMetadataOwner, FrontendVisibleValueResolver, FrontendTypeCheckAnalyzer, FrontendCompileCheckAnalyzer): validate default-argument order and variadic constraints; resolve defaults in a dedicated visibility island; publish defaultValueFunc metadata for omitted arguments; type-check accepted defaults; rescan published facts onto the compile surface without duplicating diagnostics on rejected islands.
  • Frontend lowering (FrontendLoweringFunctionPreparationPass, CFG/body passes): materialize hidden instance/static shells for accepted defaults, publish PARAMETER_DEFAULT_INIT contexts, and lower expression-rooted CFGs that return the evaluated default. Instance shells take owning-class self; static shells take no parameters.
  • Backend C (BindingData, CGenHelper, entry.h.ftl / entry.c.ftl): isolate source defaults from the bind-time Variant channel (default_argument_count stays 0; defaultVariables stays empty and fail-fasts if non-empty). Exact/static calls complete omitted args at the call site; dynamic/engine/virtual dispatch complete them in argc-aware wrappers with per-method userdata (defaultSlotCount, _K_defslot, file-scope <Class>_<method>$default_ud).
  • Tests: semantic, compile-gate, lowering, codegen, Zig/Godot integration, and test-suite fixtures under default_args/ (dynamic fill, exact/static, ClassDB.class_call_static, per-call reevaluation, TOO_FEW/TOO_MANY, engine virtual _process(delta: float = 0.0)).
  • Docs: replace the rollout plan with frontend_parameter_default_implementation.md as the long-term fact source; retarget frontend, lowering, binding, diagnostic, and test-suite contracts.

Why

  • Source functions previously could not omit trailing arguments, so a core GDScript calling convention could not compile.
  • Defaults are ordinary expressions that must re-evaluate on each omitted-argument call (mutable array/dictionary defaults must not share a static instance). Bind-time method_info.default_arguments cannot express that, so GDCC keeps ClassDB default-argument count at 0 and completes missing args through synthetic functions.
  • Constructor / _init defaults remain permanently out of scope: GDExtension class constructor callbacks have no argument channel and only invoke zero-arg _init.
  • Lambda parameter defaults stay fail-closed; they are a separate surface from source-function defaults.

Affected packages/files

  • gd.script.gdcc.frontend.sema.analyzer (FrontendParameterDefaultMetadataOwner, FrontendCompileCheckAnalyzer, FrontendTypeCheckAnalyzer, FrontendSuiteResolver, FrontendVariableAnalyzer)
  • gd.script.gdcc.frontend.sema.resolver (FrontendVisibleValueResolver)
  • gd.script.gdcc.frontend.lowering (FunctionLoweringContext, FrontendLoweringFunctionPreparationPass, CFG/body passes)
  • gd.script.gdcc.backend.c.gen (CGenHelper, CCodegen, BindingData)
  • src/main/c/codegen/template_451/entry.c.ftl, entry.h.ftl
  • Tests — FrontendParameterDefaultMetadataOwnerTest, FrontendCompileCheckAnalyzerTest, FrontendLowering*PassTest, CGenHelperTest, CCodegenTest, CallMethodInsnGenTest, CallStaticMethodInsnGenTest, FrontendLoweringToCProjectBuilderIntegrationTest, GdScriptUnitTestCompileRunnerTest, src/test/test_suite/unit_test/{script,validation}/default_args/
  • Docs — doc/module_impl/frontend/frontend_parameter_default_implementation.md, related frontend/backend/diagnostic contracts, doc/gdcc_low_ir.md, doc/test_suite.md

Validation

  • script/run-gradle-targeted-tests.sh --tests FrontendParameterDefaultMetadataOwnerTest,FrontendCompileCheckAnalyzerTest,FrontendTypeCheckAnalyzerTest,FrontendLoweringFunctionPreparationPassTest,FrontendLoweringBuildCfgPassTest,FrontendLoweringBodyInsnPassTest,CGenHelperTest,CCodegenTest,CallMethodInsnGenTest,CallStaticMethodInsnGenTest,FrontendLoweringToCProjectBuilderIntegrationTest,GdScriptUnitTestCompileRunnerTest
  • ./gradlew classes --no-daemon --info --console=plain
  • ./gradlew clean build --no-daemon --console=plain

Result: BUILD SUCCESSFUL

Risks / Notes

  • Intentional non-goals: constructor/_init defaults (permanent); lambda parameter defaults; await inside defaults; referencing earlier parameters (func f(a, b = a)); ClassDB method_info.default_arguments registration; signal/@rpc/Callable.bind omitted-argument surfaces; inferring untyped parameter types from defaults (stay Variant); override default inheritance/merging.
  • Frozen contracts: LirParameterDef.defaultValueFunc is a hidden synthetic function, not call-site inline-only; defaultVariables is always empty; default_argument_count stays 0; wrapper dedup keys include defaultSlotCount.
  • Godot 4.5 GDExtensionMethodBind::call does not pre-check argc; Object.call with too few arguments aborts the current GDScript function. GDCC wrappers complete omitted source defaults before forwarding.
  • Direct $ / % in parameter defaults stay fail-closed.

Key behaviors covered

  • Required prefix + optional default suffix + optional variadic tail; invalid order and variadic-with-default diagnose sema.invalid_parameter_default_order
  • Visibility island: instance defaults may use self/instance members; static defaults may not; parameters/locals/captures/await/get-node diagnose sema.unsupported_parameter_default_expression
  • Exact/static calls complete omitted args at the call site; dynamic/ClassDB.class_call_static/engine virtual dispatch complete them in callee wrappers
  • Defaults re-evaluate on every omitted-argument call; destroyable OWNED/BORROWED results are cleaned up after the completed call
  • TOO_FEW / TOO_MANY arity diagnostics reuse the existing resolver path with published default metadata

Diff stats

  • 64 files changed, 4702 insertions(+), 263 deletions(-)

Breaking changes

  • None

Related docs

  • doc/module_impl/frontend/frontend_parameter_default_implementation.md
  • doc/module_impl/frontend/frontend_rules.md
  • doc/module_impl/backend/godot_binding_implementation.md
  • doc/module_impl/backend/call_method_implementation.md
  • doc/module_impl/frontend/diagnostic_manager.md
  • doc/gdcc_low_ir.md
  • doc/test_suite.md

- Define supported semantics and MVP boundaries for source function defaults
- Map frontend, lowering, backend, and binding integration work
- Document implementation steps, validation criteria, limitations, and test coverage
- Validate parameter default ordering and variadic constraints
- Resolve defaults in a dedicated visibility context
- Publish helper metadata for omitted arguments
- Add diagnostics and regression coverage for supported and rejected defaults
- Update frontend implementation documentation
- Rescan published parameter default facts while preserving upstream ownership for rejected islands
- Block unsupported coroutine and constructor default routes without duplicate diagnostics
- Expand compile-check coverage for supported, rejected, and wrapped default expressions
- Update frontend rules and implementation documentation
- Delegate parameter default type resolution in prepared test contexts
- Materialize hidden instance and static shells for accepted parameter defaults
- Build expression-rooted CFGs and return-producing bodies with self support
- Add fail-fast validation for metadata drift and shell name collisions
- Expand lowering regression coverage and update implementation documentation
- Route source defaults through argc-aware C wrappers with per-method userdata
- Isolate source defaults from the bind-time Variant channel
- Support exact, static, dynamic, and virtual dispatch default completion
- Preserve receiver handling, ownership cleanup, and per-call reevaluation
- Add backend regression coverage, end-to-end fixtures, and documentation
- Replace the completed rollout plan with a maintained implementation specification
- Update frontend, lowering, backend, and binding references for the closed pipeline
- Align implementation comments with finalized default-slot and synthetic-function contracts
- Document parameter-default diagnostics and fail-closed boundaries
@SuperIceCN
SuperIceCN merged commit 01d4959 into master Sep 4, 2026
1 check passed
@SuperIceCN
SuperIceCN deleted the feat/fontend/param_default branch September 4, 2026 08:16
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.

2 participants