nimskull
nimskull copied to clipboard
language design - `compileTime` pragma: symbol visibility, types, and transfering procedural values
Goal
Determine a more precise meaning for the compileTime
pragma, and relevant interactions, and then make the compiler do that. As a first step document what should be true now, then we can work backwards.
Illustrative Examples
With the latest rework of compileTime
semantics (https://github.com/nim-works/nimskull/pull/580), which will be merged shortly, the following no longer works as inner with the proc
pis inferred as a
compiletimedeclaration, which cascades through
const`.
type Proc = proc()
proc p(): Proc {.compileTime.} =
proc inner() = # inferred to be compile-time only
discard
result = inner
const c = p()
let rt = c # <- fails to compile now
rt()
Here is a varint using a macro:
macro m() =
proc p() = # inferred to be compile-time only
discard
result = quote do:
`p`()
m() # the generated AST fails to compile
Current State of Understanding
Primarily using the above first example of two examples.
The present working conceptual model is:
-
compileTime
is applied to the symbol and controls visibility -
p
isreturning a procedural value
, meaning that it's not returninginner
but the valueinner
holds, inner can be thought of asconst inner: proc() = proc () = discard
NB: if a procedural value from inner
is returned from a compiletime
("chunk of code", p
) and if that returned procedural value raised, then it's a little surprising to see a runtime trace from ostensibly "compiletime" section of code. This is considered an entirely acceptable tradeoff, because disallowing conversion of compiletime procedural values to runtime would be a huge miss in terms of providing tools that do type or 'closed' data driven definitional emit, which is yet another tool to employ prior to resorting to macros.
Related conceptual items:
-
NimNode
parameters have special handling which promote routines takingNimNode
args tocompiletime
- this is done at the symbol level, but should probably be at the type level or both?
Thought exercises:
- special case
NimNode
has a stage restriction (comptime
) and cascade affect (promotes the proc symbol tocompiletime
)- here the type
NimNode
iscompiletime
(not the symbol, because that would mean aliases or distincts could break this)
- here the type
- referring to symbols in the body of
p
from withininner
would mean it closes over them:- if those are definitely
compiletime
symbols, then does that mean we snapshot the closed values and proceed?- would this snapshot? a must for
var
locals and/orconst
/let
locals with defs that vary per invocation
- would this snapshot? a must for
- or, promote the procedural value returned to
compiletime
?
- if those are definitely
- for
compiletime
types (NimNode
) passed into generic parameter positions, how does that impactcompileTime
-ness of things?
Thanks to @zerbina for the example, and @zerbina and @Clyybber for the debate thus far.