From 0fb35efe5bb4d557e2f80171ede59f945646354e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 4 Sep 2024 16:01:32 -0700 Subject: [PATCH] go/types, types2: factor out code for type checking composite literals 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 Reviewed-by: Robert Griesemer LUCI-TryBot-Result: Go LUCI Auto-Submit: Robert Griesemer --- src/cmd/compile/internal/types2/expr.go | 228 +----------------- src/cmd/compile/internal/types2/literals.go | 249 +++++++++++++++++++ src/go/types/expr.go | 232 +----------------- src/go/types/literals.go | 253 ++++++++++++++++++++ 4 files changed, 506 insertions(+), 456 deletions(-) create mode 100644 src/cmd/compile/internal/types2/literals.go create mode 100644 src/go/types/literals.go diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 5279d37544..1afdee2d4d 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -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 index 0000000000..43149ec9b9 --- /dev/null +++ b/src/cmd/compile/internal/types2/literals.go @@ -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 +} diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 8289de223f..55d3860996 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -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 index 0000000000..f35df42475 --- /dev/null +++ b/src/go/types/literals.go @@ -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 +} -- 2.48.1