// Copyright 2024 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 typechecking of literals. package types2 import ( "cmd/compile/internal/syntax" . "internal/types/errors" "strings" ) // langCompat reports an error if the representation of a numeric // literal is not compatible with the current language version. func (check *Checker) langCompat(lit *syntax.BasicLit) { s := lit.Value if len(s) <= 2 || check.allowVersion(go1_13) { return } // len(s) > 2 if strings.Contains(s, "_") { check.versionErrorf(lit, go1_13, "underscore in numeric literal") return } if s[0] != '0' { return } radix := s[1] if radix == 'b' || radix == 'B' { check.versionErrorf(lit, go1_13, "binary literal") return } if radix == 'o' || radix == 'O' { check.versionErrorf(lit, go1_13, "0o/0O-style octal literal") return } if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') { check.versionErrorf(lit, go1_13, "hexadecimal floating-point literal") } } func (check *Checker) basicLit(x *operand, e *syntax.BasicLit) { switch e.Kind { case syntax.IntLit, syntax.FloatLit, syntax.ImagLit: check.langCompat(e) // The max. mantissa precision for untyped numeric values // is 512 bits, or 4048 bits for each of the two integer // parts of a fraction for floating-point numbers that are // represented accurately in the go/constant package. // Constant literals that are longer than this many bits // are not meaningful; and excessively long constants may // consume a lot of space and time for a useless conversion. // Cap constant length with a generous upper limit that also // allows for separators between all digits. const limit = 10000 if len(e.Value) > limit { check.errorf(e, InvalidConstVal, "excessively long constant: %s... (%d chars)", e.Value[:10], len(e.Value)) x.mode = invalid return } } 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) x.mode = invalid return } // Ensure that integer values don't overflow (go.dev/issue/54280). x.expr = e // make sure that check.overflow below has an error position check.overflow(x, opPos(x.expr)) } func (check *Checker) funcLit(x *operand, e *syntax.FuncLit) { if sig, ok := check.typ(e.Type).(*Signature); ok { // Set the Scope's extent to the complete "func (...) {...}" // so that Scope.Innermost works correctly. sig.scope.pos = e.Pos() sig.scope.end = endPos(e) if !check.conf.IgnoreFuncBodies && e.Body != nil { // 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 (go.dev/issue/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 (go.dev/issue/22992). check.later(func() { check.funcBody(decl, "", sig, e.Body, iota) }).describef(e, "func literal") } x.mode = value x.typ = sig } else { check.errorf(e, InvalidSyntaxTree, "invalid function literal %v", e) x.mode = invalid } } func (check *Checker) compositeLit(x *operand, e *syntax.CompositeLit, hint Type) { var typ, base Type var isElem bool // true if composite literal is an element of an enclosing composite literal 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.(*syntax.ArrayType); atyp != nil && isdddArray(atyp) { // 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.varType(atyp.Elem)} 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 = typ // *T implies &T{} if b, ok := deref(coreType(base)); ok { base = b } isElem = true default: // TODO(gri) provide better error messages depending on context check.error(e, UntypedLit, "missing type in composite literal") // continue with invalid type so that elements are "used" (go.dev/issue/69092) typ = Typ[Invalid] base = typ } switch utyp := coreType(base).(type) { case *Struct: // Prevent crash if the struct referred to is not yet set up. // See analogous comment for *Array. if utyp.fields == nil { check.error(e, InvalidTypeCycle, "invalid recursive type") x.mode = invalid return } if len(e.ElemList) == 0 { break } // Convention for error messages on invalid struct literals: // we mention the struct type only if it clarifies the error // (e.g., a duplicate field error doesn't need the struct type). fields := utyp.fields if _, ok := e.ElemList[0].(*syntax.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) for _, e := range e.ElemList { kv, _ := e.(*syntax.KeyValueExpr) if kv == nil { check.error(e, MixedStructLit, "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*syntax.Name) // do all possible checks early (before exiting due to errors) // so we don't drop information on the floor check.expr(nil, x, kv.Value) if key == nil { check.errorf(kv, InvalidLitField, "invalid field name %s in struct literal", kv.Key) continue } i := fieldIndex(fields, check.pkg, key.Value, false) if i < 0 { var alt Object if j := fieldIndex(fields, check.pkg, key.Value, true); j >= 0 { alt = fields[j] } msg := check.lookupError(base, key.Value, alt, true) check.error(kv.Key, MissingLitField, msg) 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.Value) continue } visited[i] = true } } else { // no element must have a key for i, e := range e.ElemList { if kv, _ := e.(*syntax.KeyValueExpr); kv != nil { check.error(kv, MixedStructLit, "mixture of field:value and value elements in struct literal") continue } check.expr(nil, x, e) if i >= len(fields) { check.errorf(x, InvalidStructLit, "too many values in struct literal of type %s", base) 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 struct literal of type %s", fld.name, base) continue } etyp := fld.typ check.assignment(x, etyp, "struct literal") } if len(e.ElemList) < len(fields) { check.errorf(inNode(e, e.Rbrace), InvalidStructLit, "too few values in struct literal of type %s", base) // ok to continue } } case *Array: // Prevent crash if the array referred to is not yet set up. Was go.dev/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, "invalid recursive type") x.mode = invalid return } n := check.indexedElts(e.ElemList, 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 go.dev/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, "invalid recursive type") x.mode = invalid return } check.indexedElts(e.ElemList, 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, "invalid recursive type") x.mode = invalid return } // If the map key type is an interface (but not a type parameter), // the type of a constant key must be considered when checking for // duplicates. keyIsInterface := isNonTypeParamInterface(utyp.key) visited := make(map[any][]Type, len(e.ElemList)) for _, e := range e.ElemList { kv, _ := e.(*syntax.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 keyIsInterface { 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: // when "using" all elements unpack KeyValueExpr // explicitly because check.use doesn't accept them for _, e := range e.ElemList { if kv, _ := e.(*syntax.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 isValid(utyp) { var qualifier string if isElem { qualifier = " element" } var cause string if utyp == nil { cause = " (no core type)" } check.errorf(e, InvalidLit, "invalid composite literal%s type %s%s", qualifier, typ, cause) x.mode = invalid return } } x.mode = value x.typ = typ } // indexedElts checks the elements (elts) of an array or slice composite literal // against the literal's element type (typ), and the element indices against // the literal length if known (length >= 0). It returns the length of the // literal (maximum index value + 1). func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 { visited := make(map[int64]bool, len(elts)) var index, max int64 for _, e := range elts { // determine and check index validIndex := false eval := e if kv, _ := e.(*syntax.KeyValueExpr); kv != nil { if typ, i := check.index(kv.Key, length); isValid(typ) { if i >= 0 { index = i validIndex = true } else { check.errorf(e, InvalidLitIndex, "index %s must be integer constant", kv.Key) } } eval = kv.Value } else if length >= 0 && index >= length { check.errorf(e, OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length) } else { validIndex = true } // if we have a valid index, check for duplicate entries if validIndex { if visited[index] { check.errorf(e, DuplicateLitKey, "duplicate index %d in array or slice literal", index) } visited[index] = true } index++ if index > max { max = index } // check element against composite literal element type var x operand check.exprWithHint(&x, eval, typ) check.assignment(&x, typ, "array or slice literal") } return max }