Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 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 noder |
| 6 | |
| 7 | import ( |
| 8 | "go/constant" |
| 9 | |
| 10 | "cmd/compile/internal/base" |
| 11 | "cmd/compile/internal/syntax" |
| 12 | "cmd/compile/internal/types" |
| 13 | "cmd/compile/internal/types2" |
| 14 | ) |
| 15 | |
| 16 | // match reports whether types t1 and t2 are consistent |
| 17 | // representations for a given expression's type. |
| 18 | func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool { |
| 19 | tuple, ok := t2.(*types2.Tuple) |
| 20 | if !ok { |
| 21 | // Not a tuple; can use simple type identity comparison. |
| 22 | return types.Identical(t1, g.typ(t2)) |
| 23 | } |
| 24 | |
| 25 | if hasOK { |
| 26 | // For has-ok values, types2 represents the expression's type as a |
| 27 | // 2-element tuple, whereas ir just uses the first type and infers |
| 28 | // that the second type is boolean. Must match either, since we |
| 29 | // sometimes delay the transformation to the ir form. |
| 30 | if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) { |
| 31 | return true |
| 32 | } |
| 33 | return types.Identical(t1, g.typ(t2)) |
| 34 | } |
| 35 | |
| 36 | if t1 == nil || tuple == nil { |
| 37 | return t1 == nil && tuple == nil |
| 38 | } |
| 39 | if !t1.IsFuncArgStruct() { |
| 40 | return false |
| 41 | } |
| 42 | if t1.NumFields() != tuple.Len() { |
| 43 | return false |
| 44 | } |
| 45 | for i, result := range t1.FieldSlice() { |
| 46 | if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) { |
| 47 | return false |
| 48 | } |
| 49 | } |
| 50 | return true |
| 51 | } |
| 52 | |
| 53 | func (g *irgen) validate(n syntax.Node) { |
| 54 | switch n := n.(type) { |
| 55 | case *syntax.CallExpr: |
| 56 | tv := g.info.Types[n.Fun] |
| 57 | if tv.IsBuiltin() { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 58 | fun := n.Fun |
| 59 | for { |
| 60 | builtin, ok := fun.(*syntax.ParenExpr) |
| 61 | if !ok { |
| 62 | break |
| 63 | } |
| 64 | fun = builtin.X |
| 65 | } |
| 66 | switch builtin := fun.(type) { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 67 | case *syntax.Name: |
| 68 | g.validateBuiltin(builtin.Value, n) |
| 69 | case *syntax.SelectorExpr: |
| 70 | g.validateBuiltin(builtin.Sel.Value, n) |
| 71 | default: |
| 72 | g.unhandled("builtin", n) |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) { |
| 79 | switch name { |
| 80 | case "Alignof", "Offsetof", "Sizeof": |
| 81 | // Check that types2+gcSizes calculates sizes the same |
| 82 | // as cmd/compile does. |
| 83 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 84 | tv := g.info.Types[call] |
| 85 | if !tv.IsValue() { |
| 86 | base.FatalfAt(g.pos(call), "expected a value") |
| 87 | } |
| 88 | |
| 89 | if tv.Value == nil { |
| 90 | break // unsafe op is not a constant, so no further validation |
| 91 | } |
| 92 | |
| 93 | got, ok := constant.Int64Val(tv.Value) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 94 | if !ok { |
| 95 | base.FatalfAt(g.pos(call), "expected int64 constant value") |
| 96 | } |
| 97 | |
| 98 | want := g.unsafeExpr(name, call.ArgList[0]) |
| 99 | if got != want { |
| 100 | base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want) |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // unsafeExpr evaluates the given unsafe builtin function on arg. |
| 106 | func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 { |
| 107 | switch name { |
| 108 | case "Alignof": |
| 109 | return g.typ(g.info.Types[arg].Type).Alignment() |
| 110 | case "Sizeof": |
| 111 | return g.typ(g.info.Types[arg].Type).Size() |
| 112 | } |
| 113 | |
| 114 | // Offsetof |
| 115 | |
| 116 | sel := arg.(*syntax.SelectorExpr) |
| 117 | selection := g.info.Selections[sel] |
| 118 | |
| 119 | typ := g.typ(g.info.Types[sel.X].Type) |
| 120 | typ = deref(typ) |
| 121 | |
| 122 | var offset int64 |
| 123 | for _, i := range selection.Index() { |
| 124 | // Ensure field offsets have been calculated. |
| 125 | types.CalcSize(typ) |
| 126 | |
| 127 | f := typ.Field(i) |
| 128 | offset += f.Offset |
| 129 | typ = f.Type |
| 130 | } |
| 131 | return offset |
| 132 | } |