]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: unique LinkString for renamed, embedded fields
authorMatthew Dempsky <mdempsky@google.com>
Thu, 16 Dec 2021 20:55:15 +0000 (12:55 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 13 Jan 2022 21:37:29 +0000 (21:37 +0000)
Using type aliases, it's possible to create structs with embedded
fields that have no corresponding type literal notation. However, we
still need to generate a unique name for these types to use for linker
symbols. This CL introduces a new "struct{ Name = Type }" syntax for
use in LinkString formatting to represent these types.

Reattempt at CL 372914, which was rolled back due to race-y
LocalPkg.Lookup call that isn't safe for concurrency.

Fixes #50190.

Change-Id: I0b7fd81e1b0b3199a6afcffde96ade42495ad8d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/378434
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/types/fmt.go
test/fixedbugs/issue50190.go [new file with mode: 0644]

index 3198a1f53c8674c6234d6d2fcaa1eb402718a36f..e1b395559a0c192253f282ee09b0573913388307 100644 (file)
@@ -631,6 +631,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
        }
 
        var name string
+       nameSep := " "
        if verb != 'S' {
                s := f.Sym
 
@@ -639,7 +640,47 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
                        s = OrigSym(s)
                }
 
-               if s != nil && f.Embedded == 0 {
+               // Using type aliases and embedded fields, it's possible to
+               // construct types that can't be directly represented as a
+               // type literal. For example, given "type Int = int" (#50190),
+               // it would be incorrect to format "struct{ Int }" as either
+               // "struct{ int }" or "struct{ Int int }", because those each
+               // represent other, distinct types.
+               //
+               // So for the purpose of LinkString (i.e., fmtTypeID), we use
+               // the non-standard syntax "struct{ Int = int }" to represent
+               // embedded fields that have been renamed through the use of
+               // type aliases.
+               if f.Embedded != 0 {
+                       if mode == fmtTypeID {
+                               nameSep = " = "
+
+                               // Compute tsym, the symbol that would normally be used as
+                               // the field name when embedding f.Type.
+                               // TODO(mdempsky): Check for other occurences of this logic
+                               // and deduplicate.
+                               typ := f.Type
+                               if typ.IsPtr() {
+                                       base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ)
+                                       typ = typ.Elem()
+                               }
+                               tsym := typ.Sym()
+
+                               // If the field name matches the embedded type's name, then
+                               // suppress printing of the field name. For example, format
+                               // "struct{ T }" as simply that instead of "struct{ T = T }".
+                               if tsym != nil && (s == tsym || IsExported(tsym.Name) && s.Name == tsym.Name) {
+                                       s = nil
+                               }
+                       } else {
+                               // Suppress the field name for embedded fields for
+                               // non-LinkString formats, to match historical behavior.
+                               // TODO(mdempsky): Re-evaluate this.
+                               s = nil
+                       }
+               }
+
+               if s != nil {
                        if funarg != FunargNone {
                                name = fmt.Sprint(f.Nname)
                        } else if verb == 'L' {
@@ -658,7 +699,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
 
        if name != "" {
                b.WriteString(name)
-               b.WriteString(" ")
+               b.WriteString(nameSep)
        }
 
        if f.IsDDD() {
diff --git a/test/fixedbugs/issue50190.go b/test/fixedbugs/issue50190.go
new file mode 100644 (file)
index 0000000..a5ee646
--- /dev/null
@@ -0,0 +1,31 @@
+// run
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type Int = int
+
+type A = struct{ int }
+type B = struct{ Int }
+
+func main() {
+       var x, y interface{} = A{}, B{}
+       if x == y {
+               panic("FAIL")
+       }
+
+       {
+               type C = int32
+               x = struct{ C }{}
+       }
+       {
+               type C = uint32
+               y = struct{ C }{}
+       }
+       if x == y {
+               panic("FAIL")
+       }
+}