]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: factor out code for type checking composite literals
authorRobert Griesemer <gri@golang.org>
Wed, 4 Sep 2024 23:01:32 +0000 (16:01 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 5 Sep 2024 22:09:19 +0000 (22:09 +0000)
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>

src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/literals.go [new file with mode: 0644]
src/go/types/expr.go
src/go/types/literals.go [new file with mode: 0644]

index 5279d37544bd3a9e3cc2a8b60f6d29b46d30497b..1afdee2d4d2be28a7ecdd9c1c75f2bc91aa5fd82 100644 (file)
@@ -1150,235 +1150,11 @@ func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Ty
                }
 
        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)
diff --git a/src/cmd/compile/internal/types2/literals.go b/src/cmd/compile/internal/types2/literals.go
new file mode 100644 (file)
index 0000000..43149ec
--- /dev/null
@@ -0,0 +1,249 @@
+// 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
+}
index 8289de223fff5b38304a0c0563e7fb425b144cef..55d38609963629d25816378e9fe22361f7e8ad0e 100644 (file)
@@ -1128,239 +1128,11 @@ func (check *Checker) exprInternal(T *target, x *operand, e ast.Expr, hint Type)
                }
 
        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)
diff --git a/src/go/types/literals.go b/src/go/types/literals.go
new file mode 100644 (file)
index 0000000..f35df42
--- /dev/null
@@ -0,0 +1,253 @@
+// 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
+}