Description
Suppose we have a lazily initialized constant of a reference type:
FOO.matches?("FOO BAR") # => true
FOO = /foo \w+/i
Codegen actually produces something equivalent to the following Crystal pseudocode:
foo__const = uninitialized Regex
foo__initialized = false
def FOO
initializer = -> : Nil do
# regular assignment to a variable
# allocation happens inside `Regex.new`
foo__const = /foo \w+/i
end
__crystal_once(pointerof(foo__initialized), initializer.pointer)
foo__const
end
Since FOO
is a constant, reassignments should not be possible, which means instance_sizeof(typeof(FOO))
would never change. This suggests we could use ReferenceStorage
to elide the allocation in foo__const
:
foo__const = uninitialized ReferenceStorage(Regex)
foo__initialized = false
def FOO
initializer = -> : Nil do
Regex.unsafe_construct(pointerof(foo__const), _source: "foo \\w+", _options: Regex::CompileOptions::IGNORE_CASE)
end
__crystal_once(pointerof(foo__initialized), initializer.pointer)
pointerof(foo__const).as(Regex)
end
Already there is a problem if we try to implement this as a compiler transformation: it has to go from the literal constructor all the way down to Regex
's canonical, internal constructor. Another example using [1, 2]
:
initializer = -> : Nil do
__temp_1 = begin
# this is derived from the inlining of `Array.unsafe_build`
ary = ::Array(typeof(1, 2)).unsafe_construct(pointerof(foo__const), 2)
ary.size = 2
ary
end
__temp_2 = __temp_1.to_unsafe
__temp_2[0] = 1
__temp_2[1] = 2
__temp_1
end
With the current compiler architecture, this is extremely difficult to implement for arbitrary initializer expressions. However, if we are willing to write our initializers explicitly, and use class variables instead of constants, then we could somewhat replicate this without compiler support:
Test.foo.matches?("FOO BAR") # => true
macro inlined_const(decl, &block)
@@{{ decl.var }}__const = uninitialized ::ReferenceStorage({{ decl.type }})
@@{{ decl.var }}__initialized = false
def self.{{ decl.var }} : {{ decl.type }}
initializer = ::Proc(::Nil).new do
{{ block.args[0] }} = pointerof(@@{{ decl.var }}__const)
{{ block.body }}
end
::__crystal_once(pointerof(@@{{ decl.var }}__initialized), initializer.pointer)
pointerof(@@{{ decl.var }}__const).as({{ decl.type }})
end
end
module Test
inlined_const foo : Regex do |ptr|
Regex.unsafe_construct(ptr, _source: "foo \\w+", _options: Regex::CompileOptions::IGNORE_CASE)
end
end
Would we be interested in exposing this publicly in the standard library?