]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: systematic detection of missing instantiation
authorRobert Findley <rfindley@google.com>
Thu, 2 Sep 2021 14:50:42 +0000 (10:50 -0400)
committerRobert Findley <rfindley@google.com>
Thu, 2 Sep 2021 17:30:32 +0000 (17:30 +0000)
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>
src/go/types/builtins.go
src/go/types/call.go
src/go/types/eval.go
src/go/types/expr.go
src/go/types/index.go
src/go/types/stmt.go
src/go/types/testdata/examples/types.go2
src/go/types/testdata/fixedbugs/issue39634.go2

index 698435bc207be0e5c1f853bf61754f6e570f1443..ecf7b89275a9103b3d4e3ff9a54ec36a8b0c1758 100644 (file)
@@ -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
                }
index 78c81e13e9161a2c379309bf4d3fa6dfbc718321..8a80cbbd87e382c81265d97a719997d7f3842f1e 100644 (file)
@@ -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
                }
index 51259604c984c5d6d74274e9341de8349ba866b5..c8bb005eb6d4a13d638a8aa2e8b32176411c38d1 100644 (file)
@@ -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
index e5741565624b8274cdd02ab5c50695ea7f637303..5ca4edebcb90f49793769988599b75eec88ad2a3 100644 (file)
@@ -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<<novalue|1<<builtin|1<<typexpr)
        check.singleValue(x)
 }
 
 // multiExpr is like expr but the result may also be a multi-value.
 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)
 }
 
@@ -1542,16 +1573,18 @@ func (check *Checker) multiExpr(x *operand, e ast.Expr) {
 //
 func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
        assert(hint != nil)
-       check.rawExpr(x, e, hint)
+       check.rawExpr(x, e, hint, false)
        check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
        check.singleValue(x)
 }
 
 // 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.
 //
-func (check *Checker) exprOrType(x *operand, e ast.Expr) {
-       check.rawExpr(x, e, nil)
+func (check *Checker) exprOrType(x *operand, e ast.Expr, allowGeneric bool) {
+       check.rawExpr(x, e, nil, allowGeneric)
        check.exclude(x, 1<<novalue)
        check.singleValue(x)
 }
index a49bc5519cf3e9a5f29fdaf45d2595af2d1342ae..6b8fcc38808deafd8c7da417b1117d6392533e24 100644 (file)
@@ -16,7 +16,8 @@ import (
 // In that case x represents the uninstantiated function value and
 // it is the caller's responsibility to instantiate the function.
 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 {
        case invalid:
@@ -26,6 +27,7 @@ func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst
        case typexpr:
                // type instantiation
                x.mode = invalid
+               // TODO(gri) here we re-evaluate e.X - try to avoid this
                x.typ = check.varType(e.Orig)
                if x.typ != Typ[Invalid] {
                        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
        length := int64(-1) // valid if >= 0
        switch typ := under(x.typ).(type) {
index e5830bfdd4169740621bd1030b5ce6de968c13da..92542597c51beabaf9072b8bc2ecc3162e683f00 100644 (file)
@@ -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 {
index 1aebb411c69da0fb215465693d069929b88a3e15..6f6f95e781b6858c8ea311fb44b8e354154adb06 100644 (file)
@@ -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.
index f307ee9c9e9d0fa5bc14b3b28ded5a99bbe4919e..c46d38f8c4033b275be9ea8837865a6aff52286d 100644 (file)
@@ -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