In Nim, macros and templates have the ability to be designate their params as being typed or untyped which enables a user to optionally pass their input to the semantic stage (and produce symbol-bounded, constant-folded, etc. AST trees as a result), or avoid the semcheck if a less “Standard Nim” DSL is being opted for. That it is all possible to do this at all is a very useful error diagnostic tool in the face of highly transformative macros where basic errors could potentially turn into compiler gobbledygook.
I’m not sure if it’s been investigated at all in the past, but I’m curious why there isn’t a way to arbitrarily semcheck an AST from a compile time VM context (in a way that isn’t just returning a NimNode from a macro) in circumstances which typed parameter qualifiers simply cannot be leveraged properly. Is it due to issues pertaining to the invocation context? Lack of interest?
I'm not sure I understand your question but bestEffortTyped has been in mind since quite some time now. I think I never wrote an RFC though. The basic idea is that typed is all we need if typed means bestEfforTyped that leaves unresolved identifiers as nkIdent and other errors produce an nkError node. Then the macro usually keeps nkError nodes during its transformations (or it can transform errors back into valid Nim code) and it all works out.
In theory.
The benefits would be a simpler macro system which almost always produces macros that compose cleanly.
To be more specific with what I mean: the semcheck pass performed with typed only works with code blocks produced directly from the source. There is no proc typify(node: NimNode): NimNode. If an AST is produced either from the compile time VM or (probably more usefully) if there are specific edge cases from the source where part of the AST should be semchecked but it is difficult to necessarily delineate the two, e.g.
cppclass Foo of Bar: # supposing `cppclass` is a macro w/ untyped params
virtual proc baz(x: Qux) =
let x = blahblah()
let y = haha()
discard
it would be useful to programatically select which nodes we would like to "typify." I see though that in the case you provided, non-fatal semchecking might also be a different approach to the problem of "partially semantically-incorrect source" although not necessarily for VM-produced nodes.
There is no proc typify(node: NimNode): NimNode.
Ah, that's what you mean. Well you cannot typify without a scope so within a macro the typify result is pretty unpredictable. What are the use cases?
I just recently faced the same problem. My solution was to do a kind of two stage setup, where I have a untyped macro that produces a call to a typed macro. Something like this:
macro typedStage(a: static SomeType, b: static SomeOtherType, body: typed) =
produceFinalAst
macro untypedStage(body: untyped) =
...
let a = ...
let b = ...
let newBody: NimNode = ...
quote do:
typedStage(`a`, `b`, `newBody`)
What are the use cases?
There are four main things I can think of:
I just submitted a stackoverflow question on this topic, https://stackoverflow.com/questions/76072831/how-can-you-get-the-sizeof-an-untyped-type-identifier-inside-a-macro
I edited @PMunch's answer with how I ended up implementing it, but my edit wasn't accepted. It was essentially @choltreppe s answer of emitting a call to a secondary typed macro.
My use case was a bitfield helper library I'm working on, I wanted the cleanest syntax that will pass the parser, but later to statically verify type sizes against that input to be able to produce helpful error messages.