Move code into separate function in separate file.
Replace "goto Error" statements with "x.mode = invalid; return".
No other semantic changes.
Change-Id: I2d5e858e8df3dc1011fa79cdac3db9d3e7b1dfe5
Reviewed-on: https://go-review.googlesource.com/c/go/+/610556
Reviewed-by: Tim King <taking@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
}
case *syntax.CompositeLit:
- 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 && atyp.Len == 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.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")
+ check.compositeLit(T, x, e, hint)
+ if x.mode == invalid {
goto Error
}
- 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")
- goto Error
- }
- 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(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")
- goto Error
- }
- 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")
- goto Error
- }
- 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")
- goto Error
- }
- // 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[interface{}][]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)
- goto Error
- }
- }
-
- x.mode = value
- x.typ = typ
-
case *syntax.ParenExpr:
// type inference doesn't go past parentheses (target type T = nil)
kind := check.rawExpr(nil, x, e.X, nil, false)
--- /dev/null
+// 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 composite literals.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ . "internal/types/errors"
+)
+
+func (check *Checker) compositeLit(T *target, 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 && atyp.Len == 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.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")
+ x.mode = invalid
+ return
+ }
+
+ 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(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
+}
}
case *ast.CompositeLit:
- 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.(*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.varType(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 = 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")
+ check.compositeLit(T, x, e, hint)
+ if x.mode == invalid {
goto Error
}
- 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")
- goto Error
- }
- if len(e.Elts) == 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.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(nil, 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, false)
- if i < 0 {
- var alt Object
- if j := fieldIndex(fields, check.pkg, key.Name, true); j >= 0 {
- alt = fields[j]
- }
- msg := check.lookupError(base, key.Name, 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.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(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.Elts) < 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")
- 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 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")
- 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, "invalid recursive type")
- goto Error
- }
- // 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.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 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.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 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)
- goto Error
- }
- }
-
- x.mode = value
- x.typ = typ
-
case *ast.ParenExpr:
// type inference doesn't go past parentheses (target type T = nil)
kind := check.rawExpr(nil, x, e.X, nil, false)
--- /dev/null
+// 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 composite literals.
+
+package types
+
+import (
+ "go/ast"
+ . "internal/types/errors"
+)
+
+func (check *Checker) compositeLit(T *target, x *operand, e *ast.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.(*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.varType(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 = 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")
+ x.mode = invalid
+ return
+ }
+
+ 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.Elts) == 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.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(nil, 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, false)
+ if i < 0 {
+ var alt Object
+ if j := fieldIndex(fields, check.pkg, key.Name, true); j >= 0 {
+ alt = fields[j]
+ }
+ msg := check.lookupError(base, key.Name, 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.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(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.Elts) < 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.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 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.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, "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.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 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.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 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
+}