Skip to content

Commit 53c004f

Browse files
committed
go/types: local type-checking of alias declarations
Does not handle imports of packages with exported aliases yet. For #17592. Change-Id: Iee63fb9d521014995003a417271fbe0384ae04ef Reviewed-on: https://go-review.googlesource.com/32108 Reviewed-by: Alan Donovan <[email protected]>
1 parent f4c7a12 commit 53c004f

File tree

7 files changed

+234
-24
lines changed

7 files changed

+234
-24
lines changed

src/go/types/call.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -275,21 +275,24 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
275275
// so we don't need a "package" mode for operands: package names
276276
// can only appear in qualified identifiers which are mapped to
277277
// selector expressions.
278+
// (see also decl.go: checker.aliasDecl)
279+
// TODO(gri) factor this code out and share with checker.aliasDecl
278280
if ident, ok := e.X.(*ast.Ident); ok {
279281
_, obj := check.scope.LookupParent(ident.Name, check.pos)
280-
if pkg, _ := obj.(*PkgName); pkg != nil {
281-
assert(pkg.pkg == check.pkg)
282-
check.recordUse(ident, pkg)
283-
pkg.used = true
284-
exp := pkg.imported.scope.Lookup(sel)
282+
if pname, _ := obj.(*PkgName); pname != nil {
283+
assert(pname.pkg == check.pkg)
284+
check.recordUse(ident, pname)
285+
pname.used = true
286+
pkg := pname.imported
287+
exp := pkg.scope.Lookup(sel)
285288
if exp == nil {
286-
if !pkg.imported.fake {
287-
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
289+
if !pkg.fake {
290+
check.errorf(e.Pos(), "%s not declared by package %s", sel, pkg.name)
288291
}
289292
goto Error
290293
}
291294
if !exp.Exported() {
292-
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
295+
check.errorf(e.Pos(), "%s not exported by package %s", sel, pkg.name)
293296
// ok to continue
294297
}
295298
check.recordUse(e.Sel, exp)

src/go/types/decl.go

+82
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
8585
case *Func:
8686
// functions may be recursive - no need to track dependencies
8787
check.funcDecl(obj, d)
88+
case *Alias:
89+
// aliases cannot be recursive - no need to track dependencies
90+
check.aliasDecl(obj, d)
8891
default:
8992
unreachable()
9093
}
@@ -329,6 +332,85 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
329332
}
330333
}
331334

335+
func (check *Checker) aliasDecl(obj *Alias, decl *declInfo) {
336+
assert(obj.typ == nil)
337+
338+
// alias declarations cannot use iota
339+
assert(check.iota == nil)
340+
341+
// assume alias is invalid to start with
342+
obj.typ = Typ[Invalid]
343+
344+
// rhs must be package-qualified identifer pkg.sel (see also call.go: checker.selector)
345+
// TODO(gri) factor this code out and share with checker.selector
346+
rhs := decl.init
347+
var pkg *Package
348+
var sel *ast.Ident
349+
if sexpr, ok := rhs.(*ast.SelectorExpr); ok {
350+
if ident, ok := sexpr.X.(*ast.Ident); ok {
351+
_, obj := check.scope.LookupParent(ident.Name, check.pos)
352+
if pname, _ := obj.(*PkgName); pname != nil {
353+
assert(pname.pkg == check.pkg)
354+
check.recordUse(ident, pname)
355+
pname.used = true
356+
pkg = pname.imported
357+
sel = sexpr.Sel
358+
}
359+
}
360+
}
361+
if pkg == nil {
362+
check.errorf(rhs.Pos(), "invalid alias: %v is not a package-qualified identifier", rhs)
363+
return
364+
}
365+
366+
// qualified identifier must denote an exported object
367+
orig := pkg.scope.Lookup(sel.Name)
368+
if orig == nil || !orig.Exported() {
369+
if !pkg.fake {
370+
check.errorf(rhs.Pos(), "%s is not exported by package %s", sel.Name, pkg.name)
371+
}
372+
return
373+
}
374+
check.recordUse(sel, orig)
375+
376+
// An alias declaration must not refer to package unsafe.
377+
if orig.Pkg() == Unsafe {
378+
check.errorf(rhs.Pos(), "invalid alias: %s refers to package unsafe (%v)", obj.Name(), orig)
379+
return
380+
}
381+
382+
// The original must be of the same kind as the alias declaration.
383+
var why string
384+
switch obj.kind {
385+
case token.CONST:
386+
if _, ok := orig.(*Const); !ok {
387+
why = "constant"
388+
}
389+
case token.TYPE:
390+
if _, ok := orig.(*TypeName); !ok {
391+
why = "type"
392+
}
393+
case token.VAR:
394+
if _, ok := orig.(*Var); !ok {
395+
why = "variable"
396+
}
397+
case token.FUNC:
398+
if _, ok := orig.(*Func); !ok {
399+
why = "function"
400+
}
401+
default:
402+
unreachable()
403+
}
404+
if why != "" {
405+
check.errorf(rhs.Pos(), "invalid alias: %v is not a %s", orig, why)
406+
return
407+
}
408+
409+
// alias is valid
410+
obj.typ = orig.Type()
411+
obj.orig = orig
412+
}
413+
332414
func (check *Checker) declStmt(decl ast.Decl) {
333415
pkg := check.pkg
334416

src/go/types/object.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V
152152
}
153153

154154
func (obj *Const) Val() constant.Value { return obj.val }
155-
156-
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
155+
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
157156

158157
// A TypeName represents a declared type.
159158
type TypeName struct {
@@ -186,10 +185,8 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool
186185
}
187186

188187
func (obj *Var) Anonymous() bool { return obj.anonymous }
189-
190-
func (obj *Var) IsField() bool { return obj.isField }
191-
192-
func (*Var) isDependency() {} // a variable may be a dependency of an initialization expression
188+
func (obj *Var) IsField() bool { return obj.isField }
189+
func (*Var) isDependency() {} // a variable may be a dependency of an initialization expression
193190

194191
// A Func represents a declared function, concrete method, or abstract
195192
// (interface) method. Its Type() is always a *Signature.
@@ -215,11 +212,22 @@ func (obj *Func) FullName() string {
215212
return buf.String()
216213
}
217214

218-
func (obj *Func) Scope() *Scope {
219-
return obj.typ.(*Signature).scope
215+
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
216+
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
217+
218+
// An Alias represents a declared alias.
219+
type Alias struct {
220+
object
221+
kind token.Token // token.CONST, token.TYPE, token.VAR, or token.FUNC
222+
orig Object // aliased constant, type, variable, or function
223+
}
224+
225+
func NewAlias(pos token.Pos, pkg *Package, name string, kind token.Token, orig Object) *Alias {
226+
return &Alias{object{pos: pos, pkg: pkg, name: name}, kind, orig}
220227
}
221228

222-
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
229+
func (obj *Alias) Kind() token.Token { return obj.kind }
230+
func (obj *Alias) Orig() Object { return obj.orig }
223231

224232
// A Label represents a declared label.
225233
type Label struct {
@@ -279,6 +287,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
279287
}
280288
return
281289

290+
case *Alias:
291+
buf.WriteString("alias")
292+
282293
case *Label:
283294
buf.WriteString("label")
284295
typ = nil

src/go/types/resolver.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import (
1414
"unicode"
1515
)
1616

17-
// A declInfo describes a package-level const, type, var, or func declaration.
17+
// A declInfo describes a package-level const, type, var, func, or alias declaration.
1818
type declInfo struct {
1919
file *Scope // scope of file containing this declaration
2020
lhs []*Var // lhs of n:1 variable declarations, or nil
2121
typ ast.Expr // type, or nil
22-
init ast.Expr // init expression, or nil
22+
init ast.Expr // init/orig expression, or nil
2323
fdecl *ast.FuncDecl // func declaration, or nil
2424

2525
// The deps field tracks initialization expression dependencies.
@@ -275,7 +275,8 @@ func (check *Checker) collectObjects() {
275275
}
276276

277277
case *ast.AliasSpec:
278-
check.errorf(s.Name.Pos(), "cannot handle alias declarations yet")
278+
obj := NewAlias(s.Name.Pos(), pkg, s.Name.Name, d.Tok, nil)
279+
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, init: s.Orig})
279280

280281
case *ast.ValueSpec:
281282
switch d.Tok {

src/go/types/stdlib_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ func TestStdTest(t *testing.T) {
138138
}
139139

140140
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
141-
"alias2.go", // excluded until we can handle alias declarations
142141
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
143142
"sigchld.go", // don't work on Windows; testTestDir should consult build tags
144143
)

src/go/types/testdata/aliasdecl.src

+104-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,108 @@
44

55
package aliasdecl
66

7-
import "math"
7+
import (
8+
"flag"
9+
"fmt" // use at most once (to test "imported but not used" error)
10+
"go/build"
11+
. "go/build"
12+
"io"
13+
"math"
14+
"unsafe"
15+
)
816

9-
const _ = math.Pi
10-
const c /* ERROR "cannot handle alias declarations yet" */ => math.Pi
17+
// helper
18+
var before struct {
19+
f int
20+
}
21+
22+
// aliases must refer to package-qualified identifiers
23+
type _ => _ /* ERROR "_ is not a package-qualified identifier" */
24+
type t1 => _ /* ERROR "_ is not a package-qualified identifier" */
25+
26+
const _ => iota /* ERROR "iota is not a package-qualified identifier" */
27+
type _ => int /* ERROR "int is not a package-qualified identifier" */
28+
29+
const c => iota /* ERROR "iota is not a package-qualified identifier" */
30+
type t2 => int /* ERROR "int is not a package-qualified identifier" */
31+
32+
// dot-imported identifiers are not qualified identifiers
33+
// TODO(gri) fix error printing - should not print a qualified identifier...
34+
var _ => Default /* ERROR "Default is not a package-qualified identifier" */
35+
36+
// qualified identifiers must start with a package
37+
var _ => before /* ERROR "before.f is not a package-qualified identifier" */ .f
38+
func _ => before /* ERROR "before.f is not a package-qualified identifier" */ .f
39+
var _ => after /* ERROR "after.m is not a package-qualified identifier" */ .m
40+
func _ => after /* ERROR "after.m is not a package-qualified identifier" */ .m
41+
42+
var v1 => before /* ERROR "before.f is not a package-qualified identifier" */ .f
43+
func f1 => before /* ERROR "before.f is not a package-qualified identifier" */ .f
44+
var v2 => after /* ERROR "after.m is not a package-qualified identifier" */ .m
45+
func f2 => after /* ERROR "after.m is not a package-qualified identifier" */ .m
46+
47+
// TODO(gri) fix error printing - should print correct qualified identifier...
48+
var _ => Default /* ERROR "Default.ARCH is not a package-qualified identifier" */ .ARCH
49+
var _ Context // use dot-imported package go/build
50+
51+
// aliases may not refer to package unsafe
52+
type ptr => unsafe /* ERROR "refers to package unsafe" */ .Pointer
53+
func size => unsafe /* ERROR "refers to package unsafe" */ .Sizeof
54+
55+
// aliases must refer to entities of the same kind
56+
const _ => math.Pi
57+
const pi => math.Pi
58+
const pi1 => math /* ERROR "math.Sin.* is not a constant" */ .Sin
59+
60+
type _ => io.Writer
61+
type writer => io.Writer
62+
type writer1 => math /* ERROR "math.Sin.* is not a type" */ .Sin
63+
64+
var _ => build.Default
65+
var def => build.Default
66+
var def1 => build /* ERROR "build.Import.* is not a variable" */ .Import
67+
68+
func _ => math.Sin
69+
func sin => math.Sin
70+
func sin1 => math /* ERROR "math.Pi.* is not a function" */ .Pi
71+
72+
// using an incorrectly declared alias should not lead to more errors
73+
const _ = pi1
74+
type _ writer1
75+
var _ def1 = 0
76+
var _ = sin1
77+
78+
// aliases may not be called init
79+
func init /* ERROR "cannot declare init" */ => flag.Parse
80+
func _ => flag.Parse // use package flag
81+
82+
// alias reference to a package marks package as used
83+
func _ => fmt.Println
84+
85+
// re-exported aliases
86+
const Pi => math.Pi
87+
88+
type Writer => io.Writer
89+
90+
var Def => build.Default
91+
92+
func Sin => math.Sin
93+
94+
// const aliases may appear in "iota" context
95+
// (this verifies a type-checker internal assertion)
96+
const (
97+
_ = iota
98+
pi2 => math.Pi
99+
)
100+
101+
// type aliases denote identical types
102+
type myPackage => build.Package
103+
104+
var pkg myPackage
105+
var _ build.Package = pkg // valid assignment
106+
var _ *build.Package = &pkg // valid assignment
107+
108+
// helper
109+
type after struct{}
110+
111+
func (after) m() {}

src/go/types/typexpr.go

+13
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
4545
delete(check.unusedDotImports[scope], pkg)
4646
}
4747

48+
// An alias stands for the original object; use that one instead.
49+
if alias, _ := obj.(*Alias); alias != nil {
50+
if typ == Typ[Invalid] {
51+
return
52+
}
53+
obj = alias.orig
54+
// Aliases always refer to non-alias originals.
55+
if _, ok := obj.(*Alias); ok {
56+
panic("original is an alias")
57+
}
58+
assert(typ == obj.Type())
59+
}
60+
4861
switch obj := obj.(type) {
4962
case *PkgName:
5063
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)

0 commit comments

Comments
 (0)