Skip to content

Way to define multiple templates sharing ${define ..} and ${defcond }

Consider the problem posed in #35. This ticket requests item 2 in #35 (comment 3015694)

Overall scheme

The user can supply some common template pieces, which they can then reuse in multiple template definitions.

Open question - unified invocation vs named modules

In principle it might be possible to have these template pieces be exported from one place to another in the program, by embedding them in macro_rules! macros (macro_rules! derive_deftly_module_Castable) but this would involve the macro body threading trick and be quite fiddly. Also there are semver implications, because you'd be able to mix template fragments from different crates. (But that would only happen with a pub d-d module.)

Simpler would be to have the user provide all of the pieces together, in one proc_macro invocation.

In both cases the natural form for common template pieces is either a shared piece of text, or individual ${define}/${defcond}. (Henceforth, "$defines".) For reasons that will become clear, I think it ought to be $defines.

In principle we could intend to implement both, if we wanted. We probably shouldn't implement the unified invocation facility unless we are happy to have both in the future.

Syntax

The user must supply:

  • Zero or more loose $define with distinct names.
  • Zero or more template definitions

Proposed syntax (unified invocation)

ISTM that the input syntax should resemble the "normal" invocation as much as possible.

define_derive_deftly_several! {
    ${define CASTABLE {
        impl Castable ...
    }}

    define_derive_deftly! { Castable = $CASTABLE }
    define_derive_deftly! {
        Object =
        $CASTABLE
        impl Object ...
    }
}

Within the invocation define_derive_deftly isn't scoped; it's just matched by identifier precisely.

Proposed syntax (reusable modules)

define_derive_deftly_module! { Castable = ${define CASTABLE { impl ... } } }
define_derive_deftly! { Castable use Castable = $CASTABLE }
define_derive_deftly! {
    Object use Castable =
    $CASTABLE
    impl Object ...
}

Meta attr and unused ${define}/${defcond}

We want meta attr checking to be precise. If we simply prepend the $defines to each template, that template will accept even attrs tested by $defines that that template didn't use.

I propose the following semantics:

We scan each template fragment ($defines and define_derive_deftlys) for references to the shared definitions. This is done lexically. This generates a digraph, whose roots are the define_derive_deftlys.

We then prepend to each output template definition the transitive closure. So it will have precisely the $defines that it uses (or might use - again, this is done lexically).

Or to put it another way, we elide the $defines that are statically unreachable. This (statically and lexically) is how meta attr checking works. It's not detectable at expansion time because you can't refer to the $define without causing it to appear.

With module-style reuse, this must be done per define_derive_deftly!