Commit e22c7966 authored by Robert Griesemer's avatar Robert Griesemer

go/types: handle imported aliases

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/32534Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent 34c480af
...@@ -12,6 +12,9 @@ import ( ...@@ -12,6 +12,9 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"internal/testenv" "internal/testenv"
"os"
"os/exec"
"path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"strings" "strings"
...@@ -1299,6 +1302,69 @@ func f(x int) { y := x; print(y) } ...@@ -1299,6 +1302,69 @@ func f(x int) { y := x; print(y) }
func TestAliases(t *testing.T) { func TestAliases(t *testing.T) {
testenv.MustHaveGoBuild(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 = ` const src = `
package p package p
...@@ -1324,7 +1390,7 @@ var _ = Implements(nil, nil) ...@@ -1324,7 +1390,7 @@ var _ = Implements(nil, nil)
Defs: make(map[*ast.Ident]Object), Defs: make(map[*ast.Ident]Object),
Uses: make(map[*ast.Ident]Object), Uses: make(map[*ast.Ident]Object),
} }
mustTypecheck(t, "Aliases", src, &info) mustTypecheck(t, "TestAliasDefUses", src, &info)
// verify Defs // verify Defs
defs := map[string]string{ defs := map[string]string{
......
...@@ -296,6 +296,13 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { ...@@ -296,6 +296,13 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// ok to continue // ok to continue
} }
check.recordUse(e.Sel, exp) 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: // Simplified version of the code for *ast.Idents:
// - imported objects are always fully initialized // - imported objects are always fully initialized
switch exp := exp.(type) { switch exp := exp.(type) {
...@@ -318,7 +325,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { ...@@ -318,7 +325,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
x.typ = exp.typ x.typ = exp.typ
x.id = exp.id x.id = exp.id
default: default:
check.dump("unexpected object %v (%T)", exp, exp) check.dump("unexpected object %v", exp)
unreachable() unreachable()
} }
x.expr = e x.expr = e
......
...@@ -332,6 +332,21 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { ...@@ -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) { func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) {
assert(obj.typ == nil) assert(obj.typ == nil)
...@@ -372,6 +387,12 @@ func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) { ...@@ -372,6 +387,12 @@ func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) {
return return
} }
check.recordUse(sel, orig) 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. // An alias declaration must not refer to package unsafe.
if orig.Pkg() == Unsafe { if orig.Pkg() == Unsafe {
......
// 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
...@@ -46,15 +46,12 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa ...@@ -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. // 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 alias, _ := obj.(*Alias); alias != nil {
if typ == Typ[Invalid] { obj = original(obj)
if obj == nil || typ == Typ[Invalid] {
return 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()) assert(typ == obj.Type())
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment