| // 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 | |
| } |