summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/noder/validate.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/validate.go')
-rw-r--r--src/cmd/compile/internal/noder/validate.go132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go
new file mode 100644
index 0000000..dcacae7
--- /dev/null
+++ b/src/cmd/compile/internal/noder/validate.go
@@ -0,0 +1,132 @@
+// 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 noder
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// match reports whether types t1 and t2 are consistent
+// representations for a given expression's type.
+func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
+ tuple, ok := t2.(*types2.Tuple)
+ if !ok {
+ // Not a tuple; can use simple type identity comparison.
+ return types.Identical(t1, g.typ(t2))
+ }
+
+ if hasOK {
+ // For has-ok values, types2 represents the expression's type as a
+ // 2-element tuple, whereas ir just uses the first type and infers
+ // that the second type is boolean. Must match either, since we
+ // sometimes delay the transformation to the ir form.
+ if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) {
+ return true
+ }
+ return types.Identical(t1, g.typ(t2))
+ }
+
+ if t1 == nil || tuple == nil {
+ return t1 == nil && tuple == nil
+ }
+ if !t1.IsFuncArgStruct() {
+ return false
+ }
+ if t1.NumFields() != tuple.Len() {
+ return false
+ }
+ for i, result := range t1.FieldSlice() {
+ if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) {
+ return false
+ }
+ }
+ return true
+}
+
+func (g *irgen) validate(n syntax.Node) {
+ switch n := n.(type) {
+ case *syntax.CallExpr:
+ tv := g.info.Types[n.Fun]
+ if tv.IsBuiltin() {
+ fun := n.Fun
+ for {
+ builtin, ok := fun.(*syntax.ParenExpr)
+ if !ok {
+ break
+ }
+ fun = builtin.X
+ }
+ switch builtin := fun.(type) {
+ case *syntax.Name:
+ g.validateBuiltin(builtin.Value, n)
+ case *syntax.SelectorExpr:
+ g.validateBuiltin(builtin.Sel.Value, n)
+ default:
+ g.unhandled("builtin", n)
+ }
+ }
+ }
+}
+
+func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
+ switch name {
+ case "Alignof", "Offsetof", "Sizeof":
+ // Check that types2+gcSizes calculates sizes the same
+ // as cmd/compile does.
+
+ tv := g.info.Types[call]
+ if !tv.IsValue() {
+ base.FatalfAt(g.pos(call), "expected a value")
+ }
+
+ if tv.Value == nil {
+ break // unsafe op is not a constant, so no further validation
+ }
+
+ got, ok := constant.Int64Val(tv.Value)
+ if !ok {
+ base.FatalfAt(g.pos(call), "expected int64 constant value")
+ }
+
+ want := g.unsafeExpr(name, call.ArgList[0])
+ if got != want {
+ base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want)
+ }
+ }
+}
+
+// unsafeExpr evaluates the given unsafe builtin function on arg.
+func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 {
+ switch name {
+ case "Alignof":
+ return g.typ(g.info.Types[arg].Type).Alignment()
+ case "Sizeof":
+ return g.typ(g.info.Types[arg].Type).Size()
+ }
+
+ // Offsetof
+
+ sel := arg.(*syntax.SelectorExpr)
+ selection := g.info.Selections[sel]
+
+ typ := g.typ(g.info.Types[sel.X].Type)
+ typ = deref(typ)
+
+ var offset int64
+ for _, i := range selection.Index() {
+ // Ensure field offsets have been calculated.
+ types.CalcSize(typ)
+
+ f := typ.Field(i)
+ offset += f.Offset
+ typ = f.Type
+ }
+ return offset
+}