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
5package noder
6
7import (
8	"go/constant"
9
10	"cmd/compile/internal/ir"
11	"cmd/compile/internal/syntax"
12	"cmd/compile/internal/typecheck"
13	"cmd/compile/internal/types"
14	"cmd/compile/internal/types2"
15	"cmd/internal/src"
16)
17
18// Helpers for constructing typed IR nodes.
19//
20// TODO(mdempsky): Move into their own package so they can be easily
21// reused by iimport and frontend optimizations.
22
23type ImplicitNode interface {
24	ir.Node
25	SetImplicit(x bool)
26}
27
28// Implicit returns n after marking it as Implicit.
29func Implicit(n ImplicitNode) ImplicitNode {
30	n.SetImplicit(true)
31	return n
32}
33
34// typed returns n after setting its type to typ.
35func typed(typ *types.Type, n ir.Node) ir.Node {
36	n.SetType(typ)
37	n.SetTypecheck(1)
38	return n
39}
40
41// Values
42
43// FixValue returns val after converting and truncating it as
44// appropriate for typ.
45func FixValue(typ *types.Type, val constant.Value) constant.Value {
46	assert(typ.Kind() != types.TFORW)
47	switch {
48	case typ.IsInteger():
49		val = constant.ToInt(val)
50	case typ.IsFloat():
51		val = constant.ToFloat(val)
52	case typ.IsComplex():
53		val = constant.ToComplex(val)
54	}
55	if !typ.IsUntyped() {
56		val = typecheck.ConvertVal(val, typ, false)
57	}
58	ir.AssertValidTypeForConst(typ, val)
59	return val
60}
61
62// Expressions
63
64func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
65	n := typecheck.NodAddrAt(pos, x)
66	typed(types.NewPtr(x.Type()), n)
67	return n
68}
69
70func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr {
71	n := ir.NewStarExpr(pos, x)
72	typed(typ, n)
73	return n
74}
75
76// Statements
77
78func idealType(tv syntax.TypeAndValue) types2.Type {
79	// The gc backend expects all expressions to have a concrete type, and
80	// types2 mostly satisfies this expectation already. But there are a few
81	// cases where the Go spec doesn't require converting to concrete type,
82	// and so types2 leaves them untyped. So we need to fix those up here.
83	typ := types2.Unalias(tv.Type)
84	if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
85		switch basic.Kind() {
86		case types2.UntypedNil:
87			// ok; can appear in type switch case clauses
88			// TODO(mdempsky): Handle as part of type switches instead?
89		case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
90			typ = types2.Typ[types2.Uint]
91			if tv.Value != nil {
92				s := constant.ToInt(tv.Value)
93				assert(s.Kind() == constant.Int)
94				if constant.Sign(s) < 0 {
95					typ = types2.Typ[types2.Int]
96				}
97			}
98		case types2.UntypedBool:
99			typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
100		case types2.UntypedString:
101			typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
102		case types2.UntypedRune:
103			typ = types2.Typ[types2.Int32] // range over rune
104		default:
105			return nil
106		}
107	}
108	return typ
109}
110
111func isTypeParam(t types2.Type) bool {
112	_, ok := types2.Unalias(t).(*types2.TypeParam)
113	return ok
114}
115
116// isNotInHeap reports whether typ is or contains an element of type
117// runtime/internal/sys.NotInHeap.
118func isNotInHeap(typ types2.Type) bool {
119	typ = types2.Unalias(typ)
120	if named, ok := typ.(*types2.Named); ok {
121		if obj := named.Obj(); obj.Name() == "nih" && obj.Pkg().Path() == "runtime/internal/sys" {
122			return true
123		}
124		typ = named.Underlying()
125	}
126
127	switch typ := typ.(type) {
128	case *types2.Array:
129		return isNotInHeap(typ.Elem())
130	case *types2.Struct:
131		for i := 0; i < typ.NumFields(); i++ {
132			if isNotInHeap(typ.Field(i).Type()) {
133				return true
134			}
135		}
136		return false
137	default:
138		return false
139	}
140}
141