]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/go/types/typexpr.go
go/types, types2: implement Alias proposal (export API)
[gostls13.git] / src / go / types / typexpr.go
index 3636c8556a1f113c17fae381a69dd6dee77cdf62..2835958d9842c8bffe10501f1bc512d0ae2ce180 100644 (file)
@@ -11,7 +11,7 @@ import (
        "go/ast"
        "go/constant"
        "go/internal/typeparams"
-       "go/token"
+       . "internal/types/errors"
        "strings"
 )
 
@@ -19,8 +19,7 @@ import (
 // If an error occurred, x.mode is set to invalid.
 // For the meaning of def, see Checker.definedType, below.
 // If wantType is set, the identifier e is expected to denote a type.
-//
-func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) {
+func (check *Checker) ident(x *operand, e *ast.Ident, def *TypeName, wantType bool) {
        x.mode = invalid
        x.expr = e
 
@@ -37,15 +36,14 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
                                x.mode = typexpr
                                x.typ = tpar
                        } else {
-                               check.error(e, _InvalidBlank, "cannot use _ as value or type")
+                               check.error(e, InvalidBlank, "cannot use _ as value or type")
                        }
                } else {
-                       check.errorf(e, _UndeclaredName, "undeclared name: %s", e.Name)
+                       check.errorf(e, UndeclaredName, "undefined: %s", e.Name)
                }
                return
        case universeAny, universeComparable:
-               if !check.allowVersion(check.pkg, 1, 18) {
-                       check.errorf(e, _UndeclaredName, "undeclared name: %s (requires version go1.18 or later)", e.Name)
+               if !check.verifyVersionf(e, go1_18, "predeclared %s", e.Name) {
                        return // avoid follow-on errors
                }
        }
@@ -58,7 +56,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
        // a cycle which needs to be reported). Otherwise we can skip the
        // call and avoid a possible cycle error in favor of the more
        // informative "not a type/value" error that this function's caller
-       // will issue (see issue #25790).
+       // will issue (see go.dev/issue/25790).
        typ := obj.Type()
        if _, gotType := obj.(*TypeName); typ == nil || gotType && wantType {
                check.objDecl(obj, def)
@@ -76,17 +74,17 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
 
        switch obj := obj.(type) {
        case *PkgName:
-               check.errorf(e, _InvalidPkgUse, "use of package %s not in selector", obj.name)
+               check.errorf(e, InvalidPkgUse, "use of package %s not in selector", obj.name)
                return
 
        case *Const:
                check.addDeclDep(obj)
-               if typ == Typ[Invalid] {
+               if !isValid(typ) {
                        return
                }
                if obj == universeIota {
                        if check.iota == nil {
-                               check.errorf(e, _InvalidIota, "cannot use iota outside constant declaration")
+                               check.error(e, InvalidIota, "cannot use iota outside constant declaration")
                                return
                        }
                        x.val = check.iota
@@ -97,6 +95,10 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
                x.mode = constant_
 
        case *TypeName:
+               if !check.enableAlias && check.isBrokenAlias(obj) {
+                       check.errorf(e, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", obj.name)
+                       return
+               }
                x.mode = typexpr
 
        case *Var:
@@ -107,7 +109,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
                        obj.used = true
                }
                check.addDeclDep(obj)
-               if typ == Typ[Invalid] {
+               if !isValid(typ) {
                        return
                }
                x.mode = variable
@@ -141,48 +143,59 @@ func (check *Checker) typ(e ast.Expr) Type {
 // constraint interface.
 func (check *Checker) varType(e ast.Expr) Type {
        typ := check.definedType(e, nil)
-       // We don't want to call under() (via asInterface) or complete interfaces while we
-       // are in the middle of type-checking parameter declarations that might belong to
-       // interface methods. Delay this check to the end of type-checking.
+       check.validVarType(e, typ)
+       return typ
+}
+
+// validVarType reports an error if typ is a constraint interface.
+// The expression e is used for error reporting, if any.
+func (check *Checker) validVarType(e ast.Expr, typ Type) {
+       // If we have a type parameter there's nothing to do.
+       if isTypeParam(typ) {
+               return
+       }
+
+       // We don't want to call under() or complete interfaces while we are in
+       // the middle of type-checking parameter declarations that might belong
+       // to interface methods. Delay this check to the end of type-checking.
        check.later(func() {
-               if t := asInterface(typ); t != nil {
+               if t, _ := under(typ).(*Interface); t != nil {
                        tset := computeInterfaceTypeSet(check, e.Pos(), t) // TODO(gri) is this the correct position?
                        if !tset.IsMethodSet() {
                                if tset.comparable {
-                                       check.softErrorf(e, _Todo, "interface is (or embeds) comparable")
+                                       check.softErrorf(e, MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface is (or embeds) comparable", typ)
                                } else {
-                                       check.softErrorf(e, _Todo, "interface contains type constraints")
+                                       check.softErrorf(e, MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface contains type constraints", typ)
                                }
                        }
                }
-       })
-
-       return typ
+       }).describef(e, "check var type %s", typ)
 }
 
 // definedType is like typ but also accepts a type name def.
-// If def != nil, e is the type specification for the defined type def, declared
-// in a type declaration, and def.underlying will be set to the type of e before
-// any components of e are type-checked.
-//
-func (check *Checker) definedType(e ast.Expr, def *Named) Type {
+// If def != nil, e is the type specification for the type named def, declared
+// in a type declaration, and def.typ.underlying will be set to the type of e
+// before any components of e are type-checked.
+func (check *Checker) definedType(e ast.Expr, def *TypeName) Type {
        typ := check.typInternal(e, def)
        assert(isTyped(typ))
        if isGeneric(typ) {
-               check.errorf(e, _Todo, "cannot use generic type %s without instantiation", typ)
+               check.errorf(e, WrongTypeArgCount, "cannot use generic type %s without instantiation", typ)
                typ = Typ[Invalid]
        }
        check.recordTypeAndValue(e, typexpr, typ, nil)
        return typ
 }
 
-// genericType is like typ but the type must be an (uninstantiated) generic type.
-func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
+// genericType is like typ but the type must be an (uninstantiated) generic
+// type. If cause is non-nil and the type expression was a valid type but not
+// generic, cause will be populated with a message describing the error.
+func (check *Checker) genericType(e ast.Expr, cause *string) Type {
        typ := check.typInternal(e, nil)
        assert(isTyped(typ))
-       if typ != Typ[Invalid] && !isGeneric(typ) {
-               if reportErr {
-                       check.errorf(e, _Todo, "%s is not a generic type", typ)
+       if isValid(typ) && !isGeneric(typ) {
+               if cause != nil {
+                       *cause = check.sprintf("%s is not a generic type", typ)
                }
                typ = Typ[Invalid]
        }
@@ -199,10 +212,9 @@ func goTypeName(typ Type) string {
 
 // typInternal drives type checking of types.
 // Must only be called by definedType or genericType.
-//
-func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
-       if trace {
-               check.trace(e0.Pos(), "type %s", e0)
+func (check *Checker) typInternal(e0 ast.Expr, def *TypeName) (T Type) {
+       if check.conf._Trace {
+               check.trace(e0.Pos(), "-- type %s", e0)
                check.indent++
                defer func() {
                        check.indent--
@@ -231,39 +243,37 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
                switch x.mode {
                case typexpr:
                        typ := x.typ
-                       def.setUnderlying(typ)
+                       setDefType(def, typ)
                        return typ
                case invalid:
                        // ignore - error reported before
                case novalue:
-                       check.errorf(&x, _NotAType, "%s used as type", &x)
+                       check.errorf(&x, NotAType, "%s used as type", &x)
                default:
-                       check.errorf(&x, _NotAType, "%s is not a type", &x)
+                       check.errorf(&x, NotAType, "%s is not a type", &x)
                }
 
        case *ast.SelectorExpr:
                var x operand
-               check.selector(&x, e)
+               check.selector(&x, e, def, true)
 
                switch x.mode {
                case typexpr:
                        typ := x.typ
-                       def.setUnderlying(typ)
+                       setDefType(def, typ)
                        return typ
                case invalid:
                        // ignore - error reported before
                case novalue:
-                       check.errorf(&x, _NotAType, "%s used as type", &x)
+                       check.errorf(&x, NotAType, "%s used as type", &x)
                default:
-                       check.errorf(&x, _NotAType, "%s is not a type", &x)
+                       check.errorf(&x, NotAType, "%s is not a type", &x)
                }
 
        case *ast.IndexExpr, *ast.IndexListExpr:
                ix := typeparams.UnpackIndexExpr(e)
-               if !check.allowVersion(check.pkg, 1, 18) {
-                       check.softErrorf(inNode(e, ix.Lbrack), _Todo, "type instantiation requires go1.18 or later")
-               }
-               return check.instantiatedType(ix.X, ix.Indices, def)
+               check.verifyVersionf(inNode(e, ix.Lbrack), go1_18, "type instantiation")
+               return check.instantiatedType(ix, def)
 
        case *ast.ParenExpr:
                // Generic types must be instantiated before they can be used in any form.
@@ -271,55 +281,63 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
                return check.definedType(e.X, def)
 
        case *ast.ArrayType:
-               if e.Len != nil {
-                       typ := new(Array)
-                       def.setUnderlying(typ)
-                       typ.len = check.arrayLength(e.Len)
+               if e.Len == nil {
+                       typ := new(Slice)
+                       setDefType(def, typ)
                        typ.elem = check.varType(e.Elt)
                        return typ
                }
 
-               typ := new(Slice)
-               def.setUnderlying(typ)
+               typ := new(Array)
+               setDefType(def, typ)
+               // Provide a more specific error when encountering a [...] array
+               // rather than leaving it to the handling of the ... expression.
+               if _, ok := e.Len.(*ast.Ellipsis); ok {
+                       check.error(e.Len, BadDotDotDotSyntax, "invalid use of [...] array (outside a composite literal)")
+                       typ.len = -1
+               } else {
+                       typ.len = check.arrayLength(e.Len)
+               }
                typ.elem = check.varType(e.Elt)
-               return typ
+               if typ.len >= 0 {
+                       return typ
+               }
+               // report error if we encountered [...]
 
        case *ast.Ellipsis:
                // dots are handled explicitly where they are legal
                // (array composite literals and parameter lists)
-               check.error(e, _InvalidDotDotDot, "invalid use of '...'")
+               check.error(e, InvalidDotDotDot, "invalid use of '...'")
                check.use(e.Elt)
 
        case *ast.StructType:
                typ := new(Struct)
-               def.setUnderlying(typ)
+               setDefType(def, typ)
                check.structType(typ, e)
                return typ
 
        case *ast.StarExpr:
                typ := new(Pointer)
-               def.setUnderlying(typ)
+               typ.base = Typ[Invalid] // avoid nil base in invalid recursive type declaration
+               setDefType(def, typ)
                typ.base = check.varType(e.X)
                return typ
 
        case *ast.FuncType:
                typ := new(Signature)
-               def.setUnderlying(typ)
+               setDefType(def, typ)
                check.funcType(typ, nil, e)
                return typ
 
        case *ast.InterfaceType:
-               typ := new(Interface)
-               def.setUnderlying(typ)
-               if def != nil {
-                       typ.obj = def.obj
-               }
+               typ := check.newInterface()
+               setDefType(def, typ)
                check.interfaceType(typ, e, def)
                return typ
 
        case *ast.MapType:
                typ := new(Map)
-               def.setUnderlying(typ)
+               setDefType(def, typ)
 
                typ.key = check.varType(e.Key)
                typ.elem = check.varType(e.Value)
@@ -329,22 +347,22 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
                // function, map, or slice."
                //
                // Delay this check because it requires fully setup types;
-               // it is safe to continue in any case (was issue 6667).
+               // it is safe to continue in any case (was go.dev/issue/6667).
                check.later(func() {
                        if !Comparable(typ.key) {
                                var why string
-                               if asTypeParam(typ.key) != nil {
+                               if isTypeParam(typ.key) {
                                        why = " (missing comparable constraint)"
                                }
-                               check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why)
+                               check.errorf(e.Key, IncomparableMapKey, "invalid map key type %s%s", typ.key, why)
                        }
-               })
+               }).describef(e.Key, "check map key %s", typ.key)
 
                return typ
 
        case *ast.ChanType:
                typ := new(Chan)
-               def.setUnderlying(typ)
+               setDefType(def, typ)
 
                dir := SendRecv
                switch e.Dir {
@@ -355,7 +373,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
                case ast.RECV:
                        dir = RecvOnly
                default:
-                       check.invalidAST(e, "unknown channel direction %d", e.Dir)
+                       check.errorf(e, InvalidSyntaxTree, "unknown channel direction %d", e.Dir)
                        // ok to continue
                }
 
@@ -364,106 +382,96 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
                return typ
 
        default:
-               check.errorf(e0, _NotAType, "%s is not a type", e0)
+               check.errorf(e0, NotAType, "%s is not a type", e0)
+               check.use(e0)
        }
 
        typ := Typ[Invalid]
-       def.setUnderlying(typ)
+       setDefType(def, typ)
        return typ
 }
 
-func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named) (res Type) {
-       if trace {
-               check.trace(x.Pos(), "-- instantiating %s with %s", x, targsx)
+func setDefType(def *TypeName, typ Type) {
+       if def != nil {
+               switch t := def.typ.(type) {
+               case *Alias:
+                       // t.fromRHS should always be set, either to an invalid type
+                       // in the beginning, or to typ in certain cyclic declarations.
+                       if t.fromRHS != Typ[Invalid] && t.fromRHS != typ {
+                               panic(sprintf(nil, nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ))
+                       }
+                       t.fromRHS = typ
+               case *Basic:
+                       assert(t == Typ[Invalid])
+               case *Named:
+                       t.underlying = typ
+               default:
+                       panic(fmt.Sprintf("unexpected type %T", t))
+               }
+       }
+}
+
+func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName) (res Type) {
+       if check.conf._Trace {
+               check.trace(ix.Pos(), "-- instantiating type %s with %s", ix.X, ix.Indices)
                check.indent++
                defer func() {
                        check.indent--
                        // Don't format the underlying here. It will always be nil.
-                       check.trace(x.Pos(), "=> %s", res)
+                       check.trace(ix.Pos(), "=> %s", res)
                }()
        }
 
-       gtyp := check.genericType(x, true)
-       if gtyp == Typ[Invalid] {
+       var cause string
+       gtyp := check.genericType(ix.X, &cause)
+       if cause != "" {
+               check.errorf(ix.Orig, NotAGenericType, invalidOp+"%s (%s)", ix.Orig, cause)
+       }
+       if !isValid(gtyp) {
                return gtyp // error already reported
        }
 
-       origin, _ := gtyp.(*Named)
-       if origin == nil {
-               panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp))
+       orig := asNamed(gtyp)
+       if orig == nil {
+               panic(fmt.Sprintf("%v: cannot instantiate %v", ix.Pos(), gtyp))
        }
 
        // evaluate arguments
-       targs := check.typeList(targsx)
+       targs := check.typeList(ix.Indices)
        if targs == nil {
-               def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
+               setDefType(def, Typ[Invalid]) // avoid errors later due to lazy instantiation
                return Typ[Invalid]
        }
 
-       // determine argument positions
-       posList := make([]token.Pos, len(targs))
-       for i, arg := range targsx {
-               posList[i] = arg.Pos()
-       }
-
        // create the instance
-       h := check.conf.Context.typeHash(origin, targs)
-       // targs may be incomplete, and require inference. In any case we should de-duplicate.
-       inst := check.conf.Context.typeForHash(h, nil)
-       // If inst is non-nil, we can't just return here. Inst may have been
-       // constructed via recursive substitution, in which case we wouldn't do the
-       // validation below. Ensure that the validation (and resulting errors) runs
-       // for each instantiated type in the source.
-       if inst == nil {
-               tname := NewTypeName(x.Pos(), origin.obj.pkg, origin.obj.name, nil)
-               inst = check.newNamed(tname, origin, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
-               inst.targs = NewTypeList(targs)
-               inst = check.conf.Context.typeForHash(h, inst)
-       }
-       def.setUnderlying(inst)
-
-       inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
-               tparams := origin.TypeParams().list()
-
-               inferred := targs
-               if len(targs) < len(tparams) {
-                       // If inference fails, len(inferred) will be 0, and inst.underlying will
-                       // be set to Typ[Invalid] in expandNamed.
-                       inferred = check.infer(x, tparams, targs, nil, nil)
-                       if len(inferred) > len(targs) {
-                               inst.targs = NewTypeList(inferred)
-                       }
-               }
-
-               check.recordInstance(x, inferred, inst)
-               return expandNamed(ctxt, n, x.Pos())
-       }
+       inst := asNamed(check.instance(ix.Pos(), orig, targs, nil, check.context()))
+       setDefType(def, inst)
 
-       // origin.tparams may not be set up, so we need to do expansion later.
+       // orig.tparams may not be set up, so we need to do expansion later.
        check.later(func() {
                // This is an instance from the source, not from recursive substitution,
                // and so it must be resolved during type-checking so that we can report
                // errors.
-               inst.resolve(check.conf.Context)
-               // Since check is non-nil, we can still mutate inst. Unpinning the resolver
-               // frees some memory.
-               inst.resolver = nil
+               check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
 
-               if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) {
-                       if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil {
+               if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) {
+                       if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil {
                                // best position for error reporting
-                               pos := x.Pos()
-                               if i < len(posList) {
-                                       pos = posList[i]
+                               pos := ix.Pos()
+                               if i < len(ix.Indices) {
+                                       pos = ix.Indices[i].Pos()
                                }
-                               check.softErrorf(atPos(pos), _Todo, err.Error())
+                               check.softErrorf(atPos(pos), InvalidTypeArg, err.Error())
                        } else {
-                               check.mono.recordInstance(check.pkg, x.Pos(), inst.tparams.list(), inst.targs.list(), posList)
+                               check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), ix.Indices)
                        }
                }
 
-               check.validType(inst, nil)
-       })
+               // TODO(rfindley): remove this call: we don't need to call validType here,
+               // as cycles can only occur for types used inside a Named type declaration,
+               // and so it suffices to call validType from declared types.
+               check.validType(inst)
+       }).describef(ix, "resolve instance %s", inst)
 
        return inst
 }
@@ -472,19 +480,27 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named
 // and returns the constant length >= 0, or a value < 0
 // to indicate an error (and thus an unknown length).
 func (check *Checker) arrayLength(e ast.Expr) int64 {
-       // If e is an undeclared identifier, the array declaration might be an
-       // attempt at a parameterized type declaration with missing constraint.
-       // Provide a better error message than just "undeclared name: X".
-       if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil {
-               check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name)
-               return -1
+       // If e is an identifier, the array declaration might be an
+       // attempt at a parameterized type declaration with missing
+       // constraint. Provide an error message that mentions array
+       // length.
+       if name, _ := e.(*ast.Ident); name != nil {
+               obj := check.lookup(name.Name)
+               if obj == nil {
+                       check.errorf(name, InvalidArrayLen, "undefined array length %s or missing type constraint", name.Name)
+                       return -1
+               }
+               if _, ok := obj.(*Const); !ok {
+                       check.errorf(name, InvalidArrayLen, "invalid array length %s", name.Name)
+                       return -1
+               }
        }
 
        var x operand
-       check.expr(&x, e)
+       check.expr(nil, &x, e)
        if x.mode != constant_ {
                if x.mode != invalid {
-                       check.errorf(&x, _InvalidArrayLen, "array length %s must be constant", &x)
+                       check.errorf(&x, InvalidArrayLen, "array length %s must be constant", &x)
                }
                return -1
        }
@@ -495,13 +511,17 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
                                if n, ok := constant.Int64Val(val); ok && n >= 0 {
                                        return n
                                }
-                               check.errorf(&x, _InvalidArrayLen, "invalid array length %s", &x)
-                               return -1
                        }
                }
        }
 
-       check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x)
+       var msg string
+       if isInteger(x.typ) {
+               msg = "invalid array length %s"
+       } else {
+               msg = "array length %s must be integer"
+       }
+       check.errorf(&x, InvalidArrayLen, msg, &x)
        return -1
 }
 
@@ -511,7 +531,7 @@ func (check *Checker) typeList(list []ast.Expr) []Type {
        res := make([]Type, len(list)) // res != nil even if len(list) == 0
        for i, x := range list {
                t := check.varType(x)
-               if t == Typ[Invalid] {
+               if !isValid(t) {
                        res = nil
                }
                if res != nil {