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