feat(compiler): implement ternary conditional expressions from semantics to C codegen - #62
Merged
Merged
Conversation
…rnary operator - Define scope and Godot-aligned semantics for ternary expressions with right-associative nesting - Specify shared type inference and merged-type resolution with diagnostic ownership - Outline CFG construction for value and condition contexts with merge contract refinements - Detail staged implementation phases and testing matrix from parsing to end-to-end verification
- Anchor field mapping, right-associative nesting, and lowest-precedence binding at the parser boundary - Cover parenthesized associativity overrides, statement-position usage, and tolerant incomplete-syntax handling - Keep semantic analysis, CFG construction, and compile-gate surfaces unchanged for the baseline step - Align implementation plan status and acceptance criteria with the landed characterization
…onal expressions - Resolve ternary arms with contextual expected-type propagation and merge via the shared boundary compatibility matrix - Handle runtime-open, void, and incompatible arm pairs with ordered fallback to dynamic or Variant semantics - Promote conditional expressions from deferred to fully typed with right-associative nesting support - Preserve diagnostic ownership so arm failures re-emit at the conditional root with precise source ranges - Record phase completion in implementation plan and expand coverage for merging, contextuality, and error isolation
…owering - Anchor merge slot types at the shared expression result instead of per-arm source values - Route merge writes through the unified typed-boundary materialization as a re-derive consumer - Allow merge-of-merge sources across sequences while keeping dangling and mixed sources rejected - Update lowering and frontend rule documentation to reflect the refined contracts and Phase 2 completion - Add coverage for anchor typing, nested merge sources, and fail-fast edges with and/or LIR shape preserved
…pressions - Build value-context ternaries via shared branch-result merge with arm-private temps and merge-of-merge continuation - Expand condition-context ternaries as pure control flow reusing short-circuit branching without merge values - Preserve branch-local condition temps, anchor-typed merge slots, and preferred result identity for initializers - Clarify lowering and frontend gate docs to mark CFG contracts complete with compile-gate still pending - Expand coverage for nesting, associativity, short-circuit conditions/arms, and condition embeddings
… and complete integration validation - Remove explicit compile block and route ternary expressions through normal type resolution and CFG lowering - Expand focused coverage across compile checks, analysis passes, CFG shape, and body lowering for value, condition, and statement contexts - Add end-to-end suites covering mixed-type merges, nested associativity, object and null merges, truthiness, and discard semantics - Verify destroyable merge-slot lifecycle matches existing temporary and local slot ownership contracts - Synchronize implementation plans and frontend rule documentation to reflect the completed surface
…cord - Graduate conditional expression plan into maintained implementation documentation after full surface closure - Retire transitional phase markers and interim compile-gate notes - Synchronize cross-document references to the new implementation source of truth
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
Implement GDScript ternary conditional expressions (
value1 if condition else value2, including right-nested forms) across the full pipeline: frontend shared semantics, compile gate lift, CFG construction, body lowering, and C backend execution.What changed
ConditionalExpression(FrontendConditionalParseBehaviorTest); no parser changes needed.FrontendExpressionSemanticSupport): ternary type merge contract — condition resolved withexpectedType=null; both arms resolved against outerexpectedType; merge via bidirectionaldetermineFrontendBoundaryDecision, falling back toVariant; FAILED arm re-owns the rootFrontendExpressionType(binary-style); void arm rejected at root,void + Variantdegrades toDYNAMIC(Variant).FrontendCfgGraphBuilder/FrontendCfgGraph): dual-context expansion — value-context ternaries produce branch-resultMergeValueItemwith anchor-typed slots; condition-context ternaries expand as pure control flow with zero merge items;BranchNode.conditionValueIdmust be a temp, never a merge id.MergeValueItem,FrontendBodyLoweringSupport,FrontendSequenceItemInsnLoweringProcessors):merge_writere-derives viamaterializeFrontendBoundaryValue; graph-wide merge-of-merge widening validation; merge slot lifetime identical tocfg_tmp_*(declare +__prepare__default-construct + per-arm destroy-then-write +__finally__destroy) so the backend needs nocfg_merge_special cases.FrontendCompileCheckAnalyzer): removed the explicitConditionalExpressionintercept; it now falls into the default compile-surface walk with exact-range dedup.frontend_conditional_expression_implementation.md; synced 12 referencing docs and README support lists (ternary, plusfor/lambdaentries).ternary/script+validation fixture pairs wired intoGdScriptUnitTestCompileRunnerTest, covering same-type, int/float merge, nesting, object ancestor merge, null arm, non-bool condition, statement position, condition context, and destroyable arms.Why
Ternary conditionals were the last common expression form explicitly blocked at the compile gate. Godot semantics (single-arm evaluation, type merge with boundary conversion) map naturally onto the existing branch-result merge infrastructure, so the implementation reuses
MergeValueIteminstead of adding backend-specific merge handling.Affected packages/files
gd.script.gdcc.frontend.sema.analyzer(+support):FrontendCompileCheckAnalyzer,FrontendExpressionSemanticSupport,FrontendBodyOwnerProceduresgd.script.gdcc.frontend.lowering(+cfg,cfg.item,pass.body):FrontendCfgGraphBuilder,FrontendCfgGraph,MergeValueItem,FrontendBodyLoweringSupport,FrontendSequenceItemInsnLoweringProcessorsternary/fixturesdoc/module_impl/frontend/*(13 files),README.md,README.zh-CN.mdValidation
.\gradlew.bat test --tests "gd.script.gdcc.frontend.*" --no-daemon --console=plain— all frontend tests green (1276)pwsh -ExecutionPolicy Bypass -File script/run-gradle-targeted-tests.ps1 -Tests FrontendCompileCheckAnalyzerTest,FrontendCfgGraphBuilderTest,FrontendCfgGraphTest,FrontendLoweringBuildCfgPassTest,FrontendLoweringBodyInsnPassTest,GdScriptUnitTestCompileRunnerTest.\gradlew.bat clean build --no-daemon --console=plain— 3207 tests; only 5 failures from a Windows DLL file-lock on unrelated engine tests (AccessDeniedExceptiononconstruct_lambda_engine_debug_x86_64.dll), which pass when re-run individuallyternary_destroyable_arms/entry.cinspected: merge slot declared, default-constructed in__prepare__, destroy-then-write per arm, destroyed in__finally__Result:
BUILD SUCCESSFUL(targeted); full build green except environment-only DLL lock flakes.Risks / Notes
INCOMPATIBLE_TERNARYdiagnostic; incompatible merges fall back toVariantlike other binary merges.BranchNode.conditionValueIdreuse of a merge id is forbidden by contract and tests.Key behaviors covered
a if c1 else b if c2 else cint/floatviac_int_to_float)nullarm yields nullable/object-or-null mergeDiff stats
Breaking changes
Related docs
doc/module_impl/frontend/frontend_conditional_expression_implementation.mddoc/module_impl/frontend/frontend_lowering_cfg_pass_implementation.md