Source file src/cmd/compile/internal/types2/struct.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types2
     6  
     7  import (
     8  	"cmd/compile/internal/syntax"
     9  	"strconv"
    10  )
    11  
    12  // ----------------------------------------------------------------------------
    13  // API
    14  
    15  // A Struct represents a struct type.
    16  type Struct struct {
    17  	fields []*Var   // fields != nil indicates the struct is set up (possibly with len(fields) == 0)
    18  	tags   []string // field tags; nil if there are no tags
    19  }
    20  
    21  // NewStruct returns a new struct with the given fields and corresponding field tags.
    22  // If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
    23  // only as long as required to hold the tag with the largest index i. Consequently,
    24  // if no field has a tag, tags may be nil.
    25  func NewStruct(fields []*Var, tags []string) *Struct {
    26  	var fset objset
    27  	for _, f := range fields {
    28  		if f.name != "_" && fset.insert(f) != nil {
    29  			panic("multiple fields with the same name")
    30  		}
    31  	}
    32  	if len(tags) > len(fields) {
    33  		panic("more tags than fields")
    34  	}
    35  	s := &Struct{fields: fields, tags: tags}
    36  	s.markComplete()
    37  	return s
    38  }
    39  
    40  // NumFields returns the number of fields in the struct (including blank and embedded fields).
    41  func (s *Struct) NumFields() int { return len(s.fields) }
    42  
    43  // Field returns the i'th field for 0 <= i < NumFields().
    44  func (s *Struct) Field(i int) *Var { return s.fields[i] }
    45  
    46  // Tag returns the i'th field tag for 0 <= i < NumFields().
    47  func (s *Struct) Tag(i int) string {
    48  	if i < len(s.tags) {
    49  		return s.tags[i]
    50  	}
    51  	return ""
    52  }
    53  
    54  func (s *Struct) Underlying() Type { return s }
    55  func (s *Struct) String() string   { return TypeString(s, nil) }
    56  
    57  // ----------------------------------------------------------------------------
    58  // Implementation
    59  
    60  func (s *Struct) markComplete() {
    61  	if s.fields == nil {
    62  		s.fields = make([]*Var, 0)
    63  	}
    64  }
    65  
    66  func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
    67  	if e.FieldList == nil {
    68  		styp.markComplete()
    69  		return
    70  	}
    71  
    72  	// struct fields and tags
    73  	var fields []*Var
    74  	var tags []string
    75  
    76  	// for double-declaration checks
    77  	var fset objset
    78  
    79  	// current field typ and tag
    80  	var typ Type
    81  	var tag string
    82  	add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
    83  		if tag != "" && tags == nil {
    84  			tags = make([]string, len(fields))
    85  		}
    86  		if tags != nil {
    87  			tags = append(tags, tag)
    88  		}
    89  
    90  		name := ident.Value
    91  		fld := NewField(pos, check.pkg, name, typ, embedded)
    92  		// spec: "Within a struct, non-blank field names must be unique."
    93  		if name == "_" || check.declareInSet(&fset, pos, fld) {
    94  			fields = append(fields, fld)
    95  			check.recordDef(ident, fld)
    96  		}
    97  	}
    98  
    99  	// addInvalid adds an embedded field of invalid type to the struct for
   100  	// fields with errors; this keeps the number of struct fields in sync
   101  	// with the source as long as the fields are _ or have different names
   102  	// (issue #25627).
   103  	addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
   104  		typ = Typ[Invalid]
   105  		tag = ""
   106  		add(ident, true, pos)
   107  	}
   108  
   109  	var prev syntax.Expr
   110  	for i, f := range e.FieldList {
   111  		// Fields declared syntactically with the same type (e.g.: a, b, c T)
   112  		// share the same type expression. Only check type if it's a new type.
   113  		if i == 0 || f.Type != prev {
   114  			typ = check.varType(f.Type)
   115  			prev = f.Type
   116  		}
   117  		tag = ""
   118  		if i < len(e.TagList) {
   119  			tag = check.tag(e.TagList[i])
   120  		}
   121  		if f.Name != nil {
   122  			// named field
   123  			add(f.Name, false, f.Name.Pos())
   124  		} else {
   125  			// embedded field
   126  			// spec: "An embedded type must be specified as a type name T or as a
   127  			// pointer to a non-interface type name *T, and T itself may not be a
   128  			// pointer type."
   129  			pos := syntax.StartPos(f.Type)
   130  			name := embeddedFieldIdent(f.Type)
   131  			if name == nil {
   132  				check.errorf(pos, "invalid embedded field type %s", f.Type)
   133  				name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
   134  				addInvalid(name, pos)
   135  				continue
   136  			}
   137  			add(name, true, pos)
   138  
   139  			// Because we have a name, typ must be of the form T or *T, where T is the name
   140  			// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
   141  			// We must delay this check to the end because we don't want to instantiate
   142  			// (via under(t)) a possibly incomplete type.
   143  			embeddedTyp := typ // for closure below
   144  			embeddedPos := pos
   145  			check.later(func() {
   146  				t, isPtr := deref(embeddedTyp)
   147  				switch u := under(t).(type) {
   148  				case *Basic:
   149  					if t == Typ[Invalid] {
   150  						// error was reported before
   151  						return
   152  					}
   153  					// unsafe.Pointer is treated like a regular pointer
   154  					if u.kind == UnsafePointer {
   155  						check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
   156  					}
   157  				case *Pointer:
   158  					check.error(embeddedPos, "embedded field type cannot be a pointer")
   159  				case *Interface:
   160  					if isTypeParam(t) {
   161  						check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter")
   162  						break
   163  					}
   164  					if isPtr {
   165  						check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
   166  					}
   167  				}
   168  			}).describef(embeddedPos, "check embedded type %s", embeddedTyp)
   169  		}
   170  	}
   171  
   172  	styp.fields = fields
   173  	styp.tags = tags
   174  	styp.markComplete()
   175  }
   176  
   177  func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
   178  	switch e := e.(type) {
   179  	case *syntax.Name:
   180  		return e
   181  	case *syntax.Operation:
   182  		if base := ptrBase(e); base != nil {
   183  			// *T is valid, but **T is not
   184  			if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
   185  				return embeddedFieldIdent(e.X)
   186  			}
   187  		}
   188  	case *syntax.SelectorExpr:
   189  		return e.Sel
   190  	case *syntax.IndexExpr:
   191  		return embeddedFieldIdent(e.X)
   192  	}
   193  	return nil // invalid embedded field
   194  }
   195  
   196  func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
   197  	if alt := oset.insert(obj); alt != nil {
   198  		var err error_
   199  		err.errorf(pos, "%s redeclared", obj.Name())
   200  		err.recordAltDecl(alt)
   201  		check.report(&err)
   202  		return false
   203  	}
   204  	return true
   205  }
   206  
   207  func (check *Checker) tag(t *syntax.BasicLit) string {
   208  	// If t.Bad, an error was reported during parsing.
   209  	if t != nil && !t.Bad {
   210  		if t.Kind == syntax.StringLit {
   211  			if val, err := strconv.Unquote(t.Value); err == nil {
   212  				return val
   213  			}
   214  		}
   215  		check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
   216  	}
   217  	return ""
   218  }
   219  
   220  func ptrBase(x *syntax.Operation) syntax.Expr {
   221  	if x.Op == syntax.Mul && x.Y == nil {
   222  		return x.X
   223  	}
   224  	return nil
   225  }
   226  

View as plain text