/
types.cr
223 lines (199 loc) · 5.81 KB
/
types.cr
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
module Crystal
class Type
# Returns `true` if this type is passed as a `self` argument
# in the codegen phase. For example a method whose receiver is
# the Program, or a Metaclass, doesn't have a `self` argument.
def passed_as_self?
case self
when Program, FileModule, LibType, MetaclassType
false
else
true
end
end
# Returns `true` if this type passed by value (if it's not a primitive type).
# In the codegen phase these types are passed as byval pointers.
def passed_by_value?
case self
when PrimitiveType, PointerInstanceType, ProcInstanceType
false
when TupleInstanceType, NamedTupleInstanceType, MixedUnionType
true
when VirtualType
self.struct?
when NonGenericModuleType
self.including_types.try &.passed_by_value?
when GenericModuleInstanceType
self.including_types.try &.passed_by_value?
when GenericClassInstanceType
self.generic_type.passed_by_value?
when TypeDefType
self.typedef.passed_by_value?
when AliasType
self.aliased_type.passed_by_value?
when ClassType
self.struct?
else
false
end
end
# Returns `true` if the type has inner pointers.
# This is useful to know because if a type doesn't have
# inner pointers we can use `malloc_atomic` instead of
# `malloc` in `Pointer.malloc` for a tiny performance boost.
def has_inner_pointers?
case self
when .void?
# We consider Void to have pointers, so doing
# Pointer(Void).malloc(...).as(ReferenceType)
# will consider potential inner pointers as such.
true
when PointerInstanceType
true
when ProcInstanceType
# A proc can have closure data which might have pointers
true
when StaticArrayInstanceType
self.element_type.has_inner_pointers?
when TupleInstanceType
self.tuple_types.any? &.has_inner_pointers?
when NamedTupleInstanceType
self.entries.any? &.type.has_inner_pointers?
when PrimitiveType
false
when EnumType
false
when UnionType
self.union_types.any? &.has_inner_pointers?
when AliasType
self.aliased_type.has_inner_pointers?
when TypeDefType
self.typedef.has_inner_pointers?
when VirtualType
if struct?
self.subtypes.any? &.has_inner_pointers?
else
true
end
when InstanceVarContainer
if struct?
all_instance_vars.each_value.any? &.type.has_inner_pointers?
else
true
end
else
true
end
end
def llvm_name
String.build do |io|
llvm_name io
end
end
def llvm_name(io)
to_s_with_options io, codegen: true
end
def append_to_expand_union_types(types)
types << self
end
end
class PrimitiveType
def llvm_name(io)
io << name
end
end
class AliasType
def llvm_name(io)
io << "alias."
to_s_with_options io, codegen: true
end
end
class NonGenericClassType
def llvm_name(io)
if extern?
io << (extern_union? ? "union" : "struct")
io << '.'
end
to_s_with_options io, codegen: true
end
end
class NonGenericModuleType
def append_to_expand_union_types(types)
if including_types = @including_types
including_types.each &.virtual_type.append_to_expand_union_types(types)
else
types << self
end
end
end
class GenericModuleInstanceType
def append_to_expand_union_types(types)
if including_types = @including_types
including_types.each &.virtual_type.append_to_expand_union_types(types)
else
types << self
end
end
end
class UnionType
def expand_union_types
if union_types.any?(&.is_a?(NonGenericModuleType))
types = [] of Type
union_types.each &.append_to_expand_union_types(types)
types
else
union_types
end
end
end
class TypeDefType
def llvm_name(io)
typedef.llvm_name(io)
end
end
class Const
property initializer : LLVM::Value?
# Was this constant already read during the codegen phase?
# If not, and we are at the place that declares the constant, we can
# directly initialize the constant now, without checking for an `init` flag.
property? read = false
# If true, there's no need to check whether the constant was initialized or
# not when reading it.
property? no_init_flag = false
def initialized_llvm_name
"#{llvm_name}:init"
end
# Returns `true` if this constant's value is a simple literal, like
# `nil`, a number, char, string or symbol literal.
def simple?
return false if pointer_read?
value.simple_literal?
end
def needs_init_flag?
return true if pointer_read?
!(initializer || no_init_flag? || simple?)
end
@compile_time_value : (Int16 | Int32 | Int64 | Int8 | UInt16 | UInt32 | UInt64 | UInt8 | Bool | Char | Nil)
@computed_compile_time_value = false
# Returns a value if this constant's value can be evaluated at
# compile time (things like `1 + 2` and such). Returns nil otherwise.
def compile_time_value
unless @computed_compile_time_value
@computed_compile_time_value = true
case value = self.value
when BoolLiteral
@compile_time_value = value.value
when CharLiteral
@compile_time_value = value.value
else
case value.type?
when IntegerType, EnumType
interpreter = MathInterpreter.new(namespace, visitor)
@compile_time_value = interpreter.interpret(value) rescue nil
end
end
end
@compile_time_value
end
end
end