package types
import (
"fmt"
"go/ast"
"go/constant"
"go/internal/typeparams"
"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 , "undeclared name: %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 )
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 .errorf (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 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 , "interface is (or embeds) comparable" )
} else {
check .softErrorf (e , _MisplacedConstraintIface , "interface contains type constraints" )
}
}
}
})
}
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 , reason *string ) Type {
typ := check .typInternal (e , nil )
assert (isTyped (typ ))
if typ != Typ [Invalid ] && !isGeneric (typ ) {
if reason != nil {
*reason = 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 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 )
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 )
if !check .allowVersion (check .pkg , 1 , 18 ) {
check .softErrorf (inNode (e , ix .Lbrack ), _UnsupportedFeature , "type instantiation requires go1.18 or later" )
}
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 )
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 )
if def != nil {
typ .obj = def .obj
}
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 , "incomparable map key type %s%s" , typ .key , why )
}
})
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 .invalidAST (e , "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 )
}
typ := Typ [Invalid ]
def .setUnderlying (typ )
return typ
}
func (check *Checker ) instantiatedType (ix *typeparams .IndexExpr , def *Named ) (res Type ) {
pos := ix .X .Pos ()
if trace {
check .trace (pos , "-- instantiating %s with %s" , ix .X , ix .Indices )
check .indent ++
defer func () {
check .indent --
check .trace (pos , "=> %s" , res )
}()
}
var reason string
gtyp := check .genericType (ix .X , &reason )
if reason != "" {
check .invalidOp (ix .Orig , _NotAGenericType , "%s (%s)" , ix .Orig , reason )
}
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 ]
}
const enableTypeTypeInference = false
ctxt := check .bestContext (nil )
h := ctxt .instanceHash (orig , targs )
inst , _ := ctxt .lookup (h , orig , targs ).(*Named )
if inst == nil {
tname := NewTypeName (ix .Pos (), orig .obj .pkg , orig .obj .name , nil )
inst = check .newNamed (tname , orig , nil , nil , nil )
inst .targs = newTypeList (targs )
inst = ctxt .update (h , orig , targs , inst ).(*Named )
}
def .setUnderlying (inst )
inst .resolver = func (ctxt *Context , n *Named ) (*TypeParamList , Type , *methodList ) {
tparams := n .orig .TypeParams ().list ()
targs := n .targs .list ()
if enableTypeTypeInference && len (targs ) < len (tparams ) {
inferred := check .infer (ix .Orig , tparams , targs , nil , nil )
if len (inferred ) > len (targs ) {
n .targs = newTypeList (inferred )
}
}
return expandNamed (ctxt , n , pos )
}
check .later (func () {
inst .resolve (ctxt )
inst .resolver = nil
check .recordInstance (ix .Orig , inst .TypeArgs ().list (), inst )
if check .validateTArgLen (pos , inst .tparams .Len (), inst .targs .Len ()) {
if i , err := check .verify (pos , inst .tparams .list (), inst .targs .list ()); 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 , pos , inst .tparams .list (), inst .targs .list (), ix .Indices )
}
}
check .validType (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 , "undeclared name %s for array length" , 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 )
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
}
check .errorf (&x , _InvalidArrayLen , "invalid array length %s" , &x )
return -1
}
}
}
check .errorf (&x , _InvalidArrayLen , "array length %s must be integer" , &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.4.5 . (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 .