// Copyright 2013 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// This file implements type-checking of identifiers and type expressions.package typesimport (.)// ident type-checks identifier e and initializes x with the value or type of e.// 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 ( *Checker) ( *operand, *ast.Ident, *TypeName, bool) { .mode = invalid .expr = // Note that we cannot use check.lookup here because the returned scope // may be different from obj.Parent(). See also Scope.LookupParent doc. , := .scope.LookupParent(.Name, .pos)switch {casenil:if .Name == "_" {// Blank identifiers are never declared, but the current identifier may // be a placeholder for a receiver type parameter. In this case we can // resolve its type and object from Checker.recvTParamMap.if := .recvTParamMap[]; != nil { .mode = typexpr .typ = } else { .error(, InvalidBlank, "cannot use _ as value or type") } } else { .errorf(, UndeclaredName, "undefined: %s", .Name) }returncaseuniverseComparable:if !.verifyVersionf(, go1_18, "predeclared %s", .Name) {return// avoid follow-on errors } }// Because the representation of any depends on gotypesalias, we don't check // pointer identity here.if .Name() == "any" && .Parent() == Universe {if !.verifyVersionf(, go1_18, "predeclared %s", .Name) {return// avoid follow-on errors } } .recordUse(, )// If we want a type but don't have one, stop right here and avoid potential problems // with missing underlying types. This also gives better error messages in some cases // (see go.dev/issue/65344). , := .(*TypeName)if ! && { .errorf(, NotAType, "%s is not a type", .Name())// avoid "declared but not used" errors // (don't use Checker.use - we don't want to evaluate too much)if , := .(*Var); != nil && .pkg == .pkg/* see Checker.use1 */ { .used = true }return }// Type-check the object. // Only call Checker.objDecl if the object doesn't have a type yet // (in which case we must actually determine it) or the object is a // TypeName and we also want a type (in which case we might detect // 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 go.dev/issue/25790). := .Type()if == nil || && { .objDecl(, ) = .Type() // type must have been assigned by Checker.objDecl }assert( != nil)// The object may have been dot-imported. // If so, mark the respective package as used. // (This code is only needed for dot-imports. Without them, // we only have to mark variables, see *Var case below).if := .dotImportMap[dotImportKey{, .Name()}]; != nil { .used = true }switch obj := .(type) {case *PkgName: .errorf(, InvalidPkgUse, "use of package %s not in selector", .name)returncase *Const: .addDeclDep()if !isValid() {return }if == universeIota {if .iota == nil { .error(, InvalidIota, "cannot use iota outside constant declaration")return } .val = .iota } else { .val = .val }assert(.val != nil) .mode = constant_case *TypeName:if !.conf._EnableAlias && .isBrokenAlias() { .errorf(, InvalidDeclCycle, "invalid use of type alias %s in recursive type (see go.dev/issue/50729)", .name)return } .mode = typexprcase *Var:// It's ok to mark non-local variables, but ignore variables // from other packages to avoid potential race conditions with // dot-imported variables.if .pkg == .pkg { .used = true } .addDeclDep()if !isValid() {return } .mode = variablecase *Func: .addDeclDep() .mode = valuecase *Builtin: .id = .id .mode = builtincase *Nil: .mode = valuedefault:panic("unreachable") } .typ = }// typ type-checks the type expression e and returns its type, or Typ[Invalid].// The type must not be an (uninstantiated) generic type.func ( *Checker) ( ast.Expr) Type {return .definedType(, nil)}// varType type-checks the type expression e and returns its type, or Typ[Invalid].// The type must not be an (uninstantiated) generic type and it must not be a// constraint interface.func ( *Checker) ( ast.Expr) Type { := .definedType(, nil) .validVarType(, )return}// validVarType reports an error if typ is a constraint interface.// The expression e is used for error reporting, if any.func ( *Checker) ( ast.Expr, Type) {// If we have a type parameter there's nothing to do.ifisTypeParam() {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. .later(func() {if , := under().(*Interface); != nil { := computeInterfaceTypeSet(, .Pos(), ) // TODO(gri) is this the correct position?if !.IsMethodSet() {if .comparable { .softErrorf(, MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface is (or embeds) comparable", ) } else { .softErrorf(, MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface contains type constraints", ) } } } }).describef(, "check var type %s", )}// definedType is like typ but also accepts a type name def.// 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 ( *Checker) ( ast.Expr, *TypeName) Type { := .typInternal(, )assert(isTyped())ifisGeneric() { .errorf(, WrongTypeArgCount, "cannot use generic type %s without instantiation", ) = Typ[Invalid] } .recordTypeAndValue(, typexpr, , nil)return}// 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 ( *Checker) ( ast.Expr, *string) Type { := .typInternal(, nil)assert(isTyped())ifisValid() && !isGeneric() {if != nil { * = .sprintf("%s is not a generic type", ) } = Typ[Invalid] }// TODO(gri) what is the correct call below? .recordTypeAndValue(, typexpr, , nil)return}// goTypeName returns the Go type name for typ and// removes any occurrences of "types." from that name.func goTypeName( Type) string {returnstrings.ReplaceAll(fmt.Sprintf("%T", ), "types.", "")}// typInternal drives type checking of types.// Must only be called by definedType or genericType.func ( *Checker) ( ast.Expr, *TypeName) ( Type) {if .conf._Trace { .trace(.Pos(), "-- type %s", ) .indent++deferfunc() { .indent--varTypeif != nil {// Calling under() here may lead to endless instantiations. // Test case: type T[P any] *T[P] = safeUnderlying() }if == { .trace(.Pos(), "=> %s // %s", , goTypeName()) } else { .trace(.Pos(), "=> %s (under = %s) // %s", , , goTypeName()) } }() }switch e := .(type) {case *ast.BadExpr:// ignore - error reported beforecase *ast.Ident:varoperand .ident(&, , , true)switch .mode {casetypexpr: := .typsetDefType(, )returncaseinvalid:// ignore - error reported beforecasenovalue: .errorf(&, NotAType, "%s used as type", &)default: .errorf(&, NotAType, "%s is not a type", &) }case *ast.SelectorExpr:varoperand .selector(&, , , true)switch .mode {casetypexpr: := .typsetDefType(, )returncaseinvalid:// ignore - error reported beforecasenovalue: .errorf(&, NotAType, "%s used as type", &)default: .errorf(&, NotAType, "%s is not a type", &) }case *ast.IndexExpr, *ast.IndexListExpr: := typeparams.UnpackIndexExpr() .verifyVersionf(inNode(, .Lbrack), go1_18, "type instantiation")return .instantiatedType(, )case *ast.ParenExpr:// Generic types must be instantiated before they can be used in any form. // Consequently, generic types cannot be parenthesized.return .definedType(.X, )case *ast.ArrayType:if .Len == nil { := new(Slice)setDefType(, ) .elem = .varType(.Elt)return } := new(Array)setDefType(, )// Provide a more specific error when encountering a [...] array // rather than leaving it to the handling of the ... expression.if , := .Len.(*ast.Ellipsis); { .error(.Len, BadDotDotDotSyntax, "invalid use of [...] array (outside a composite literal)") .len = -1 } else { .len = .arrayLength(.Len) } .elem = .varType(.Elt)if .len >= 0 {return }// report error if we encountered [...]case *ast.Ellipsis:// dots are handled explicitly where they are legal // (array composite literals and parameter lists) .error(, InvalidDotDotDot, "invalid use of '...'") .use(.Elt)case *ast.StructType: := new(Struct)setDefType(, ) .structType(, )returncase *ast.StarExpr: := new(Pointer) .base = Typ[Invalid] // avoid nil base in invalid recursive type declarationsetDefType(, ) .base = .varType(.X)returncase *ast.FuncType: := new(Signature)setDefType(, ) .funcType(, nil, )returncase *ast.InterfaceType: := .newInterface()setDefType(, ) .interfaceType(, , )returncase *ast.MapType: := new(Map)setDefType(, ) .key = .varType(.Key) .elem = .varType(.Value)// spec: "The comparison operators == and != must be fully defined // for operands of the key type; thus the key type must not be a // function, map, or slice." // // Delay this check because it requires fully setup types; // it is safe to continue in any case (was go.dev/issue/6667). .later(func() {if !Comparable(.key) {varstringifisTypeParam(.key) { = " (missing comparable constraint)" } .errorf(.Key, IncomparableMapKey, "invalid map key type %s%s", .key, ) } }).describef(.Key, "check map key %s", .key)returncase *ast.ChanType: := new(Chan)setDefType(, ) := SendRecvswitch .Dir {caseast.SEND | ast.RECV:// nothing to docaseast.SEND: = SendOnlycaseast.RECV: = RecvOnlydefault: .errorf(, InvalidSyntaxTree, "unknown channel direction %d", .Dir)// ok to continue } .dir = .elem = .varType(.Value)returndefault: .errorf(, NotAType, "%s is not a type", ) .use() } := Typ[Invalid]setDefType(, )return}func setDefType( *TypeName, Type) {if != nil {switch t := .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 .fromRHS != Typ[Invalid] && .fromRHS != {panic(sprintf(nil, nil, true, "t.fromRHS = %s, typ = %s\n", .fromRHS, )) } .fromRHS = case *Basic:assert( == Typ[Invalid])case *Named: .underlying = default:panic(fmt.Sprintf("unexpected type %T", )) } }}func ( *Checker) ( *typeparams.IndexExpr, *TypeName) ( Type) {if .conf._Trace { .trace(.Pos(), "-- instantiating type %s with %s", .X, .Indices) .indent++deferfunc() { .indent--// Don't format the underlying here. It will always be nil. .trace(.Pos(), "=> %s", ) }() }deferfunc() {setDefType(, ) }()varstring := .genericType(.X, &)if != "" { .errorf(.Orig, NotAGenericType, invalidOp+"%s (%s)", .Orig, ) }if !isValid() {return// error already reported }// evaluate arguments := .typeList(.Indices)if == nil {returnTyp[Invalid] }if , := .(*Alias); != nil {return .instance(.Pos(), , , nil, .context()) } := asNamed()if == nil {panic(fmt.Sprintf("%v: cannot instantiate %v", .Pos(), )) }// create the instance := asNamed(.instance(.Pos(), , , nil, .context()))// orig.tparams may not be set up, so we need to do expansion later. .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. .recordInstance(.Orig, .TypeArgs().list(), )if .validateTArgLen(.Pos(), .obj.name, .TypeParams().Len(), .TypeArgs().Len()) {if , := .verify(.Pos(), .TypeParams().list(), .TypeArgs().list(), .context()); != nil {// best position for error reporting := .Pos()if < len(.Indices) { = .Indices[].Pos() } .softErrorf(atPos(), InvalidTypeArg, "%v", ) } else { .mono.recordInstance(.pkg, .Pos(), .TypeParams().list(), .TypeArgs().list(), .Indices) } }// 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. .validType() }).describef(, "resolve instance %s", )return}// arrayLength type-checks the array length expression e// and returns the constant length >= 0, or a value < 0// to indicate an error (and thus an unknown length).func ( *Checker) ( ast.Expr) int64 {// 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 , := .(*ast.Ident); != nil { := .lookup(.Name)if == nil { .errorf(, InvalidArrayLen, "undefined array length %s or missing type constraint", .Name)return -1 }if , := .(*Const); ! { .errorf(, InvalidArrayLen, "invalid array length %s", .Name)return -1 } }varoperand .expr(nil, &, )if .mode != constant_ {if .mode != invalid { .errorf(&, InvalidArrayLen, "array length %s must be constant", &) }return -1 }ifisUntyped(.typ) || isInteger(.typ) {if := constant.ToInt(.val); .Kind() == constant.Int {ifrepresentableConst(, , Typ[Int], nil) {if , := constant.Int64Val(); && >= 0 {return } } } }varstringifisInteger(.typ) { = "invalid array length %s" } else { = "array length %s must be integer" } .errorf(&, InvalidArrayLen, , &)return -1}// typeList provides the list of types corresponding to the incoming expression list.// If an error occurred, the result is nil, but all list elements were type-checked.func ( *Checker) ( []ast.Expr) []Type { := make([]Type, len()) // res != nil even if len(list) == 0for , := range { := .varType()if !isValid() { = nil }if != nil { [] = } }return}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.