mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types: systematic detection of missing instantiation
This is a port of CL 346471 to go/types. Additionally, CheckExpr was updated for the new API to explicitly allow generic expressions. The error messages in issue39634.go2 are different because go/parser produces an IndexExpr with BadExpr index value, for backward compatibility. Change-Id: I725926de183a016381513fe0e750d1280688ce29 Reviewed-on: https://go-review.googlesource.com/c/go/+/347391 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
acc2957bc9
commit
7609b50701
8 changed files with 74 additions and 25 deletions
|
|
@ -772,7 +772,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
var t operand
|
var t operand
|
||||||
x1 := x
|
x1 := x
|
||||||
for _, arg := range call.Args {
|
for _, arg := range call.Args {
|
||||||
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
|
check.rawExpr(x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T))
|
||||||
check.dump("%v: %s", x1.Pos(), x1)
|
check.dump("%v: %s", x1.Pos(), x1)
|
||||||
x1 = &t // use incoming x only for first argument
|
x1 = &t // use incoming x only for first argument
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
||||||
check.record(x)
|
check.record(x)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
check.exprOrType(x, call.Fun)
|
check.exprOrType(x, call.Fun, true)
|
||||||
}
|
}
|
||||||
|
// x.typ map be generic
|
||||||
|
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
|
|
@ -100,6 +101,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
||||||
|
|
||||||
case typexpr:
|
case typexpr:
|
||||||
// conversion
|
// conversion
|
||||||
|
check.nonGeneric(x)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return conversion
|
||||||
|
}
|
||||||
T := x.typ
|
T := x.typ
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
switch n := len(call.Args); n {
|
switch n := len(call.Args); n {
|
||||||
|
|
@ -128,6 +133,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
||||||
return conversion
|
return conversion
|
||||||
|
|
||||||
case builtin:
|
case builtin:
|
||||||
|
// no need to check for non-genericity here
|
||||||
id := x.id
|
id := x.id
|
||||||
if !check.builtin(x, call, id) {
|
if !check.builtin(x, call, id) {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
|
|
@ -141,6 +147,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ordinary function/method call
|
// ordinary function/method call
|
||||||
|
// signature may be generic
|
||||||
cgocall := x.mode == cgofunc
|
cgocall := x.mode == cgofunc
|
||||||
|
|
||||||
sig := asSignature(x.typ)
|
sig := asSignature(x.typ)
|
||||||
|
|
@ -478,15 +485,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check.exprOrType(x, e.X)
|
check.exprOrType(x, e.X, false)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
if x.mode == typexpr {
|
|
||||||
x.typ = check.varType(e.X)
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
|
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -722,7 +725,7 @@ func (check *Checker) use(arg ...ast.Expr) {
|
||||||
// The nil check below is necessary since certain AST fields
|
// The nil check below is necessary since certain AST fields
|
||||||
// may legally be nil (e.g., the ast.SliceExpr.High field).
|
// may legally be nil (e.g., the ast.SliceExpr.High field).
|
||||||
if e != nil {
|
if e != nil {
|
||||||
check.rawExpr(&x, e, nil)
|
check.rawExpr(&x, e, nil, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -754,7 +757,7 @@ func (check *Checker) useLHS(arg ...ast.Expr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.rawExpr(&x, e, nil)
|
check.rawExpr(&x, e, nil, false)
|
||||||
if v != nil {
|
if v != nil {
|
||||||
v.used = v_used // restore v.used
|
v.used = v_used // restore v.used
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,10 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type
|
||||||
return info.Types[node], err
|
return info.Types[node], err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckExpr type checks the expression expr as if it had appeared at
|
// CheckExpr type checks the expression expr as if it had appeared at position
|
||||||
// position pos of package pkg. Type information about the expression
|
// pos of package pkg. Type information about the expression is recorded in
|
||||||
// is recorded in info.
|
// info. The expression may be an uninstantiated parameterized function or
|
||||||
|
// type.
|
||||||
//
|
//
|
||||||
// If pkg == nil, the Universe scope is used and the provided
|
// If pkg == nil, the Universe scope is used and the provided
|
||||||
// position pos is ignored. If pkg != nil, and pos is invalid,
|
// position pos is ignored. If pkg != nil, and pos is invalid,
|
||||||
|
|
@ -91,8 +92,8 @@ func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr,
|
||||||
|
|
||||||
// evaluate node
|
// evaluate node
|
||||||
var x operand
|
var x operand
|
||||||
check.rawExpr(&x, expr, nil)
|
check.rawExpr(&x, expr, nil, true) // allow generic expressions
|
||||||
check.processDelayed(0) // incl. all functions
|
check.processDelayed(0) // incl. all functions
|
||||||
check.recordUntyped()
|
check.recordUntyped()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1059,8 +1059,10 @@ const (
|
||||||
// rawExpr typechecks expression e and initializes x with the expression
|
// rawExpr typechecks expression e and initializes x with the expression
|
||||||
// value or type. If an error occurred, x.mode is set to invalid.
|
// value or type. If an error occurred, x.mode is set to invalid.
|
||||||
// If hint != nil, it is the type of a composite literal element.
|
// If hint != nil, it is the type of a composite literal element.
|
||||||
|
// If allowGeneric is set, the operand type may be an uninstantiated
|
||||||
|
// parameterized type or function value.
|
||||||
//
|
//
|
||||||
func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
|
func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind {
|
||||||
if trace {
|
if trace {
|
||||||
check.trace(e.Pos(), "expr %s", e)
|
check.trace(e.Pos(), "expr %s", e)
|
||||||
check.indent++
|
check.indent++
|
||||||
|
|
@ -1071,11 +1073,40 @@ func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
kind := check.exprInternal(x, e, hint)
|
kind := check.exprInternal(x, e, hint)
|
||||||
|
|
||||||
|
if !allowGeneric {
|
||||||
|
check.nonGeneric(x)
|
||||||
|
}
|
||||||
|
|
||||||
check.record(x)
|
check.record(x)
|
||||||
|
|
||||||
return kind
|
return kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If x is a generic function or type, nonGeneric reports an error and invalidates x.mode and x.typ.
|
||||||
|
// Otherwise it leaves x alone.
|
||||||
|
func (check *Checker) nonGeneric(x *operand) {
|
||||||
|
if x.mode == invalid || x.mode == novalue {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var what string
|
||||||
|
switch t := x.typ.(type) {
|
||||||
|
case *Named:
|
||||||
|
if isGeneric(t) {
|
||||||
|
what = "type"
|
||||||
|
}
|
||||||
|
case *Signature:
|
||||||
|
if t.tparams != nil {
|
||||||
|
what = "function"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if what != "" {
|
||||||
|
check.errorf(x.expr, _Todo, "cannot use generic %s %s without instantiation", what, x.expr)
|
||||||
|
x.mode = invalid
|
||||||
|
x.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// exprInternal contains the core of type checking of expressions.
|
// exprInternal contains the core of type checking of expressions.
|
||||||
// Must only be called by rawExpr.
|
// Must only be called by rawExpr.
|
||||||
//
|
//
|
||||||
|
|
@ -1354,7 +1385,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
x.typ = typ
|
x.typ = typ
|
||||||
|
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
kind := check.rawExpr(x, e.X, nil)
|
kind := check.rawExpr(x, e.X, nil, false)
|
||||||
x.expr = e
|
x.expr = e
|
||||||
return kind
|
return kind
|
||||||
|
|
||||||
|
|
@ -1405,7 +1436,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
return check.callExpr(x, e)
|
return check.callExpr(x, e)
|
||||||
|
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
check.exprOrType(x, e.X)
|
check.exprOrType(x, e.X, false)
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
goto Error
|
goto Error
|
||||||
|
|
@ -1525,14 +1556,14 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface,
|
||||||
// If an error occurred, x.mode is set to invalid.
|
// If an error occurred, x.mode is set to invalid.
|
||||||
//
|
//
|
||||||
func (check *Checker) expr(x *operand, e ast.Expr) {
|
func (check *Checker) expr(x *operand, e ast.Expr) {
|
||||||
check.rawExpr(x, e, nil)
|
check.rawExpr(x, e, nil, false)
|
||||||
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
||||||
check.singleValue(x)
|
check.singleValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiExpr is like expr but the result may also be a multi-value.
|
// multiExpr is like expr but the result may also be a multi-value.
|
||||||
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
|
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
|
||||||
check.rawExpr(x, e, nil)
|
check.rawExpr(x, e, nil, false)
|
||||||
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1542,16 +1573,18 @@ func (check *Checker) multiExpr(x *operand, e ast.Expr) {
|
||||||
//
|
//
|
||||||
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
|
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
|
||||||
assert(hint != nil)
|
assert(hint != nil)
|
||||||
check.rawExpr(x, e, hint)
|
check.rawExpr(x, e, hint, false)
|
||||||
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
|
||||||
check.singleValue(x)
|
check.singleValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
|
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
|
||||||
|
// If allowGeneric is set, the operand type may be an uninstantiated parameterized type or function
|
||||||
|
// value.
|
||||||
// If an error occurred, x.mode is set to invalid.
|
// If an error occurred, x.mode is set to invalid.
|
||||||
//
|
//
|
||||||
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
|
func (check *Checker) exprOrType(x *operand, e ast.Expr, allowGeneric bool) {
|
||||||
check.rawExpr(x, e, nil)
|
check.rawExpr(x, e, nil, allowGeneric)
|
||||||
check.exclude(x, 1<<novalue)
|
check.exclude(x, 1<<novalue)
|
||||||
check.singleValue(x)
|
check.singleValue(x)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import (
|
||||||
// In that case x represents the uninstantiated function value and
|
// In that case x represents the uninstantiated function value and
|
||||||
// it is the caller's responsibility to instantiate the function.
|
// it is the caller's responsibility to instantiate the function.
|
||||||
func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
|
func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
|
||||||
check.exprOrType(x, e.X)
|
check.exprOrType(x, e.X, true)
|
||||||
|
// x may be generic
|
||||||
|
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
|
|
@ -26,6 +27,7 @@ func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst
|
||||||
case typexpr:
|
case typexpr:
|
||||||
// type instantiation
|
// type instantiation
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
|
// TODO(gri) here we re-evaluate e.X - try to avoid this
|
||||||
x.typ = check.varType(e.Orig)
|
x.typ = check.varType(e.Orig)
|
||||||
if x.typ != Typ[Invalid] {
|
if x.typ != Typ[Invalid] {
|
||||||
x.mode = typexpr
|
x.mode = typexpr
|
||||||
|
|
@ -39,6 +41,12 @@ func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// x should not be generic at this point, but be safe and check
|
||||||
|
check.nonGeneric(x)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
valid := false
|
valid := false
|
||||||
length := int64(-1) // valid if >= 0
|
length := int64(-1) // valid if >= 0
|
||||||
switch typ := under(x.typ).(type) {
|
switch typ := under(x.typ).(type) {
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
|
||||||
var x operand
|
var x operand
|
||||||
var msg string
|
var msg string
|
||||||
var code errorCode
|
var code errorCode
|
||||||
switch check.rawExpr(&x, call, nil) {
|
switch check.rawExpr(&x, call, nil, false) {
|
||||||
case conversion:
|
case conversion:
|
||||||
msg = "requires function call, not conversion"
|
msg = "requires function call, not conversion"
|
||||||
code = _InvalidDefer
|
code = _InvalidDefer
|
||||||
|
|
@ -391,7 +391,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
// function and method calls and receive operations can appear
|
// function and method calls and receive operations can appear
|
||||||
// in statement context. Such statements may be parenthesized."
|
// in statement context. Such statements may be parenthesized."
|
||||||
var x operand
|
var x operand
|
||||||
kind := check.rawExpr(&x, s.X, nil)
|
kind := check.rawExpr(&x, s.X, nil, false)
|
||||||
var msg string
|
var msg string
|
||||||
var code errorCode
|
var code errorCode
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
|
|
|
||||||
1
src/go/types/testdata/examples/types.go2
vendored
1
src/go/types/testdata/examples/types.go2
vendored
|
|
@ -102,6 +102,7 @@ func _() {
|
||||||
|
|
||||||
// Generic types cannot be used without instantiation.
|
// Generic types cannot be used without instantiation.
|
||||||
var _ T // ERROR cannot use generic type T
|
var _ T // ERROR cannot use generic type T
|
||||||
|
var _ = T /* ERROR cannot use generic type T */ (0)
|
||||||
|
|
||||||
// In type context, generic (parameterized) types cannot be parenthesized before
|
// In type context, generic (parameterized) types cannot be parenthesized before
|
||||||
// being instantiated. See also NOTES entry from 12/4/2019.
|
// being instantiated. See also NOTES entry from 12/4/2019.
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@ var x T25 /* ERROR without instantiation */ .m1
|
||||||
|
|
||||||
// crash 26
|
// crash 26
|
||||||
type T26 = interface{ F26[ /* ERROR methods cannot have type parameters */ Z any]() }
|
type T26 = interface{ F26[ /* ERROR methods cannot have type parameters */ Z any]() }
|
||||||
|
// The error messages on the line below differ from types2 because for backward
|
||||||
|
// compatibility go/parser must produce an IndexExpr with BadExpr index for the
|
||||||
|
// expression F26[].
|
||||||
func F26[Z any]() T26 { return F26[] /* ERROR operand */ }
|
func F26[Z any]() T26 { return F26[] /* ERROR operand */ }
|
||||||
|
|
||||||
// crash 27
|
// crash 27
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue