package types
import (
"fmt"
"go/ast"
"go/constant"
"go/internal/typeparams"
"go/token"
"math"
)
type opPredicates map [token .Token ]func (Type ) bool
var unaryOpPredicates opPredicates
func init() {
unaryOpPredicates = opPredicates {
token .ADD : allNumeric ,
token .SUB : allNumeric ,
token .XOR : allInteger ,
token .NOT : allBoolean ,
}
}
func (check *Checker ) op (m opPredicates , x *operand , op token .Token ) bool {
if pred := m [op ]; pred != nil {
if !pred (x .typ ) {
check .invalidOp (x , _UndefinedOp , "operator %s not defined on %s" , op , x )
return false
}
} else {
check .invalidAST (x , "unknown operator %s" , op )
return false
}
return true
}
func (check *Checker ) overflow (x *operand , op token .Token , opPos token .Pos ) {
assert (x .mode == constant_ )
if x .val .Kind () == constant .Unknown {
check .errorf (atPos (opPos ), _InvalidConstVal , "constant result is not representable" )
return
}
if isTyped (x .typ ) {
check .representable (x , under (x .typ ).(*Basic ))
return
}
const prec = 512
if x .val .Kind () == constant .Int && constant .BitLen (x .val ) > prec {
check .errorf (atPos (opPos ), _InvalidConstVal , "constant %s overflow" , opName (x .expr ))
x .val = constant .MakeUnknown ()
}
}
func opName(e ast .Expr ) string {
switch e := e .(type ) {
case *ast .BinaryExpr :
if int (e .Op ) < len (op2str2 ) {
return op2str2 [e .Op ]
}
case *ast .UnaryExpr :
if int (e .Op ) < len (op2str1 ) {
return op2str1 [e .Op ]
}
}
return ""
}
var op2str1 = [...]string {
token .XOR : "bitwise complement" ,
}
var op2str2 = [...]string {
token .ADD : "addition" ,
token .SUB : "subtraction" ,
token .XOR : "bitwise XOR" ,
token .MUL : "multiplication" ,
token .SHL : "shift" ,
}
func underIs(typ Type , f func (Type ) bool ) bool {
if tpar , _ := typ .(*TypeParam ); tpar != nil {
return tpar .underIs (f )
}
return f (under (typ ))
}
func (check *Checker ) unary (x *operand , e *ast .UnaryExpr ) {
check .expr (x , e .X )
if x .mode == invalid {
return
}
switch e .Op {
case token .AND :
if _ , ok := unparen (e .X ).(*ast .CompositeLit ); !ok && x .mode != variable {
check .invalidOp (x , _UnaddressableOperand , "cannot take address of %s" , x )
x .mode = invalid
return
}
x .mode = value
x .typ = &Pointer {base : x .typ }
return
case token .ARROW :
u := coreType (x .typ )
if u == nil {
check .invalidOp (x , _InvalidReceive , "cannot receive from %s: no core type" , x )
x .mode = invalid
return
}
ch , _ := u .(*Chan )
if ch == nil {
check .invalidOp (x , _InvalidReceive , "cannot receive from non-channel %s" , x )
x .mode = invalid
return
}
if ch .dir == SendOnly {
check .invalidOp (x , _InvalidReceive , "cannot receive from send-only channel %s" , x )
x .mode = invalid
return
}
x .mode = commaok
x .typ = ch .elem
check .hasCallOrRecv = true
return
}
if !check .op (unaryOpPredicates , x , e .Op ) {
x .mode = invalid
return
}
if x .mode == constant_ {
if x .val .Kind () == constant .Unknown {
return
}
var prec uint
if isUnsigned (x .typ ) {
prec = uint (check .conf .sizeof (x .typ ) * 8 )
}
x .val = constant .UnaryOp (e .Op , x .val , prec )
x .expr = e
check .overflow (x , e .Op , x .Pos ())
return
}
x .mode = value
}
func isShift(op token .Token ) bool {
return op == token .SHL || op == token .SHR
}
func isComparison(op token .Token ) bool {
switch op {
case token .EQL , token .NEQ , token .LSS , token .LEQ , token .GTR , token .GEQ :
return true
}
return false
}
func fitsFloat32(x constant .Value ) bool {
f32 , _ := constant .Float32Val (x )
f := float64 (f32 )
return !math .IsInf (f , 0 )
}
func roundFloat32(x constant .Value ) constant .Value {
f32 , _ := constant .Float32Val (x )
f := float64 (f32 )
if !math .IsInf (f , 0 ) {
return constant .MakeFloat64 (f )
}
return nil
}
func fitsFloat64(x constant .Value ) bool {
f , _ := constant .Float64Val (x )
return !math .IsInf (f , 0 )
}
func roundFloat64(x constant .Value ) constant .Value {
f , _ := constant .Float64Val (x )
if !math .IsInf (f , 0 ) {
return constant .MakeFloat64 (f )
}
return nil
}
func representableConst(x constant .Value , check *Checker , typ *Basic , rounded *constant .Value ) bool {
if x .Kind () == constant .Unknown {
return true
}
var conf *Config
if check != nil {
conf = check .conf
}
switch {
case isInteger (typ ):
x := constant .ToInt (x )
if x .Kind () != constant .Int {
return false
}
if rounded != nil {
*rounded = x
}
if x , ok := constant .Int64Val (x ); ok {
switch typ .kind {
case Int :
var s = uint (conf .sizeof (typ )) * 8
return int64 (-1 )<<(s -1 ) <= x && x <= int64 (1 )<<(s -1 )-1
case Int8 :
const s = 8
return -1 <<(s -1 ) <= x && x <= 1 <<(s -1 )-1
case Int16 :
const s = 16
return -1 <<(s -1 ) <= x && x <= 1 <<(s -1 )-1
case Int32 :
const s = 32
return -1 <<(s -1 ) <= x && x <= 1 <<(s -1 )-1
case Int64 , UntypedInt :
return true
case Uint , Uintptr :
if s := uint (conf .sizeof (typ )) * 8 ; s < 64 {
return 0 <= x && x <= int64 (1 )<<s -1
}
return 0 <= x
case Uint8 :
const s = 8
return 0 <= x && x <= 1 <<s -1
case Uint16 :
const s = 16
return 0 <= x && x <= 1 <<s -1
case Uint32 :
const s = 32
return 0 <= x && x <= 1 <<s -1
case Uint64 :
return 0 <= x
default :
unreachable ()
}
}
switch n := constant .BitLen (x ); typ .kind {
case Uint , Uintptr :
var s = uint (conf .sizeof (typ )) * 8
return constant .Sign (x ) >= 0 && n <= int (s )
case Uint64 :
return constant .Sign (x ) >= 0 && n <= 64
case UntypedInt :
return true
}
case isFloat (typ ):
x := constant .ToFloat (x )
if x .Kind () != constant .Float {
return false
}
switch typ .kind {
case Float32 :
if rounded == nil {
return fitsFloat32 (x )
}
r := roundFloat32 (x )
if r != nil {
*rounded = r
return true
}
case Float64 :
if rounded == nil {
return fitsFloat64 (x )
}
r := roundFloat64 (x )
if r != nil {
*rounded = r
return true
}
case UntypedFloat :
return true
default :
unreachable ()
}
case isComplex (typ ):
x := constant .ToComplex (x )
if x .Kind () != constant .Complex {
return false
}
switch typ .kind {
case Complex64 :
if rounded == nil {
return fitsFloat32 (constant .Real (x )) && fitsFloat32 (constant .Imag (x ))
}
re := roundFloat32 (constant .Real (x ))
im := roundFloat32 (constant .Imag (x ))
if re != nil && im != nil {
*rounded = constant .BinaryOp (re , token .ADD , constant .MakeImag (im ))
return true
}
case Complex128 :
if rounded == nil {
return fitsFloat64 (constant .Real (x )) && fitsFloat64 (constant .Imag (x ))
}
re := roundFloat64 (constant .Real (x ))
im := roundFloat64 (constant .Imag (x ))
if re != nil && im != nil {
*rounded = constant .BinaryOp (re , token .ADD , constant .MakeImag (im ))
return true
}
case UntypedComplex :
return true
default :
unreachable ()
}
case isString (typ ):
return x .Kind () == constant .String
case isBoolean (typ ):
return x .Kind () == constant .Bool
}
return false
}
func (check *Checker ) representable (x *operand , typ *Basic ) {
v , code := check .representation (x , typ )
if code != 0 {
check .invalidConversion (code , x , typ )
x .mode = invalid
return
}
assert (v != nil )
x .val = v
}
func (check *Checker ) representation (x *operand , typ *Basic ) (constant .Value , errorCode ) {
assert (x .mode == constant_ )
v := x .val
if !representableConst (x .val , check , typ , &v ) {
if isNumeric (x .typ ) && isNumeric (typ ) {
if !isInteger (x .typ ) && isInteger (typ ) {
return nil , _TruncatedFloat
} else {
return nil , _NumericOverflow
}
}
return nil , _InvalidConstVal
}
return v , 0
}
func (check *Checker ) invalidConversion (code errorCode , x *operand , target Type ) {
msg := "cannot convert %s to %s"
switch code {
case _TruncatedFloat :
msg = "%s truncated to %s"
case _NumericOverflow :
msg = "%s overflows %s"
}
check .errorf (x , code , msg , x , target )
}
func (check *Checker ) updateExprType (x ast .Expr , typ Type , final bool ) {
check .updateExprType0 (nil , x , typ , final )
}
func (check *Checker ) updateExprType0 (parent , x ast .Expr , typ Type , final bool ) {
old , found := check .untyped [x ]
if !found {
return
}
switch x := x .(type ) {
case *ast .BadExpr ,
*ast .FuncLit ,
*ast .CompositeLit ,
*ast .IndexExpr ,
*ast .SliceExpr ,
*ast .TypeAssertExpr ,
*ast .StarExpr ,
*ast .KeyValueExpr ,
*ast .ArrayType ,
*ast .StructType ,
*ast .FuncType ,
*ast .InterfaceType ,
*ast .MapType ,
*ast .ChanType :
if debug {
check .dump ("%v: found old type(%s): %s (new: %s)" , x .Pos (), x , old .typ , typ )
unreachable ()
}
return
case *ast .CallExpr :
case *ast .Ident , *ast .BasicLit , *ast .SelectorExpr :
case *ast .ParenExpr :
check .updateExprType0 (x , x .X , typ , final )
case *ast .UnaryExpr :
if old .val != nil {
break
}
check .updateExprType0 (x , x .X , typ , final )
case *ast .BinaryExpr :
if old .val != nil {
break
}
if isComparison (x .Op ) {
} else if isShift (x .Op ) {
check .updateExprType0 (x , x .X , typ , final )
} else {
check .updateExprType0 (x , x .X , typ , final )
check .updateExprType0 (x , x .Y , typ , final )
}
default :
unreachable ()
}
if !final && isUntyped (typ ) {
old .typ = under (typ ).(*Basic )
check .untyped [x ] = old
return
}
delete (check .untyped , x )
if old .isLhs {
if !allInteger (typ ) {
if compilerErrorMessages {
check .invalidOp (x , _InvalidShiftOperand , "%s (shift of type %s)" , parent , typ )
} else {
check .invalidOp (x , _InvalidShiftOperand , "shifted operand %s (type %s) must be integer" , x , typ )
}
return
}
}
if old .val != nil {
c := operand {old .mode , x , old .typ , old .val , 0 }
check .convertUntyped (&c , typ )
if c .mode == invalid {
return
}
}
check .recordTypeAndValue (x , old .mode , typ , old .val )
}
func (check *Checker ) updateExprVal (x ast .Expr , val constant .Value ) {
if info , ok := check .untyped [x ]; ok {
info .val = val
check .untyped [x ] = info
}
}
func (check *Checker ) convertUntyped (x *operand , target Type ) {
newType , val , code := check .implicitTypeAndValue (x , target )
if code != 0 {
t := target
if !isTypeParam (target ) {
t = safeUnderlying (target )
}
check .invalidConversion (code , x , t )
x .mode = invalid
return
}
if val != nil {
x .val = val
check .updateExprVal (x .expr , val )
}
if newType != x .typ {
x .typ = newType
check .updateExprType (x .expr , newType , false )
}
}
func (check *Checker ) implicitTypeAndValue (x *operand , target Type ) (Type , constant .Value , errorCode ) {
if x .mode == invalid || isTyped (x .typ ) || target == Typ [Invalid ] {
return x .typ , nil , 0
}
if isUntyped (target ) {
xkind := x .typ .(*Basic ).kind
tkind := target .(*Basic ).kind
if isNumeric (x .typ ) && isNumeric (target ) {
if xkind < tkind {
return target , nil , 0
}
} else if xkind != tkind {
return nil , nil , _InvalidUntypedConversion
}
return x .typ , nil , 0
}
switch u := under (target ).(type ) {
case *Basic :
if x .mode == constant_ {
v , code := check .representation (x , u )
if code != 0 {
return nil , nil , code
}
return target , v , code
}
switch x .typ .(*Basic ).kind {
case UntypedBool :
if !isBoolean (target ) {
return nil , nil , _InvalidUntypedConversion
}
case UntypedInt , UntypedRune , UntypedFloat , UntypedComplex :
if !isNumeric (target ) {
return nil , nil , _InvalidUntypedConversion
}
case UntypedString :
if !isString (target ) {
return nil , nil , _InvalidUntypedConversion
}
case UntypedNil :
if !hasNil (target ) {
return nil , nil , _InvalidUntypedConversion
}
return Typ [UntypedNil ], nil , 0
default :
return nil , nil , _InvalidUntypedConversion
}
case *Interface :
if isTypeParam (target ) {
if !u .typeSet ().underIs (func (u Type ) bool {
if u == nil {
return false
}
t , _ , _ := check .implicitTypeAndValue (x , u )
return t != nil
}) {
return nil , nil , _InvalidUntypedConversion
}
if x .isNil () {
return Typ [UntypedNil ], nil , 0
}
break
}
if x .isNil () {
return Typ [UntypedNil ], nil , 0
}
if !u .Empty () {
return nil , nil , _InvalidUntypedConversion
}
return Default (x .typ ), nil , 0
case *Pointer , *Signature , *Slice , *Map , *Chan :
if !x .isNil () {
return nil , nil , _InvalidUntypedConversion
}
return Typ [UntypedNil ], nil , 0
default :
return nil , nil , _InvalidUntypedConversion
}
return target , nil , 0
}
func (check *Checker ) comparison (x , y *operand , op token .Token , switchCase bool ) {
if switchCase {
op = token .EQL
}
errOp := x
cause := ""
code := _MismatchedTypes
ok , _ := x .assignableTo (check , y .typ , nil )
if !ok {
ok , _ = y .assignableTo (check , x .typ , nil )
}
if !ok {
errOp = y
if !compilerErrorMessages {
errOp = x
}
cause = check .sprintf ("mismatched types %s and %s" , x .typ , y .typ )
goto Error
}
code = _UndefinedOp
switch op {
case token .EQL , token .NEQ :
switch {
case x .isNil () || y .isNil ():
typ := x .typ
if x .isNil () {
typ = y .typ
}
if !hasNil (typ ) {
errOp = y
goto Error
}
case !Comparable (x .typ ):
errOp = x
cause = check .incomparableCause (x .typ )
goto Error
case !Comparable (y .typ ):
errOp = y
cause = check .incomparableCause (y .typ )
goto Error
}
case token .LSS , token .LEQ , token .GTR , token .GEQ :
switch {
case !allOrdered (x .typ ):
errOp = x
goto Error
case !allOrdered (y .typ ):
errOp = y
goto Error
}
default :
unreachable ()
}
if x .mode == constant_ && y .mode == constant_ {
x .val = constant .MakeBool (constant .Compare (x .val , op , y .val ))
} else {
x .mode = value
check .updateExprType (x .expr , Default (x .typ ), true )
check .updateExprType (y .expr , Default (y .typ ), true )
}
x .typ = Typ [UntypedBool ]
return
Error :
if cause == "" {
if isTypeParam (x .typ ) || isTypeParam (y .typ ) {
if !isTypeParam (x .typ ) {
errOp = y
}
cause = check .sprintf ("type parameter %s is not comparable with %s" , errOp .typ , op )
} else {
cause = check .sprintf ("operator %s not defined on %s" , op , check .kindString (errOp .typ ))
}
}
if switchCase {
check .errorf (x , code , "invalid case %s in switch on %s (%s)" , x .expr , y .expr , cause )
} else {
if compilerErrorMessages {
check .invalidOp (errOp , code , "%s %s %s (%s)" , x .expr , op , y .expr , cause )
} else {
check .invalidOp (errOp , code , "cannot compare %s %s %s (%s)" , x .expr , op , y .expr , cause )
}
}
x .mode = invalid
}
func (check *Checker ) incomparableCause (typ Type ) string {
switch under (typ ).(type ) {
case *Slice , *Signature , *Map :
return check .kindString (typ ) + " can only be compared to nil"
}
var cause string
comparable (typ , true , nil , func (format string , args ...interface {}) {
cause = check .sprintf (format , args ...)
})
return cause
}
func (check *Checker ) kindString (typ Type ) string {
switch under (typ ).(type ) {
case *Array :
return "array"
case *Slice :
return "slice"
case *Struct :
return "struct"
case *Pointer :
return "pointer"
case *Signature :
return "func"
case *Interface :
if isTypeParam (typ ) {
return check .sprintf ("type parameter %s" , typ )
}
return "interface"
case *Map :
return "map"
case *Chan :
return "chan"
default :
return check .sprintf ("%s" , typ )
}
}
func (check *Checker ) shift (x , y *operand , e ast .Expr , op token .Token ) {
var xval constant .Value
if x .mode == constant_ {
xval = constant .ToInt (x .val )
}
if allInteger (x .typ ) || isUntyped (x .typ ) && xval != nil && xval .Kind () == constant .Int {
} else {
check .invalidOp (x , _InvalidShiftOperand , "shifted operand %s must be integer" , x )
x .mode = invalid
return
}
if y .mode == constant_ {
yval := constant .ToInt (y .val )
if yval .Kind () == constant .Int && constant .Sign (yval ) < 0 {
check .invalidOp (y , _InvalidShiftCount , "negative shift count %s" , y )
x .mode = invalid
return
}
if isUntyped (y .typ ) {
check .representable (y , Typ [Uint ])
if y .mode == invalid {
x .mode = invalid
return
}
}
}
switch {
case allInteger (y .typ ):
if !allUnsigned (y .typ ) && !check .allowVersion (check .pkg , 1 , 13 ) {
check .invalidOp (y , _InvalidShiftCount , "signed shift count %s requires go1.13 or later" , y )
x .mode = invalid
return
}
case isUntyped (y .typ ):
check .convertUntyped (y , Typ [Uint ])
if y .mode == invalid {
x .mode = invalid
return
}
default :
check .invalidOp (y , _InvalidShiftCount , "shift count %s must be integer" , y )
x .mode = invalid
return
}
if x .mode == constant_ {
if y .mode == constant_ {
if x .val .Kind () == constant .Unknown || y .val .Kind () == constant .Unknown {
x .val = constant .MakeUnknown ()
if !isInteger (x .typ ) {
x .typ = Typ [UntypedInt ]
}
return
}
const shiftBound = 1023 - 1 + 52
s , ok := constant .Uint64Val (y .val )
if !ok || s > shiftBound {
check .invalidOp (y , _InvalidShiftCount , "invalid shift count %s" , y )
x .mode = invalid
return
}
if !isInteger (x .typ ) {
x .typ = Typ [UntypedInt ]
}
x .val = constant .Shift (xval , op , uint (s ))
x .expr = e
opPos := x .Pos ()
if b , _ := e .(*ast .BinaryExpr ); b != nil {
opPos = b .OpPos
}
check .overflow (x , op , opPos )
return
}
if isUntyped (x .typ ) {
if info , found := check .untyped [x .expr ]; found {
info .isLhs = true
check .untyped [x .expr ] = info
}
x .mode = value
return
}
}
if !allInteger (x .typ ) {
check .invalidOp (x , _InvalidShiftOperand , "shifted operand %s must be integer" , x )
x .mode = invalid
return
}
x .mode = value
}
var binaryOpPredicates opPredicates
func init() {
binaryOpPredicates = opPredicates {
token .ADD : allNumericOrString ,
token .SUB : allNumeric ,
token .MUL : allNumeric ,
token .QUO : allNumeric ,
token .REM : allInteger ,
token .AND : allInteger ,
token .OR : allInteger ,
token .XOR : allInteger ,
token .AND_NOT : allInteger ,
token .LAND : allBoolean ,
token .LOR : allBoolean ,
}
}
func (check *Checker ) binary (x *operand , e ast .Expr , lhs , rhs ast .Expr , op token .Token , opPos token .Pos ) {
var y operand
check .expr (x , lhs )
check .expr (&y , rhs )
if x .mode == invalid {
return
}
if y .mode == invalid {
x .mode = invalid
x .expr = y .expr
return
}
if isShift (op ) {
check .shift (x , &y , e , op )
return
}
canMix := func (x , y *operand ) bool {
if IsInterface (x .typ ) && !isTypeParam (x .typ ) || IsInterface (y .typ ) && !isTypeParam (y .typ ) {
return true
}
if allBoolean (x .typ ) != allBoolean (y .typ ) {
return false
}
if allString (x .typ ) != allString (y .typ ) {
return false
}
if x .isNil () && !hasNil (y .typ ) {
return false
}
if y .isNil () && !hasNil (x .typ ) {
return false
}
return true
}
if canMix (x , &y ) {
check .convertUntyped (x , y .typ )
if x .mode == invalid {
return
}
check .convertUntyped (&y , x .typ )
if y .mode == invalid {
x .mode = invalid
return
}
}
if isComparison (op ) {
check .comparison (x , &y , op , false )
return
}
if !Identical (x .typ , y .typ ) {
if x .typ != Typ [Invalid ] && y .typ != Typ [Invalid ] {
var posn positioner = x
if e != nil {
posn = e
}
if e != nil {
check .invalidOp (posn , _MismatchedTypes , "%s (mismatched types %s and %s)" , e , x .typ , y .typ )
} else {
check .invalidOp (posn , _MismatchedTypes , "%s %s= %s (mismatched types %s and %s)" , lhs , op , rhs , x .typ , y .typ )
}
}
x .mode = invalid
return
}
if !check .op (binaryOpPredicates , x , op ) {
x .mode = invalid
return
}
if op == token .QUO || op == token .REM {
if (x .mode == constant_ || allInteger (x .typ )) && y .mode == constant_ && constant .Sign (y .val ) == 0 {
check .invalidOp (&y , _DivByZero , "division by zero" )
x .mode = invalid
return
}
if x .mode == constant_ && y .mode == constant_ && isComplex (x .typ ) {
re , im := constant .Real (y .val ), constant .Imag (y .val )
re2 , im2 := constant .BinaryOp (re , token .MUL , re ), constant .BinaryOp (im , token .MUL , im )
if constant .Sign (re2 ) == 0 && constant .Sign (im2 ) == 0 {
check .invalidOp (&y , _DivByZero , "division by zero" )
x .mode = invalid
return
}
}
}
if x .mode == constant_ && y .mode == constant_ {
if x .val .Kind () == constant .Unknown || y .val .Kind () == constant .Unknown {
x .val = constant .MakeUnknown ()
return
}
if op == token .QUO && isInteger (x .typ ) {
op = token .QUO_ASSIGN
}
x .val = constant .BinaryOp (x .val , op , y .val )
x .expr = e
check .overflow (x , op , opPos )
return
}
x .mode = value
}
type exprKind int
const (
conversion exprKind = iota
expression
statement
)
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 ++
defer func () {
check .indent --
check .trace (e .Pos (), "=> %s" , x )
}()
}
kind := check .exprInternal (x , e , hint )
if !allowGeneric {
check .nonGeneric (x )
}
check .record (x )
return kind
}
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 , _WrongTypeArgCount , "cannot use generic %s %s without instantiation" , what , x .expr )
x .mode = invalid
x .typ = Typ [Invalid ]
}
}
func (check *Checker ) exprInternal (x *operand , e ast .Expr , hint Type ) exprKind {
x .mode = invalid
x .typ = Typ [Invalid ]
switch e := e .(type ) {
case *ast .BadExpr :
goto Error
case *ast .Ident :
check .ident (x , e , nil , false )
case *ast .Ellipsis :
check .error (e , _BadDotDotDotSyntax , "invalid use of '...'" )
goto Error
case *ast .BasicLit :
switch e .Kind {
case token .INT , token .FLOAT , token .IMAG :
check .langCompat (e )
const limit = 10000
if len (e .Value ) > limit {
check .errorf (e , _InvalidConstVal , "excessively long constant: %s... (%d chars)" , e .Value [:10 ], len (e .Value ))
goto Error
}
}
x .setConst (e .Kind , e .Value )
if x .mode == invalid {
check .errorf (e , _InvalidConstVal , "malformed constant: %s" , e .Value )
goto Error
}
case *ast .FuncLit :
if sig , ok := check .typ (e .Type ).(*Signature ); ok {
if !check .conf .IgnoreFuncBodies && e .Body != nil {
decl := check .decl
iota := check .iota
check .later (func () {
check .funcBody (decl , "<function literal>" , sig , e .Body , iota )
})
}
x .mode = value
x .typ = sig
} else {
check .invalidAST (e , "invalid function literal %s" , e )
goto Error
}
case *ast .CompositeLit :
var typ , base Type
switch {
case e .Type != nil :
if atyp , _ := e .Type .(*ast .ArrayType ); atyp != nil && atyp .Len != nil {
if ellip , _ := atyp .Len .(*ast .Ellipsis ); ellip != nil && ellip .Elt == nil {
typ = &Array {len : -1 , elem : check .varType (atyp .Elt )}
base = typ
break
}
}
typ = check .typ (e .Type )
base = typ
case hint != nil :
typ = hint
base , _ = deref (coreType (typ ))
if base == nil {
check .errorf (e , _InvalidLit , "invalid composite literal element type %s: no core type" , typ )
goto Error
}
default :
check .error (e , _UntypedLit , "missing type in composite literal" )
goto Error
}
switch utyp := coreType (base ).(type ) {
case *Struct :
if utyp .fields == nil {
check .error (e , _InvalidDeclCycle , "illegal cycle in type declaration" )
goto Error
}
if len (e .Elts ) == 0 {
break
}
fields := utyp .fields
if _ , ok := e .Elts [0 ].(*ast .KeyValueExpr ); ok {
visited := make ([]bool , len (fields ))
for _ , e := range e .Elts {
kv , _ := e .(*ast .KeyValueExpr )
if kv == nil {
check .error (e , _MixedStructLit , "mixture of field:value and value elements in struct literal" )
continue
}
key , _ := kv .Key .(*ast .Ident )
check .expr (x , kv .Value )
if key == nil {
check .errorf (kv , _InvalidLitField , "invalid field name %s in struct literal" , kv .Key )
continue
}
i := fieldIndex (utyp .fields , check .pkg , key .Name )
if i < 0 {
check .errorf (kv , _MissingLitField , "unknown field %s in struct literal" , key .Name )
continue
}
fld := fields [i ]
check .recordUse (key , fld )
etyp := fld .typ
check .assignment (x , etyp , "struct literal" )
if visited [i ] {
check .errorf (kv , _DuplicateLitField , "duplicate field name %s in struct literal" , key .Name )
continue
}
visited [i ] = true
}
} else {
for i , e := range e .Elts {
if kv , _ := e .(*ast .KeyValueExpr ); kv != nil {
check .error (kv , _MixedStructLit , "mixture of field:value and value elements in struct literal" )
continue
}
check .expr (x , e )
if i >= len (fields ) {
check .error (x , _InvalidStructLit , "too many values in struct literal" )
break
}
fld := fields [i ]
if !fld .Exported () && fld .pkg != check .pkg {
check .errorf (x ,
_UnexportedLitField ,
"implicit assignment to unexported field %s in %s literal" , fld .name , typ )
continue
}
etyp := fld .typ
check .assignment (x , etyp , "struct literal" )
}
if len (e .Elts ) < len (fields ) {
check .error (inNode (e , e .Rbrace ), _InvalidStructLit , "too few values in struct literal" )
}
}
case *Array :
if utyp .elem == nil {
check .error (e , _InvalidTypeCycle , "illegal cycle in type declaration" )
goto Error
}
n := check .indexedElts (e .Elts , utyp .elem , utyp .len )
if utyp .len < 0 {
utyp .len = n
if e .Type != nil {
check .recordTypeAndValue (e .Type , typexpr , utyp , nil )
}
}
case *Slice :
if utyp .elem == nil {
check .error (e , _InvalidTypeCycle , "illegal cycle in type declaration" )
goto Error
}
check .indexedElts (e .Elts , utyp .elem , -1 )
case *Map :
if utyp .key == nil || utyp .elem == nil {
check .error (e , _InvalidTypeCycle , "illegal cycle in type declaration" )
goto Error
}
visited := make (map [any ][]Type , len (e .Elts ))
for _ , e := range e .Elts {
kv , _ := e .(*ast .KeyValueExpr )
if kv == nil {
check .error (e , _MissingLitKey , "missing key in map literal" )
continue
}
check .exprWithHint (x , kv .Key , utyp .key )
check .assignment (x , utyp .key , "map literal" )
if x .mode == invalid {
continue
}
if x .mode == constant_ {
duplicate := false
xkey := keyVal (x .val )
if IsInterface (utyp .key ) {
for _ , vtyp := range visited [xkey ] {
if Identical (vtyp , x .typ ) {
duplicate = true
break
}
}
visited [xkey ] = append (visited [xkey ], x .typ )
} else {
_, duplicate = visited [xkey ]
visited [xkey ] = nil
}
if duplicate {
check .errorf (x , _DuplicateLitKey , "duplicate key %s in map literal" , x .val )
continue
}
}
check .exprWithHint (x , kv .Value , utyp .elem )
check .assignment (x , utyp .elem , "map literal" )
}
default :
for _ , e := range e .Elts {
if kv , _ := e .(*ast .KeyValueExpr ); kv != nil {
e = kv .Value
}
check .use (e )
}
if utyp != Typ [Invalid ] {
check .errorf (e , _InvalidLit , "invalid composite literal type %s" , typ )
goto Error
}
}
x .mode = value
x .typ = typ
case *ast .ParenExpr :
kind := check .rawExpr (x , e .X , nil , false )
x .expr = e
return kind
case *ast .SelectorExpr :
check .selector (x , e , nil )
case *ast .IndexExpr , *ast .IndexListExpr :
ix := typeparams .UnpackIndexExpr (e )
if check .indexExpr (x , ix ) {
check .funcInst (x , ix )
}
if x .mode == invalid {
goto Error
}
case *ast .SliceExpr :
check .sliceExpr (x , e )
if x .mode == invalid {
goto Error
}
case *ast .TypeAssertExpr :
check .expr (x , e .X )
if x .mode == invalid {
goto Error
}
if isTypeParam (x .typ ) {
check .invalidOp (x , _InvalidAssert , "cannot use type assertion on type parameter value %s" , x )
goto Error
}
if _ , ok := under (x .typ ).(*Interface ); !ok {
check .invalidOp (x , _InvalidAssert , "%s is not an interface" , x )
goto Error
}
if e .Type == nil {
check .error (e , _BadTypeKeyword , "use of .(type) outside type switch" )
goto Error
}
T := check .varType (e .Type )
if T == Typ [Invalid ] {
goto Error
}
check .typeAssertion (e , x , T , false )
x .mode = commaok
x .typ = T
case *ast .CallExpr :
return check .callExpr (x , e )
case *ast .StarExpr :
check .exprOrType (x , e .X , false )
switch x .mode {
case invalid :
goto Error
case typexpr :
check .validVarType (e .X , x .typ )
x .typ = &Pointer {base : x .typ }
default :
var base Type
if !underIs (x .typ , func (u Type ) bool {
p , _ := u .(*Pointer )
if p == nil {
check .invalidOp (x , _InvalidIndirection , "cannot indirect %s" , x )
return false
}
if base != nil && !Identical (p .base , base ) {
check .invalidOp (x , _InvalidIndirection , "pointers of %s must have identical base types" , x )
return false
}
base = p .base
return true
}) {
goto Error
}
x .mode = variable
x .typ = base
}
case *ast .UnaryExpr :
check .unary (x , e )
if x .mode == invalid {
goto Error
}
if e .Op == token .ARROW {
x .expr = e
return statement
}
case *ast .BinaryExpr :
check .binary (x , e , e .X , e .Y , e .Op , e .OpPos )
if x .mode == invalid {
goto Error
}
case *ast .KeyValueExpr :
check .invalidAST (e , "no key:value expected" )
goto Error
case *ast .ArrayType , *ast .StructType , *ast .FuncType ,
*ast .InterfaceType , *ast .MapType , *ast .ChanType :
x .mode = typexpr
x .typ = check .typ (e )
default :
panic (fmt .Sprintf ("%s: unknown expression type %T" , check .fset .Position (e .Pos ()), e ))
}
x .expr = e
return expression
Error :
x .mode = invalid
x .expr = e
return statement
}
func keyVal(x constant .Value ) any {
switch x .Kind () {
case constant .Bool :
return constant .BoolVal (x )
case constant .String :
return constant .StringVal (x )
case constant .Int :
if v , ok := constant .Int64Val (x ); ok {
return v
}
if v , ok := constant .Uint64Val (x ); ok {
return v
}
case constant .Float :
v , _ := constant .Float64Val (x )
return v
case constant .Complex :
r , _ := constant .Float64Val (constant .Real (x ))
i , _ := constant .Float64Val (constant .Imag (x ))
return complex (r , i )
}
return x
}
func (check *Checker ) typeAssertion (e ast .Expr , x *operand , T Type , typeSwitch bool ) {
method , alt := check .assertableTo (under (x .typ ).(*Interface ), T )
if method == nil {
return
}
cause := check .missingMethodReason (T , x .typ , method , alt )
if typeSwitch {
check .errorf (e , _ImpossibleAssert , "impossible type switch case: %s\n\t%s cannot have dynamic type %s %s" , e , x , T , cause )
return
}
check .errorf (e , _ImpossibleAssert , "impossible type assertion: %s\n\t%s does not implement %s %s" , e , T , x .typ , cause )
}
func (check *Checker ) expr (x *operand , e ast .Expr ) {
check .rawExpr (x , e , nil , false )
check .exclude (x , 1 <<novalue |1 <<builtin |1 <<typexpr )
check .singleValue (x )
}
func (check *Checker ) multiExpr (x *operand , e ast .Expr ) {
check .rawExpr (x , e , nil , false )
check .exclude (x , 1 <<novalue |1 <<builtin |1 <<typexpr )
}
func (check *Checker ) exprWithHint (x *operand , e ast .Expr , hint Type ) {
assert (hint != nil )
check .rawExpr (x , e , hint , false )
check .exclude (x , 1 <<novalue |1 <<builtin |1 <<typexpr )
check .singleValue (x )
}
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 )
}
func (check *Checker ) exclude (x *operand , modeset uint ) {
if modeset &(1 <<x .mode ) != 0 {
var msg string
var code errorCode
switch x .mode {
case novalue :
if modeset &(1 <<typexpr ) != 0 {
msg = "%s used as value"
} else {
msg = "%s used as value or type"
}
code = _TooManyValues
case builtin :
msg = "%s must be called"
code = _UncalledBuiltin
case typexpr :
msg = "%s is not an expression"
code = _NotAnExpr
default :
unreachable ()
}
check .errorf (x , code , msg , x )
x .mode = invalid
}
}
func (check *Checker ) singleValue (x *operand ) {
if x .mode == value {
if t , ok := x .typ .(*Tuple ); ok {
assert (t .Len () != 1 )
if compilerErrorMessages {
check .errorf (x , _TooManyValues , "multiple-value %s in single-value context" , x )
} else {
check .errorf (x , _TooManyValues , "%d-valued %s where single value is expected" , t .Len (), x )
}
x .mode = invalid
}
}
}
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 .