mirror of
https://github.com/golang/go
synced 2024-09-06 00:19:45 +00:00
[dev.typeparams] cmd/compile/internal/types2: move struct checking into separate file
This only moves functionality from one file into another. Except for import adjustments there are no changes to the code. Change-Id: I8dff41fe82693c96b09a152975c3fd1e3b439e8d Reviewed-on: https://go-review.googlesource.com/c/go/+/321589 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
211244e172
commit
cfe0250497
165
src/cmd/compile/internal/types2/struct.go
Normal file
165
src/cmd/compile/internal/types2/struct.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
package types2
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
|
||||
if e.FieldList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// struct fields and tags
|
||||
var fields []*Var
|
||||
var tags []string
|
||||
|
||||
// for double-declaration checks
|
||||
var fset objset
|
||||
|
||||
// current field typ and tag
|
||||
var typ Type
|
||||
var tag string
|
||||
add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
|
||||
if tag != "" && tags == nil {
|
||||
tags = make([]string, len(fields))
|
||||
}
|
||||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
name := ident.Value
|
||||
fld := NewField(pos, check.pkg, name, typ, embedded)
|
||||
// spec: "Within a struct, non-blank field names must be unique."
|
||||
if name == "_" || check.declareInSet(&fset, pos, fld) {
|
||||
fields = append(fields, fld)
|
||||
check.recordDef(ident, fld)
|
||||
}
|
||||
}
|
||||
|
||||
// addInvalid adds an embedded field of invalid type to the struct for
|
||||
// fields with errors; this keeps the number of struct fields in sync
|
||||
// with the source as long as the fields are _ or have different names
|
||||
// (issue #25627).
|
||||
addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
|
||||
typ = Typ[Invalid]
|
||||
tag = ""
|
||||
add(ident, true, pos)
|
||||
}
|
||||
|
||||
var prev syntax.Expr
|
||||
for i, f := range e.FieldList {
|
||||
// Fields declared syntactically with the same type (e.g.: a, b, c T)
|
||||
// share the same type expression. Only check type if it's a new type.
|
||||
if i == 0 || f.Type != prev {
|
||||
typ = check.varType(f.Type)
|
||||
prev = f.Type
|
||||
}
|
||||
tag = ""
|
||||
if i < len(e.TagList) {
|
||||
tag = check.tag(e.TagList[i])
|
||||
}
|
||||
if f.Name != nil {
|
||||
// named field
|
||||
add(f.Name, false, f.Name.Pos())
|
||||
} else {
|
||||
// embedded field
|
||||
// spec: "An embedded type must be specified as a type name T or as a
|
||||
// pointer to a non-interface type name *T, and T itself may not be a
|
||||
// pointer type."
|
||||
pos := syntax.StartPos(f.Type)
|
||||
name := embeddedFieldIdent(f.Type)
|
||||
if name == nil {
|
||||
check.errorf(pos, "invalid embedded field type %s", f.Type)
|
||||
name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
|
||||
addInvalid(name, pos)
|
||||
continue
|
||||
}
|
||||
add(name, true, pos)
|
||||
|
||||
// Because we have a name, typ must be of the form T or *T, where T is the name
|
||||
// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
|
||||
// We must delay this check to the end because we don't want to instantiate
|
||||
// (via under(t)) a possibly incomplete type.
|
||||
embeddedTyp := typ // for closure below
|
||||
embeddedPos := pos
|
||||
check.later(func() {
|
||||
t, isPtr := deref(embeddedTyp)
|
||||
switch t := optype(t).(type) {
|
||||
case *Basic:
|
||||
if t == Typ[Invalid] {
|
||||
// error was reported before
|
||||
return
|
||||
}
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if t.kind == UnsafePointer {
|
||||
check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
|
||||
}
|
||||
case *Pointer:
|
||||
check.error(embeddedPos, "embedded field type cannot be a pointer")
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
styp.fields = fields
|
||||
styp.tags = tags
|
||||
}
|
||||
|
||||
func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
|
||||
switch e := e.(type) {
|
||||
case *syntax.Name:
|
||||
return e
|
||||
case *syntax.Operation:
|
||||
if base := ptrBase(e); base != nil {
|
||||
// *T is valid, but **T is not
|
||||
if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
|
||||
return embeddedFieldIdent(e.X)
|
||||
}
|
||||
}
|
||||
case *syntax.SelectorExpr:
|
||||
return e.Sel
|
||||
case *syntax.IndexExpr:
|
||||
return embeddedFieldIdent(e.X)
|
||||
}
|
||||
return nil // invalid embedded field
|
||||
}
|
||||
|
||||
func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
|
||||
if alt := oset.insert(obj); alt != nil {
|
||||
var err error_
|
||||
err.errorf(pos, "%s redeclared", obj.Name())
|
||||
err.recordAltDecl(alt)
|
||||
check.report(&err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (check *Checker) tag(t *syntax.BasicLit) string {
|
||||
// If t.Bad, an error was reported during parsing.
|
||||
if t != nil && !t.Bad {
|
||||
if t.Kind == syntax.StringLit {
|
||||
if val, err := strconv.Unquote(t.Value); err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ptrBase(x *syntax.Operation) syntax.Expr {
|
||||
if x.Op == syntax.Mul && x.Y == nil {
|
||||
return x.X
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -800,158 +799,3 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 sy
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
|
||||
if alt := oset.insert(obj); alt != nil {
|
||||
var err error_
|
||||
err.errorf(pos, "%s redeclared", obj.Name())
|
||||
err.recordAltDecl(alt)
|
||||
check.report(&err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (check *Checker) tag(t *syntax.BasicLit) string {
|
||||
// If t.Bad, an error was reported during parsing.
|
||||
if t != nil && !t.Bad {
|
||||
if t.Kind == syntax.StringLit {
|
||||
if val, err := strconv.Unquote(t.Value); err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
|
||||
if e.FieldList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// struct fields and tags
|
||||
var fields []*Var
|
||||
var tags []string
|
||||
|
||||
// for double-declaration checks
|
||||
var fset objset
|
||||
|
||||
// current field typ and tag
|
||||
var typ Type
|
||||
var tag string
|
||||
add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
|
||||
if tag != "" && tags == nil {
|
||||
tags = make([]string, len(fields))
|
||||
}
|
||||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
name := ident.Value
|
||||
fld := NewField(pos, check.pkg, name, typ, embedded)
|
||||
// spec: "Within a struct, non-blank field names must be unique."
|
||||
if name == "_" || check.declareInSet(&fset, pos, fld) {
|
||||
fields = append(fields, fld)
|
||||
check.recordDef(ident, fld)
|
||||
}
|
||||
}
|
||||
|
||||
// addInvalid adds an embedded field of invalid type to the struct for
|
||||
// fields with errors; this keeps the number of struct fields in sync
|
||||
// with the source as long as the fields are _ or have different names
|
||||
// (issue #25627).
|
||||
addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
|
||||
typ = Typ[Invalid]
|
||||
tag = ""
|
||||
add(ident, true, pos)
|
||||
}
|
||||
|
||||
var prev syntax.Expr
|
||||
for i, f := range e.FieldList {
|
||||
// Fields declared syntactically with the same type (e.g.: a, b, c T)
|
||||
// share the same type expression. Only check type if it's a new type.
|
||||
if i == 0 || f.Type != prev {
|
||||
typ = check.varType(f.Type)
|
||||
prev = f.Type
|
||||
}
|
||||
tag = ""
|
||||
if i < len(e.TagList) {
|
||||
tag = check.tag(e.TagList[i])
|
||||
}
|
||||
if f.Name != nil {
|
||||
// named field
|
||||
add(f.Name, false, f.Name.Pos())
|
||||
} else {
|
||||
// embedded field
|
||||
// spec: "An embedded type must be specified as a type name T or as a
|
||||
// pointer to a non-interface type name *T, and T itself may not be a
|
||||
// pointer type."
|
||||
pos := syntax.StartPos(f.Type)
|
||||
name := embeddedFieldIdent(f.Type)
|
||||
if name == nil {
|
||||
check.errorf(pos, "invalid embedded field type %s", f.Type)
|
||||
name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
|
||||
addInvalid(name, pos)
|
||||
continue
|
||||
}
|
||||
add(name, true, pos)
|
||||
|
||||
// Because we have a name, typ must be of the form T or *T, where T is the name
|
||||
// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
|
||||
// We must delay this check to the end because we don't want to instantiate
|
||||
// (via under(t)) a possibly incomplete type.
|
||||
embeddedTyp := typ // for closure below
|
||||
embeddedPos := pos
|
||||
check.later(func() {
|
||||
t, isPtr := deref(embeddedTyp)
|
||||
switch t := optype(t).(type) {
|
||||
case *Basic:
|
||||
if t == Typ[Invalid] {
|
||||
// error was reported before
|
||||
return
|
||||
}
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if t.kind == UnsafePointer {
|
||||
check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
|
||||
}
|
||||
case *Pointer:
|
||||
check.error(embeddedPos, "embedded field type cannot be a pointer")
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
styp.fields = fields
|
||||
styp.tags = tags
|
||||
}
|
||||
|
||||
func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
|
||||
switch e := e.(type) {
|
||||
case *syntax.Name:
|
||||
return e
|
||||
case *syntax.Operation:
|
||||
if base := ptrBase(e); base != nil {
|
||||
// *T is valid, but **T is not
|
||||
if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
|
||||
return embeddedFieldIdent(e.X)
|
||||
}
|
||||
}
|
||||
case *syntax.SelectorExpr:
|
||||
return e.Sel
|
||||
case *syntax.IndexExpr:
|
||||
return embeddedFieldIdent(e.X)
|
||||
}
|
||||
return nil // invalid embedded field
|
||||
}
|
||||
|
||||
func ptrBase(x *syntax.Operation) syntax.Expr {
|
||||
if x.Op == syntax.Mul && x.Y == nil {
|
||||
return x.X
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue