aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/validate.go
blob: dcacae7480c2975c50182aeb72d46d31e374d4f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
}