package types
import (
"fmt"
"go/ast"
"go/constant"
"go/internal/typeparams"
. "internal/types/errors"
"strings"
)
func (check *Checker ) ident (x *operand , e *ast .Ident , def *Named , wantType bool ) {
x .mode = invalid
x .expr = e
scope , obj := check .scope .LookupParent (e .Name , check .pos )
switch obj {
case nil :
if e .Name == "_" {
if tpar := check .recvTParamMap [e ]; tpar != nil {
x .mode = typexpr
x .typ = tpar
} else {
check .error (e , InvalidBlank , "cannot use _ as value or type" )
}
} else {
check .errorf (e , UndeclaredName , "undefined: %s" , e .Name )
}
return
case universeAny , universeComparable :
if !check .verifyVersionf (e , go1_18 , "predeclared %s" , e .Name ) {
return
}
}
check .recordUse (e , obj )
typ := obj .Type ()
if _ , gotType := obj .(*TypeName ); typ == nil || gotType && wantType {
check .objDecl (obj , def )
typ = obj .Type ()
}
assert (typ != nil )
if pkgName := check .dotImportMap [dotImportKey {scope , obj .Name ()}]; pkgName != nil {
pkgName .used = true
}
switch obj := obj .(type ) {
case *PkgName :
check .errorf (e , InvalidPkgUse , "use of package %s not in selector" , obj .name )
return
case *Const :
check .addDeclDep (obj )
if typ == Typ [Invalid ] {
return
}
if obj == universeIota {
if check .iota == nil {
check .error (e , InvalidIota , "cannot use iota outside constant declaration" )
return
}
x .val = check .iota
} else {
x .val = obj .val
}
assert (x .val != nil )
x .mode = constant_
case *TypeName :
if 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 :
if obj .pkg == check .pkg {
obj .used = true
}
check .addDeclDep (obj )
if typ == Typ [Invalid ] {
return
}
x .mode = variable
case *Func :
check .addDeclDep (obj )
x .mode = value
case *Builtin :
x .id = obj .id
x .mode = builtin
case *Nil :
x .mode = value
default :
unreachable ()
}
x .typ = typ
}
func (check *Checker ) typ (e ast .Expr ) Type {
return check .definedType (e , nil )
}
func (check *Checker ) varType (e ast .Expr ) Type {
typ := check .definedType (e , nil )
check .validVarType (e , typ )
return typ
}
func (check *Checker ) validVarType (e ast .Expr , typ Type ) {
if isTypeParam (typ ) {
return
}
check .later (func () {
if t , _ := under (typ ).(*Interface ); t != nil {
tset := computeInterfaceTypeSet (check , e .Pos (), t )
if !tset .IsMethodSet () {
if tset .comparable {
check .softErrorf (e , MisplacedConstraintIface , "cannot use type %s outside a type constraint: interface is (or embeds) comparable" , typ )
} else {
check .softErrorf (e , MisplacedConstraintIface , "cannot use type %s outside a type constraint: interface contains type constraints" , typ )
}
}
}
}).describef (e , "check var type %s" , typ )
}
func (check *Checker ) definedType (e ast .Expr , def *Named ) Type {
typ := check .typInternal (e , def )
assert (isTyped (typ ))
if isGeneric (typ ) {
check .errorf (e , WrongTypeArgCount , "cannot use generic type %s without instantiation" , typ )
typ = Typ [Invalid ]
}
check .recordTypeAndValue (e , typexpr , typ , nil )
return typ
}
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 cause != nil {
*cause = check .sprintf ("%s is not a generic type" , typ )
}
typ = Typ [Invalid ]
}
check .recordTypeAndValue (e , typexpr , typ , nil )
return typ
}
func goTypeName(typ Type ) string {
return strings .ReplaceAll (fmt .Sprintf ("%T" , typ ), "types." , "" )
}
func (check *Checker ) typInternal (e0 ast .Expr , def *Named ) (T Type ) {
if check .conf ._Trace {
check .trace (e0 .Pos (), "-- type %s" , e0 )
check .indent ++
defer func () {
check .indent --
var under Type
if T != nil {
under = safeUnderlying (T )
}
if T == under {
check .trace (e0 .Pos (), "=> %s // %s" , T , goTypeName (T ))
} else {
check .trace (e0 .Pos (), "=> %s (under = %s) // %s" , T , under , goTypeName (T ))
}
}()
}
switch e := e0 .(type ) {
case *ast .BadExpr :
case *ast .Ident :
var x operand
check .ident (&x , e , def , true )
switch x .mode {
case typexpr :
typ := x .typ
def .setUnderlying (typ )
return typ
case invalid :
case novalue :
check .errorf (&x , NotAType , "%s used as type" , &x )
default :
check .errorf (&x , NotAType , "%s is not a type" , &x )
}
case *ast .SelectorExpr :
var x operand
check .selector (&x , e , def , true )
switch x .mode {
case typexpr :
typ := x .typ
def .setUnderlying (typ )
return typ
case invalid :
case novalue :
check .errorf (&x , NotAType , "%s used as type" , &x )
default :
check .errorf (&x , NotAType , "%s is not a type" , &x )
}
case *ast .IndexExpr , *ast .IndexListExpr :
ix := typeparams .UnpackIndexExpr (e )
check .verifyVersionf (inNode (e , ix .Lbrack ), go1_18 , "type instantiation" )
return check .instantiatedType (ix , def )
case *ast .ParenExpr :
return check .definedType (e .X , def )
case *ast .ArrayType :
if e .Len == nil {
typ := new (Slice )
def .setUnderlying (typ )
typ .elem = check .varType (e .Elt )
return typ
}
typ := new (Array )
def .setUnderlying (typ )
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 )
if typ .len >= 0 {
return typ
}
case *ast .Ellipsis :
check .error (e , InvalidDotDotDot , "invalid use of '...'" )
check .use (e .Elt )
case *ast .StructType :
typ := new (Struct )
def .setUnderlying (typ )
check .structType (typ , e )
return typ
case *ast .StarExpr :
typ := new (Pointer )
typ .base = Typ [Invalid ]
def .setUnderlying (typ )
typ .base = check .varType (e .X )
return typ
case *ast .FuncType :
typ := new (Signature )
def .setUnderlying (typ )
check .funcType (typ , nil , e )
return typ
case *ast .InterfaceType :
typ := check .newInterface ()
def .setUnderlying (typ )
check .interfaceType (typ , e , def )
return typ
case *ast .MapType :
typ := new (Map )
def .setUnderlying (typ )
typ .key = check .varType (e .Key )
typ .elem = check .varType (e .Value )
check .later (func () {
if !Comparable (typ .key ) {
var why string
if isTypeParam (typ .key ) {
why = " (missing comparable constraint)"
}
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 )
dir := SendRecv
switch e .Dir {
case ast .SEND | ast .RECV :
case ast .SEND :
dir = SendOnly
case ast .RECV :
dir = RecvOnly
default :
check .errorf (e , InvalidSyntaxTree , "unknown channel direction %d" , e .Dir )
}
typ .dir = dir
typ .elem = check .varType (e .Value )
return typ
default :
check .errorf (e0 , NotAType , "%s is not a type" , e0 )
check .use (e0 )
}
typ := Typ [Invalid ]
def .setUnderlying (typ )
return typ
}
func (check *Checker ) instantiatedType (ix *typeparams .IndexExpr , def *Named ) (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 --
check .trace (ix .Pos (), "=> %s" , res )
}()
}
var cause string
gtyp := check .genericType (ix .X , &cause )
if cause != "" {
check .errorf (ix .Orig , NotAGenericType , invalidOp +"%s (%s)" , ix .Orig , cause )
}
if gtyp == Typ [Invalid ] {
return gtyp
}
orig , _ := gtyp .(*Named )
if orig == nil {
panic (fmt .Sprintf ("%v: cannot instantiate %v" , ix .Pos (), gtyp ))
}
targs := check .typeList (ix .Indices )
if targs == nil {
def .setUnderlying (Typ [Invalid ])
return Typ [Invalid ]
}
inst := check .instance (ix .Pos (), orig , targs , nil , check .context ()).(*Named )
def .setUnderlying (inst )
check .later (func () {
check .recordInstance (ix .Orig , inst .TypeArgs ().list (), inst )
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 {
pos := ix .Pos ()
if i < len (ix .Indices ) {
pos = ix .Indices [i ].Pos ()
}
check .softErrorf (atPos (pos ), InvalidTypeArg , err .Error())
} else {
check .mono .recordInstance (check .pkg , ix .Pos (), inst .TypeParams ().list (), inst .TypeArgs ().list (), ix .Indices )
}
}
check .validType (inst )
}).describef (ix , "resolve instance %s" , inst )
return inst
}
func (check *Checker ) arrayLength (e ast .Expr ) int64 {
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 (nil , &x , e )
if x .mode != constant_ {
if x .mode != invalid {
check .errorf (&x , InvalidArrayLen , "array length %s must be constant" , &x )
}
return -1
}
if isUntyped (x .typ ) || isInteger (x .typ ) {
if val := constant .ToInt (x .val ); val .Kind () == constant .Int {
if representableConst (val , check , Typ [Int ], nil ) {
if n , ok := constant .Int64Val (val ); ok && n >= 0 {
return n
}
}
}
}
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
}
func (check *Checker ) typeList (list []ast .Expr ) []Type {
res := make ([]Type , len (list ))
for i , x := range list {
t := check .varType (x )
if t == Typ [Invalid ] {
res = nil
}
if res != nil {
res [i ] = t
}
}
return res
}
The pages are generated with Golds v0.6.6 . (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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds .