mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
compiler: canonicalize method signature identities
The interface lowering pass used a separate formatter for method signatures. Same-named local aliases could therefore make distinct generic methods compare equal. Reuse getTypeCodeName so interface checks preserve type identity.
This commit is contained in:
+6
-110
@@ -980,14 +980,15 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
// getMethodSignatureName returns a unique name (that can be used as the name of
|
// getMethodSignatureName returns a unique name (that can be used as the name of
|
||||||
// a global) for the given method.
|
// a global) for the given method.
|
||||||
func (c *compilerContext) getMethodSignatureName(method *types.Func) string {
|
func (c *compilerContext) getMethodSignatureName(method *types.Func) string {
|
||||||
signature := methodSignature(method)
|
name := method.Name()
|
||||||
var globalName string
|
var prefix string
|
||||||
if token.IsExported(method.Name()) {
|
if token.IsExported(method.Name()) {
|
||||||
globalName = "reflect/methods." + signature
|
prefix = "reflect/methods."
|
||||||
} else {
|
} else {
|
||||||
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
prefix = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods."
|
||||||
}
|
}
|
||||||
return globalName
|
signature, _ := c.getTypeCodeName(method.Type())
|
||||||
|
return prefix + name + ":" + signature
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMethodSignature returns a global variable which is a reference to an
|
// getMethodSignature returns a global variable which is a reference to an
|
||||||
@@ -1298,108 +1299,3 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
|
|||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// methodSignature creates a readable version of a method signature (including
|
|
||||||
// the function name, excluding the receiver name). This string is used
|
|
||||||
// internally to match interfaces and to call the correct method on an
|
|
||||||
// interface. Examples:
|
|
||||||
//
|
|
||||||
// String() string
|
|
||||||
// Read([]byte) (int, error)
|
|
||||||
func methodSignature(method *types.Func) string {
|
|
||||||
return method.Name() + signature(method.Type().(*types.Signature))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a readable version of a function (pointer) signature.
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// () string
|
|
||||||
// (string, int) (int, error)
|
|
||||||
func signature(sig *types.Signature) string {
|
|
||||||
var s strings.Builder
|
|
||||||
if sig.Params().Len() == 0 {
|
|
||||||
s.WriteString("()")
|
|
||||||
} else {
|
|
||||||
s.WriteString("(")
|
|
||||||
i := 0
|
|
||||||
for v := range sig.Params().Variables() {
|
|
||||||
if i > 0 {
|
|
||||||
s.WriteString(", ")
|
|
||||||
}
|
|
||||||
s.WriteString(typestring(v.Type()))
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
s.WriteString(")")
|
|
||||||
}
|
|
||||||
if sig.Results().Len() == 0 {
|
|
||||||
// keep as-is
|
|
||||||
} else if sig.Results().Len() == 1 {
|
|
||||||
s.WriteString(" " + typestring(sig.Results().At(0).Type()))
|
|
||||||
} else {
|
|
||||||
s.WriteString(" (")
|
|
||||||
i := 0
|
|
||||||
for v := range sig.Results().Variables() {
|
|
||||||
if i > 0 {
|
|
||||||
s.WriteString(", ")
|
|
||||||
}
|
|
||||||
s.WriteString(typestring(v.Type()))
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
s.WriteString(")")
|
|
||||||
}
|
|
||||||
return s.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// typestring returns a stable (human-readable) type string for the given type
|
|
||||||
// that can be used for interface equality checks. It is almost (but not
|
|
||||||
// exactly) the same as calling t.String(). The main difference is some
|
|
||||||
// normalization around `byte` vs `uint8` for example.
|
|
||||||
func typestring(t types.Type) string {
|
|
||||||
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
|
||||||
switch t := types.Unalias(t).(type) {
|
|
||||||
case *types.Array:
|
|
||||||
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
|
||||||
case *types.Basic:
|
|
||||||
return basicTypeNames[t.Kind()]
|
|
||||||
case *types.Chan:
|
|
||||||
switch t.Dir() {
|
|
||||||
case types.SendRecv:
|
|
||||||
return "chan (" + typestring(t.Elem()) + ")"
|
|
||||||
case types.SendOnly:
|
|
||||||
return "chan<- (" + typestring(t.Elem()) + ")"
|
|
||||||
case types.RecvOnly:
|
|
||||||
return "<-chan (" + typestring(t.Elem()) + ")"
|
|
||||||
default:
|
|
||||||
panic("unknown channel direction")
|
|
||||||
}
|
|
||||||
case *types.Interface:
|
|
||||||
methods := make([]string, t.NumMethods())
|
|
||||||
for i := range methods {
|
|
||||||
method := t.Method(i)
|
|
||||||
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
|
|
||||||
}
|
|
||||||
return "interface{" + strings.Join(methods, ";") + "}"
|
|
||||||
case *types.Map:
|
|
||||||
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
|
|
||||||
case *types.Named:
|
|
||||||
return t.String()
|
|
||||||
case *types.Pointer:
|
|
||||||
return "*" + typestring(t.Elem())
|
|
||||||
case *types.Signature:
|
|
||||||
return "func" + signature(t)
|
|
||||||
case *types.Slice:
|
|
||||||
return "[]" + typestring(t.Elem())
|
|
||||||
case *types.Struct:
|
|
||||||
fields := make([]string, t.NumFields())
|
|
||||||
for i := range fields {
|
|
||||||
field := t.Field(i)
|
|
||||||
fields[i] = field.Name() + " " + typestring(field.Type())
|
|
||||||
if tag := t.Tag(i); tag != "" {
|
|
||||||
fields[i] += " " + strconv.Quote(tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "struct{" + strings.Join(fields, ";") + "}"
|
|
||||||
default:
|
|
||||||
panic("unknown type: " + t.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -192,5 +192,5 @@ declare i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}
|
|||||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Get() main.aliasMethodResult[main.F]" }
|
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Get:func:{}{named:main.aliasMethodResult[basic:float32]}" }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
|
|||||||
+1
-1
@@ -195,6 +195,6 @@ attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb
|
|||||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||||
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #11 = { nounwind }
|
attributes #11 = { nounwind }
|
||||||
|
|||||||
+1
-1
@@ -206,6 +206,6 @@ attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+cal
|
|||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||||
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||||
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #11 = { nounwind }
|
attributes #11 = { nounwind }
|
||||||
|
|||||||
Vendored
+4
-4
@@ -131,8 +131,8 @@ declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"
|
|||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}" }
|
||||||
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo:func:{basic:int}{basic:uint8}" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}; main.$methods.foo:func:{basic:int}{basic:uint8}" }
|
||||||
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error:func:{}{basic:string}" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||||
attributes #6 = { nounwind }
|
attributes #6 = { nounwind }
|
||||||
|
|||||||
Vendored
+2
-2
@@ -57,5 +57,5 @@ ok: aliasBox[float32] implements Get() float32
|
|||||||
ok: aliasBox[float64] implements Get() float64
|
ok: aliasBox[float64] implements Get() float64
|
||||||
ok: aliasMethod[float32] accepts own
|
ok: aliasMethod[float32] accepts own
|
||||||
ok: aliasMethod[float64] accepts own
|
ok: aliasMethod[float64] accepts own
|
||||||
BUG: aliasMethod[float32] rejects [float64]
|
ok: aliasMethod[float32] rejects [float64]
|
||||||
BUG: aliasMethod[float64] rejects [float32]
|
ok: aliasMethod[float64] rejects [float32]
|
||||||
|
|||||||
Reference in New Issue
Block a user