Created
August 17, 2021 05:53
-
-
Save percybolmer/8ff3b11d6d3cd77d790cacd9b6c0f408 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// exprInternal contains the core of type checking of expressions. | |
// Must only be called by rawExpr. | |
// | |
func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { | |
// make sure x has a valid state in case of bailout | |
// (was issue 5770) | |
x.mode = invalid | |
x.typ = Typ[Invalid] | |
switch e := e.(type) { | |
case *ast.BadExpr: | |
goto Error // error was reported before | |
case *ast.Ident: | |
check.ident(x, e, nil, false) | |
case *ast.Ellipsis: | |
// ellipses are handled explicitly where they are legal | |
// (array composite literals and parameter lists) | |
check.error(e, _BadDotDotDotSyntax, "invalid use of '...'") | |
goto Error | |
case *ast.BasicLit: | |
x.setConst(e.Kind, e.Value) | |
if x.mode == invalid { | |
// The parser already establishes syntactic correctness. | |
// If we reach here it's because of number under-/overflow. | |
// TODO(gri) setConst (and in turn the go/constant package) | |
// should return an error describing the issue. | |
check.errorf(e, _InvalidConstVal, "malformed constant: %s", e.Value) | |
goto Error | |
} | |
case *ast.FuncLit: | |
if sig, ok := check.typ(e.Type).(*Signature); ok { | |
// Anonymous functions are considered part of the | |
// init expression/func declaration which contains | |
// them: use existing package-level declaration info. | |
decl := check.decl // capture for use in closure below | |
iota := check.iota // capture for use in closure below (#22345) | |
// Don't type-check right away because the function may | |
// be part of a type definition to which the function | |
// body refers. Instead, type-check as soon as possible, | |
// but before the enclosing scope contents changes (#22992). | |
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: | |
// composite literal type present - use it | |
// [...]T array types may only appear with composite literals. | |
// Check for them here so we don't have to handle ... in general. | |
if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { | |
if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { | |
// We have an "open" [...]T array type. | |
// Create a new ArrayType with unknown length (-1) | |
// and finish setting it up after analyzing the literal. | |
typ = &Array{len: -1, elem: check.typ(atyp.Elt)} | |
base = typ | |
break | |
} | |
} | |
typ = check.typ(e.Type) | |
base = typ | |
case hint != nil: | |
// no composite literal type present - use hint (element type of enclosing type) | |
typ = hint | |
base, _ = deref(typ.Underlying()) // *T implies &T{} | |
default: | |
// TODO(gri) provide better error messages depending on context | |
check.error(e, _UntypedLit, "missing type in composite literal") | |
goto Error | |
} | |
switch utyp := base.Underlying().(type) { | |
case *Struct: | |
if len(e.Elts) == 0 { | |
break | |
} | |
fields := utyp.fields | |
if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { | |
// all elements must have keys | |
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) | |
// do all possible checks early (before exiting due to errors) | |
// so we don't drop information on the floor | |
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") | |
// 0 <= i < len(fields) | |
if visited[i] { | |
check.errorf(kv, _DuplicateLitField, "duplicate field name %s in struct literal", key.Name) | |
continue | |
} | |
visited[i] = true | |
} | |
} else { | |
// no element must have a key | |
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 // cannot continue | |
} | |
// i < len(fields) | |
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") | |
// ok to continue | |
} | |
} | |
case *Array: | |
// Prevent crash if the array referred to is not yet set up. Was issue #18643. | |
// This is a stop-gap solution. Should use Checker.objPath to report entire | |
// path starting with earliest declaration in the source. TODO(gri) fix this. | |
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 we have an array of unknown length (usually [...]T arrays, but also | |
// arrays [n]T where n is invalid) set the length now that we know it and | |
// record the type for the array (usually done by check.typ which is not | |
// called for [...]T). We handle [...]T arrays and arrays with invalid | |
// length the same here because it makes sense to "guess" the length for | |
// the latter if we have a composite literal; e.g. for [n]int{1, 2, 3} | |
// where n is invalid for some reason, it seems fair to assume it should | |
// be 3 (see also Checked.arrayLength and issue #27346). | |
if utyp.len < 0 { | |
utyp.len = n | |
// e.Type is missing if we have a composite literal element | |
// that is itself a composite literal with omitted type. In | |
// that case there is nothing to record (there is no type in | |
// the source at that point). | |
if e.Type != nil { | |
check.recordTypeAndValue(e.Type, typexpr, utyp, nil) | |
} | |
} | |
case *Slice: | |
// Prevent crash if the slice referred to is not yet set up. | |
// See analogous comment for *Array. | |
if utyp.elem == nil { | |
check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration") | |
goto Error | |
} | |
check.indexedElts(e.Elts, utyp.elem, -1) | |
case *Map: | |
// Prevent crash if the map referred to is not yet set up. | |
// See analogous comment for *Array. | |
if utyp.key == nil || utyp.elem == nil { | |
check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration") | |
goto Error | |
} | |
visited := make(map[interface{}][]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 | |
// if the key is of interface type, the type is also significant when checking for duplicates | |
xkey := keyVal(x.val) | |
if _, ok := utyp.key.Underlying().(*Interface); ok { | |
for _, vtyp := range visited[xkey] { | |
if check.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: | |
// when "using" all elements unpack KeyValueExpr | |
// explicitly because check.use doesn't accept them | |
for _, e := range e.Elts { | |
if kv, _ := e.(*ast.KeyValueExpr); kv != nil { | |
// Ideally, we should also "use" kv.Key but we can't know | |
// if it's an externally defined struct key or not. Going | |
// forward anyway can lead to other errors. Give up instead. | |
e = kv.Value | |
} | |
check.use(e) | |
} | |
// if utyp is invalid, an error was reported before | |
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) | |
x.expr = e | |
return kind | |
case *ast.SelectorExpr: | |
check.selector(x, e) | |
case *ast.IndexExpr: | |
check.expr(x, e.X) | |
if x.mode == invalid { | |
check.use(e.Index) | |
goto Error | |
} | |
valid := false | |
length := int64(-1) // valid if >= 0 | |
switch typ := x.typ.Underlying().(type) { | |
case *Basic: | |
if isString(typ) { | |
valid = true | |
if x.mode == constant_ { | |
length = int64(len(constant.StringVal(x.val))) | |
} | |
// an indexed string always yields a byte value | |
// (not a constant) even if the string and the | |
// index are constant | |
x.mode = value | |
x.typ = universeByte // use 'byte' name | |
} | |
case *Array: | |
valid = true | |
length = typ.len | |
if x.mode != variable { | |
x.mode = value | |
} | |
x.typ = typ.elem | |
case *Pointer: | |
if typ, _ := typ.base.Underlying().(*Array); typ != nil { | |
valid = true | |
length = typ.len | |
x.mode = variable | |
x.typ = typ.elem | |
} | |
case *Slice: | |
valid = true | |
x.mode = variable | |
x.typ = typ.elem | |
case *Map: | |
var key operand | |
check.expr(&key, e.Index) | |
check.assignment(&key, typ.key, "map index") | |
// ok to continue even if indexing failed - map element type is known | |
x.mode = mapindex | |
x.typ = typ.elem | |
x.expr = e | |
return expression | |
} | |
if !valid { | |
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x) | |
goto Error | |
} | |
if e.Index == nil { | |
check.invalidAST(e, "missing index for %s", x) | |
goto Error | |
} | |
check.index(e.Index, length) | |
// ok to continue | |
case *ast.SliceExpr: | |
check.expr(x, e.X) | |
if x.mode == invalid { | |
check.use(e.Low, e.High, e.Max) | |
goto Error | |
} | |
valid := false | |
length := int64(-1) // valid if >= 0 | |
switch typ := x.typ.Underlying().(type) { | |
case *Basic: | |
if isString(typ) { | |
if e.Slice3 { | |
check.invalidOp(x, _InvalidSliceExpr, "3-index slice of string") | |
goto Error | |
} | |
valid = true | |
if x.mode == constant_ { | |
length = int64(len(constant.StringVal(x.val))) | |
} | |
// spec: "For untyped string operands the result | |
// is a non-constant value of type string." | |
if typ.kind == UntypedString { | |
x.typ = Typ[String] | |
} | |
} | |
case *Array: | |
valid = true | |
length = typ.len | |
if x.mode != variable { | |
check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x) | |
goto Error | |
} | |
x.typ = &Slice{elem: typ.elem} | |
case *Pointer: | |
if typ, _ := typ.base.Underlying().(*Array); typ != nil { | |
valid = true | |
length = typ.len | |
x.typ = &Slice{elem: typ.elem} | |
} | |
case *Slice: | |
valid = true | |
// x.typ doesn't change | |
} | |
if !valid { | |
check.invalidOp(x, _NonSliceableOperand, "cannot slice %s", x) | |
goto Error | |
} | |
x.mode = value | |
// spec: "Only the first index may be omitted; it defaults to 0." | |
if e.Slice3 && (e.High == nil || e.Max == nil) { | |
check.invalidAST(inNode(e, e.Rbrack), "2nd and 3rd index required in 3-index slice") | |
goto Error | |
} | |
// check indices | |
var ind [3]int64 | |
for i, expr := range []ast.Expr{e.Low, e.High, e.Max} { | |
x := int64(-1) | |
switch { | |
case expr != nil: | |
// The "capacity" is only known statically for strings, arrays, | |
// and pointers to arrays, and it is the same as the length for | |
// those types. | |
max := int64(-1) | |
if length >= 0 { | |
max = length + 1 | |
} | |
if _, v := check.index(expr, max); v >= 0 { | |
x = v | |
} | |
case i == 0: | |
// default is 0 for the first index | |
x = 0 | |
case length >= 0: | |
// default is length (== capacity) otherwise | |
x = length | |
} | |
ind[i] = x | |
} | |
// constant indices must be in range | |
// (check.index already checks that existing indices >= 0) | |
L: | |
for i, x := range ind[:len(ind)-1] { | |
if x > 0 { | |
for _, y := range ind[i+1:] { | |
if y >= 0 && x > y { | |
check.errorf(inNode(e, e.Rbrack), _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y) | |
break L // only report one error, ok to continue | |
} | |
} | |
} | |
} | |
case *ast.TypeAssertExpr: | |
check.expr(x, e.X) | |
if x.mode == invalid { | |
goto Error | |
} | |
xtyp, _ := x.typ.Underlying().(*Interface) | |
if xtyp == nil { | |
check.invalidOp(x, _InvalidAssert, "%s is not an interface", x) | |
goto Error | |
} | |
// x.(type) expressions are handled explicitly in type switches | |
if e.Type == nil { | |
// Don't use invalidAST because this can occur in the AST produced by | |
// go/parser. | |
check.error(e, _BadTypeKeyword, "use of .(type) outside type switch") | |
goto Error | |
} | |
T := check.typ(e.Type) | |
if T == Typ[Invalid] { | |
goto Error | |
} | |
check.typeAssertion(x, x, xtyp, T) | |
x.mode = commaok | |
x.typ = T | |
case *ast.CallExpr: | |
return check.call(x, e) | |
case *ast.StarExpr: | |
check.exprOrType(x, e.X) | |
switch x.mode { | |
case invalid: | |
goto Error | |
case typexpr: | |
x.typ = &Pointer{base: x.typ} | |
default: | |
if typ, ok := x.typ.Underlying().(*Pointer); ok { | |
x.mode = variable | |
x.typ = typ.base | |
} else { | |
check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x) | |
goto Error | |
} | |
} | |
case *ast.UnaryExpr: | |
check.expr(x, e.X) | |
if x.mode == invalid { | |
goto Error | |
} | |
check.unary(x, e, e.Op) | |
if x.mode == invalid { | |
goto Error | |
} | |
if e.Op == token.ARROW { | |
x.expr = e | |
return statement // receive operations may appear in statement context | |
} | |
case *ast.BinaryExpr: | |
check.binary(x, e, e.X, e.Y, e.Op, e.OpPos) | |
if x.mode == invalid { | |
goto Error | |
} | |
case *ast.KeyValueExpr: | |
// key:value expressions are handled in composite literals | |
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) | |
// Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue | |
// even though check.typ has already called it. This is fine as both | |
// times the same expression and type are recorded. It is also not a | |
// performance issue because we only reach here for composite literal | |
// types, which are comparatively rare. | |
default: | |
panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e)) | |
} | |
// everything went well | |
x.expr = e | |
return expression | |
Error: | |
x.mode = invalid | |
x.expr = e | |
return statement // avoid follow-up errors | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment