From 61d789db3a52e4570596f1fd15122358deb73b77 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 4 Nov 2021 21:31:08 -0700 Subject: [PATCH] cmd/compile/internal/types2: report error for incomplete struct composite literal type Mark a struct as "complete" with a non-nil (but possibly zero length) fields list. Add a test when type-checking struct composite literals, the same way we do for other composite literal types. Fixes #49276. Change-Id: If44a3d790bf7032ddcd155af49bdc47b1cdff4fc Reviewed-on: https://go-review.googlesource.com/c/go/+/361412 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/expr.go | 6 +++ src/cmd/compile/internal/types2/struct.go | 14 +++++- src/cmd/compile/internal/types2/subst.go | 4 +- .../types2/testdata/fixedbugs/issue49276.go | 46 +++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 95b96f2334..d618ebd372 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -1260,6 +1260,12 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin switch utyp := structure(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, "illegal cycle in type declaration") + goto Error + } if len(e.ElemList) == 0 { break } diff --git a/src/cmd/compile/internal/types2/struct.go b/src/cmd/compile/internal/types2/struct.go index 933d7ef947..8c39f5e3c4 100644 --- a/src/cmd/compile/internal/types2/struct.go +++ b/src/cmd/compile/internal/types2/struct.go @@ -14,7 +14,7 @@ import ( // A Struct represents a struct type. type Struct struct { - fields []*Var + fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0) tags []string // field tags; nil if there are no tags } @@ -32,7 +32,9 @@ func NewStruct(fields []*Var, tags []string) *Struct { if len(tags) > len(fields) { panic("more tags than fields") } - return &Struct{fields: fields, tags: tags} + s := &Struct{fields: fields, tags: tags} + s.markComplete() + return s } // NumFields returns the number of fields in the struct (including blank and embedded fields). @@ -55,8 +57,15 @@ func (s *Struct) String() string { return TypeString(s, nil) } // ---------------------------------------------------------------------------- // Implementation +func (s *Struct) markComplete() { + if s.fields == nil { + s.fields = make([]*Var, 0) + } +} + func (check *Checker) structType(styp *Struct, e *syntax.StructType) { if e.FieldList == nil { + styp.markComplete() return } @@ -160,6 +169,7 @@ func (check *Checker) structType(styp *Struct, e *syntax.StructType) { styp.fields = fields styp.tags = tags + styp.markComplete() } func embeddedFieldIdent(e syntax.Expr) *syntax.Name { diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 269b284ac4..a4e46b2097 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -91,7 +91,9 @@ func (subst *subster) typ(typ Type) Type { case *Struct: if fields, copied := subst.varList(t.fields); copied { - return &Struct{fields: fields, tags: t.tags} + s := &Struct{fields: fields, tags: t.tags} + s.markComplete() + return s } case *Pointer: diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go new file mode 100644 index 0000000000..8839087b50 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go @@ -0,0 +1,46 @@ +// 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 p + +import "unsafe" + +type S /* ERROR illegal cycle in declaration of S */ struct { + _ [unsafe.Sizeof(s)]byte +} + +var s S + +// Since f is a pointer, this case could be valid. +// But it's pathological and not worth the expense. +type T struct { + f *[unsafe.Sizeof(T /* ERROR illegal cycle in type declaration */ {})]int +} + +// a mutually recursive case using unsafe.Sizeof +type ( + A1 struct { + _ [unsafe.Sizeof(B1{})]int + } + + B1 struct { + _ [unsafe.Sizeof(A1 /* ERROR illegal cycle in type declaration */ {})]int + } +) + +// a mutually recursive case using len +type ( + A2 struct { + f [len(B2{}.f)]int + } + + B2 struct { + f [len(A2 /* ERROR illegal cycle in type declaration */ {}.f)]int + } +) + +// test case from issue +type a struct { + _ [42 - unsafe.Sizeof(a /* ERROR illegal cycle in type declaration */ {})]byte +} -- 2.50.0