]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: handle imported aliases
authorRobert Griesemer <gri@golang.org>
Tue, 1 Nov 2016 17:38:24 +0000 (10:38 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 1 Nov 2016 20:33:58 +0000 (20:33 +0000)
When we lookup a qualified identifier, we need to unpack
an alias. Do this in all places, not just one. Duh!

Fixes #17716.
For #17592.

Change-Id: I58d57b17cc635d0408b370f109c719c16757fd8e
Reviewed-on: https://go-review.googlesource.com/32534
Reviewed-by: Alan Donovan <adonovan@google.com>
src/go/types/api_test.go
src/go/types/call.go
src/go/types/decl.go
src/go/types/testdata/alias.go [new file with mode: 0644]
src/go/types/typexpr.go

index 7c4fe575c84e1e6f2b6843a1f74ec0d61c3cf309..4db9760ba5927d20e487122280698fa12f27e45c 100644 (file)
@@ -12,6 +12,9 @@ import (
        "go/parser"
        "go/token"
        "internal/testenv"
+       "os"
+       "os/exec"
+       "path/filepath"
        "reflect"
        "regexp"
        "strings"
@@ -1299,6 +1302,69 @@ func f(x int) { y := x; print(y) }
 func TestAliases(t *testing.T) {
        testenv.MustHaveGoBuild(t)
 
+       const src = `
+package b
+
+import (
+       "./testdata/alias"
+       a "./testdata/alias"
+       // "math" // TODO(gri) does not work yet - fix importer (issue #17726)
+)
+
+const (
+       c1 = alias.Pi
+       c2 => a.Pi
+)
+
+var (
+       v1 => alias.Default
+       v2 => a.Default
+       v3 = f1
+)
+
+type (
+       t1 => alias.Context
+       t2 => a.Context
+)
+
+func f1 => alias.Sin
+func f2 => a.Sin
+
+func _() {
+       assert(c1 == c2 && c1 == alias.Pi && c2 == a.Pi)
+       v1 = v2 // must be assignable
+       var _ *t1 = new(t2) // must be assignable
+       var _ t2 = alias.Default
+       f1(1) // must be callable
+       f2(1)
+       _ = alias.Sin(1)
+       _ = a.Sin(1)
+}
+`
+
+       if out := compile(t, "testdata", "alias.go"); out != "" {
+               defer os.Remove(out)
+       }
+
+       DefPredeclaredTestFuncs() // declare assert built-in for testing
+       mustTypecheck(t, "Aliases", src, nil)
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+       cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", filename)
+       cmd.Dir = dirname
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Logf("%s", out)
+               t.Fatalf("go tool compile %s failed: %s", filename, err)
+       }
+       // filename should end with ".go"
+       return filepath.Join(dirname, filename[:len(filename)-2]+"o")
+}
+
+func TestAliasDefUses(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
        const src = `
 package p
 
@@ -1324,7 +1390,7 @@ var _ = Implements(nil, nil)
                Defs: make(map[*ast.Ident]Object),
                Uses: make(map[*ast.Ident]Object),
        }
-       mustTypecheck(t, "Aliases", src, &info)
+       mustTypecheck(t, "TestAliasDefUses", src, &info)
 
        // verify Defs
        defs := map[string]string{
index 0d1fb34bce5c81b9ddf7581e40549169eb04020d..8e5c5371f229f7a98f0aef7dbafecdeed2413d0d 100644 (file)
@@ -296,6 +296,13 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
                                // ok to continue
                        }
                        check.recordUse(e.Sel, exp)
+                       exp = original(exp)
+
+                       // avoid further errors if the imported object is an alias that's broken
+                       if exp == nil {
+                               goto Error
+                       }
+
                        // Simplified version of the code for *ast.Idents:
                        // - imported objects are always fully initialized
                        switch exp := exp.(type) {
@@ -318,7 +325,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
                                x.typ = exp.typ
                                x.id = exp.id
                        default:
-                               check.dump("unexpected object %v (%T)", exp, exp)
+                               check.dump("unexpected object %v", exp)
                                unreachable()
                        }
                        x.expr = e
index 89c56534d296035a8b5a234fa8e772ba9bf716b7..be04f0d82ed557c48a9bbc037bb48785876e5ca0 100644 (file)
@@ -332,6 +332,21 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
        }
 }
 
+// original returns the original Object if obj is an Alias;
+// otherwise it returns obj. The result is never an Alias,
+// but it may be nil.
+func original(obj Object) Object {
+       // an alias stands for the original object; use that one instead
+       if alias, _ := obj.(*Alias); alias != nil {
+               obj = alias.orig
+               // aliases always refer to non-alias originals
+               if _, ok := obj.(*Alias); ok {
+                       panic("original is an alias")
+               }
+       }
+       return obj
+}
+
 func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) {
        assert(obj.typ == nil)
 
@@ -372,6 +387,12 @@ func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) {
                return
        }
        check.recordUse(sel, orig)
+       orig = original(orig)
+
+       // avoid further errors if the imported object is an alias that's broken
+       if orig == nil {
+               return
+       }
 
        // An alias declaration must not refer to package unsafe.
        if orig.Pkg() == Unsafe {
diff --git a/src/go/types/testdata/alias.go b/src/go/types/testdata/alias.go
new file mode 100644 (file)
index 0000000..c74aeaa
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 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.
+
+// Used by TestAliases (api_test.go).
+
+package alias
+
+import (
+       "go/build"
+       "math"
+)
+
+const Pi => math.Pi
+
+var Default => build.Default
+
+type Context => build.Context
+
+func Sin => math.Sin
index 012d3a7034ca52122ae705dba7e0d3f94d7f4c6f..d78d2fa98c2469f863ab064077cee9b21326c43c 100644 (file)
@@ -46,15 +46,12 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
        }
 
        // An alias stands for the original object; use that one instead.
+       // TODO(gri) We should be able to factor out the Typ[Invalid] test.
        if alias, _ := obj.(*Alias); alias != nil {
-               if typ == Typ[Invalid] {
+               obj = original(obj)
+               if obj == nil || typ == Typ[Invalid] {
                        return
                }
-               obj = alias.orig
-               // Aliases always refer to non-alias originals.
-               if _, ok := obj.(*Alias); ok {
-                       panic("original is an alias")
-               }
                assert(typ == obj.Type())
        }