fix(gengo): hoist every BEGINDUMP-supplied import, not just fmt/strings

doGenerate's deferred-imports patch handled only "fmt" and "strings"
— enough for inline RTL but a dead end for #pragma BEGINDUMP blocks
that import third-party / private packages (e.g. solmade's
internal/dartapi). hoistGoImports() registered every imported path
in g.imports correctly, but the final placeholder replacement never
emitted them, so the generated .go file's import block stayed
incomplete and `go build` failed with "undefined: dartapi".

Now every entry of g.imports that isn't already in the header gets
appended to the DEFERRED_IMPORTS substitution. Sorted output for
stable diffs.

Verified: a 4-line BEGINDUMP block in /tmp/poc_dartapi.prg that
imports "encoding/json" + "gitea.fivego.org/kwon_ai/solmade/internal/dartapi"
now compiles into a single 23 MB binary that calls
dartapi.AllAliases() at run time and returns the real count (3).
Full Five regression: go test compiler/, compat 56/56, std.ch 17/17,
FRB 7/7 all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 14:33:20 +09:00
parent f3e0ffe10c
commit 48bebda687

View File

@@ -180,14 +180,29 @@ func doGenerate(file *ast.File, debug, library bool) string {
} }
g.emitSymCache() g.emitSymCache()
// Patch deferred imports: inline RTL may add "fmt"/"strings" after header was emitted. // Patch deferred imports: emitDecl may add to g.imports after the
// header was written. The common cases are inline RTL pulling in
// "fmt"/"strings", but #pragma BEGINDUMP blocks can also hoist
// third-party / private modules (e.g. solmade's internal/dartapi).
// Dump every import not already in the header so external Go
// packages used inside BEGINDUMP actually link.
result := g.buf.String() result := g.buf.String()
var deferred string var deferredLines []string
if g.imports["fmt"] && !strings.Contains(result, "\"fmt\"") { for imp := range g.imports {
deferred += "\t\"fmt\"\n" if strings.Contains(result, "\""+imp+"\"") {
continue // already emitted in the initial import block
}
if alias, ok := g.importAlias[imp]; ok && alias != "" {
deferredLines = append(deferredLines, fmt.Sprintf("\t%s %q", alias, imp))
} else {
deferredLines = append(deferredLines, fmt.Sprintf("\t%q", imp))
}
} }
if g.imports["strings"] && !strings.Contains(result, "\"strings\"") { // Sorted for deterministic diffs.
deferred += "\t\"strings\"\n" sort.Strings(deferredLines)
deferred := strings.Join(deferredLines, "\n")
if deferred != "" {
deferred += "\n"
} }
result = strings.Replace(result, "\t/*DEFERRED_IMPORTS*/\n", deferred, 1) result = strings.Replace(result, "\t/*DEFERRED_IMPORTS*/\n", deferred, 1)
// Patch guards // Patch guards