Skip to content

if/else inside Group { … } builder triggers Swift type-checker crash #158

@leogdion

Description

@leogdion

Repro

import SyntaxKit

let _ = Group {
    if true {
        Struct("A") { Variable(.let, name: "x", type: "Int") }
    }
}

Fails to compile with:

error: failed to produce diagnostic for expression; please submit a bug report
let _ = Group {
        ^

Confirmed against Apple Swift version 6.3.2 (swiftlang-6.3.2.1.108) on arm64-apple-macosx26.0 against SyntaxKit at e5b1b73 (research/swift-manifest-codegen).

What should happen

CodeBlockBuilderResult declares buildEither(first:), buildEither(second:), buildOptional, and buildArray (Sources/SyntaxKit/CodeBlocks/CodeBlockBuilderResult.swift:46-71), so the API surface says if/else inside the builder is supported. The Swift type-checker should either accept the expression or emit a real diagnostic.

Probable cause

The type-checker is bailing out on overload resolution. CodeBlockBuilderResult has two buildBlock variants — (any CodeBlock...) -> [any CodeBlock] and ([any CodeBlock]...) -> [any CodeBlock] — combined with buildEither / buildOptional / buildArray over any CodeBlock existentials. The combination is likely exhausting the type-checker's effort budget and triggering the "failed to produce diagnostic" fallback rather than a real error.

Impact

Users can't write conditional codegen at the top level of a builder closure, even though the API documents it as supported. Affects anyone trying to emit conditional structures (platform gates, debug-only fields, optional members from runtime config, etc.). No unit tests under Tests/SyntaxKitTests/Unit/ exercise an if inside Group { … }, which is why this hasn't been caught.

Workaround

Hoist the conditional into a normal function that returns any CodeBlock, then call the function from inside the builder — the type-checker never sees the if at the builder level.

func extraField(_ include: Bool) -> any CodeBlock {
    if include { Variable(.let, name: "debug", type: "Bool") }
    else { EmptyCodeBlock() }
}

Group {
    Variable(.let, name: "name", type: "String")
    extraField(true)
}

References

  • `Docs/research/poc-step1-results.md` §4 — original discovery during the skitrun POC.
  • `Docs/research/codegen-cli-design.md` §7 — tracked as an open SyntaxKit bug, independent of the skitrun CLI design.
  • PR skit: SyntaxKit codegen CLI for v0.0.5 #156 (skitrun POC) — surfaces this for users writing input files to the codegen CLI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions