blob: dcacae7480c2975c50182aeb72d46d31e374d4f4 [file] [log] [blame]
// 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
}