From 7609b5070174905478ba33876679c9b592367a5c Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 2 Sep 2021 10:50:42 -0400 Subject: [PATCH] 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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/builtins.go | 2 +- src/go/types/call.go | 19 ++++--- src/go/types/eval.go | 11 +++-- src/go/types/expr.go | 49 ++++++++++++++++--- src/go/types/index.go | 10 +++- src/go/types/stmt.go | 4 +- src/go/types/testdata/examples/types.go2 | 1 + .../types/testdata/fixedbugs/issue39634.go2 | 3 ++ 8 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 698435bc207..ecf7b89275a 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -772,7 +772,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b var t operand x1 := x 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) x1 = &t // use incoming x only for first argument } diff --git a/src/go/types/call.go b/src/go/types/call.go index 78c81e13e91..8a80cbbd87e 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -89,8 +89,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { check.record(x) } else { - check.exprOrType(x, call.Fun) + check.exprOrType(x, call.Fun, true) } + // x.typ map be generic switch x.mode { case invalid: @@ -100,6 +101,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { case typexpr: // conversion + check.nonGeneric(x) + if x.mode == invalid { + return conversion + } T := x.typ x.mode = invalid switch n := len(call.Args); n { @@ -128,6 +133,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { return conversion case builtin: + // no need to check for non-genericity here id := x.id if !check.builtin(x, call, id) { x.mode = invalid @@ -141,6 +147,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { } // ordinary function/method call + // signature may be generic cgocall := x.mode == cgofunc 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 { goto Error } - if x.mode == typexpr { - x.typ = check.varType(e.X) - } - obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) if obj == nil { switch { @@ -722,7 +725,7 @@ func (check *Checker) use(arg ...ast.Expr) { // The nil check below is necessary since certain AST fields // may legally be nil (e.g., the ast.SliceExpr.High field). 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 { v.used = v_used // restore v.used } diff --git a/src/go/types/eval.go b/src/go/types/eval.go index 51259604c98..c8bb005eb6d 100644 --- a/src/go/types/eval.go +++ b/src/go/types/eval.go @@ -35,9 +35,10 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type return info.Types[node], err } -// CheckExpr type checks the expression expr as if it had appeared at -// position pos of package pkg. Type information about the expression -// is recorded in info. +// CheckExpr type checks the expression expr as if it had appeared at position +// pos of package pkg. Type information about the expression is recorded in +// info. The expression may be an uninstantiated parameterized function or +// type. // // If pkg == nil, the Universe scope is used and the provided // 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 var x operand - check.rawExpr(&x, expr, nil) - check.processDelayed(0) // incl. all functions + check.rawExpr(&x, expr, nil, true) // allow generic expressions + check.processDelayed(0) // incl. all functions check.recordUntyped() return nil diff --git a/src/go/types/expr.go b/src/go/types/expr.go index e5741565624..5ca4edebcb9 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1059,8 +1059,10 @@ const ( // rawExpr typechecks expression e and initializes x with the expression // 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 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 { check.trace(e.Pos(), "expr %s", e) check.indent++ @@ -1071,11 +1073,40 @@ func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { } kind := check.exprInternal(x, e, hint) + + if !allowGeneric { + check.nonGeneric(x) + } + check.record(x) 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. // 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 case *ast.ParenExpr: - kind := check.rawExpr(x, e.X, nil) + kind := check.rawExpr(x, e.X, nil, false) x.expr = e return kind @@ -1405,7 +1436,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { return check.callExpr(x, e) case *ast.StarExpr: - check.exprOrType(x, e.X) + check.exprOrType(x, e.X, false) switch x.mode { case invalid: 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. // func (check *Checker) expr(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) + check.rawExpr(x, e, nil, false) check.exclude(x, 1<= 0 switch typ := under(x.typ).(type) { diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index e5830bfdd41..92542597c51 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -174,7 +174,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) { var x operand var msg string var code errorCode - switch check.rawExpr(&x, call, nil) { + switch check.rawExpr(&x, call, nil, false) { case conversion: msg = "requires function call, not conversion" code = _InvalidDefer @@ -391,7 +391,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // function and method calls and receive operations can appear // in statement context. Such statements may be parenthesized." var x operand - kind := check.rawExpr(&x, s.X, nil) + kind := check.rawExpr(&x, s.X, nil, false) var msg string var code errorCode switch x.mode { diff --git a/src/go/types/testdata/examples/types.go2 b/src/go/types/testdata/examples/types.go2 index 1aebb411c69..6f6f95e781b 100644 --- a/src/go/types/testdata/examples/types.go2 +++ b/src/go/types/testdata/examples/types.go2 @@ -102,6 +102,7 @@ func _() { // Generic types cannot be used without instantiation. 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 // being instantiated. See also NOTES entry from 12/4/2019. diff --git a/src/go/types/testdata/fixedbugs/issue39634.go2 b/src/go/types/testdata/fixedbugs/issue39634.go2 index f307ee9c9e9..c46d38f8c40 100644 --- a/src/go/types/testdata/fixedbugs/issue39634.go2 +++ b/src/go/types/testdata/fixedbugs/issue39634.go2 @@ -85,6 +85,9 @@ var x T25 /* ERROR without instantiation */ .m1 // crash 26 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 */ } // crash 27