From 140c82e012af2f8204b5828cf0c5d023d467df96 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:55:18 -0700 Subject: [PATCH] compiler: disambiguate generic instance link names with local type args --- compiler/goroutine.go | 4 ++-- compiler/symbol.go | 26 ++++++++++++++++++++++++++ testdata/localtypes.txt | 6 +++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 30c8ef242..6b1a19edd 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -97,7 +97,7 @@ func (b *builder) createGo(instr *ssa.Go) { funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature)) params = append(params, context, funcPtr) hasContext = true - prefix = b.fn.RelString(nil) + prefix = b.getFunctionInfo(b.fn).linkName } paramBundle := b.emitPointerPack(params) @@ -139,7 +139,7 @@ func (b *builder) createWasmExport() { // Declare the exported function. paramTypes := b.llvmFnType.ParamTypes() exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false) - exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType) + exportedFn := llvm.AddFunction(b.mod, b.getFunctionInfo(b.fn).linkName+suffix, exportedFnType) b.addStandardAttributes(exportedFn) llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn) exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport)) diff --git a/compiler/symbol.go b/compiler/symbol.go index 9076e870d..d6f680c3f 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -323,6 +323,12 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { linkName: f.RelString(nil), } + // RelString is not unique for local type arguments, so add a suffix + // when needed. + if suffix := c.localTypeArgsSuffix(f); suffix != "" { + info.linkName += suffix + } + // Check for a few runtime functions that are treated specially. if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" { info.linkName = "_initialize" @@ -347,6 +353,26 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { return info } +func (c *compilerContext) localTypeArgsSuffix(f *ssa.Function) string { + typeArgs := f.TypeArgs() + if len(typeArgs) == 0 { + return "" + } + var hasLocal bool + parts := make([]string, len(typeArgs)) + for i, ta := range typeArgs { + name, isLocal := c.getTypeCodeName(ta) + if isLocal { + hasLocal = true + } + parts[i] = name + } + if !hasLocal { + return "" + } + return "$localtype:" + strings.Join(parts, ",") +} + // parsePragmas is used by getFunctionInfo to parse function pragmas such as // //export or //go:noinline. func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { diff --git a/testdata/localtypes.txt b/testdata/localtypes.txt index f42fafad6..dd5d72ddd 100644 --- a/testdata/localtypes.txt +++ b/testdata/localtypes.txt @@ -42,8 +42,8 @@ ok: doublyNestedInGeneric[string].Y accepts own ok: doublyNestedInGeneric[int].Y rejects [string].Y ok: doublyNestedInGeneric[string].Y rejects [int].Y issue4931A labels: 0 -issue4931B labels: 1 +issue4931B labels: 0 issue4931PairA labels: 0 -issue4931PairB labels: 1 +issue4931PairB labels: 0 issue4931MethodA labels: 0 -issue4931MethodB labels: 1 +issue4931MethodB labels: 0