RFR: 8367531: Template Framework: use scopes and tokens instead of misbehaving immediate-return-queries [v29]
Roberto Castañeda Lozano
rcastanedalo at openjdk.org
Fri Nov 14 14:39:57 UTC 2025
On Fri, 14 Nov 2025 14:16:06 GMT, Roberto Castañeda Lozano <rcastanedalo at openjdk.org> wrote:
>>> Hi Emanuel, thanks for improving the design of the template framework, the enforcement of "everything is a token" and the introduction of explicit scope constraints seem like a step in the right direction. Before I go on with the review, I would like to ask two high-level questions (apologies if these are already discussed, it is hard to browse through a PR history):
>>>
>>> * The tutorial and the Template documentation remark that we would ideally have used string templates rather than hashtag replacements. Is this still true after the introduction of explicit scoping constraints, i.e. could we still simply use string templates and still enforce the user-provided scoping rules if the feature was available?
>>
>> Yes, I think so. We would probably get rid of `let` and just use local variables in lambdas, and then format them directly into strings. Scopes would be colocated with lambdas, so that local variables could be local to the scopes. I'm less sure about letting local variables (instead of hashtags) escape lambdas .. that's not really possible. But maybe there would be work-arounds.
>>
>>> * If I got the comments in the tutorial right, it seems that the user has good control over the "transparency level" of scopes, while the transparency rules for templates are hardcoded (hashtag replacements never escape, DataNames always escape, etc.). This felt a bit surprising, would it be feasible to just let the outermost scope in a template determine the template's transparency level?
>>
>> The understanding seems to maybe be incomplete:
>>
>>> transparency rules for templates are hardcoded
>>
>> It is only "hardcoded" to never let hashtags and setFuelCost escape, it just implicitly downgrades a scope on those two "dimensions".
>>
>> But the user still has control over the name "dimension":
>> It still needs to be possible to decide if `DataName`s and `StructuralName`s escape the Template scope, otherwise they cannot escape from a `Hook.insert`.
>>
>> Maybe it would be nice to force the user to use only scopes that exactly match the semantics of the implementation (only allow control if names are transparent or not). So maybe one could only use `scope` (completely non-transparent) or `scopeWithNameTransparency` (only transparent to names), and that would somehow be enforced by the Java types/interfaces of the framework. Or maybe we just throw an Exception if the wrong one is used. But it would require us to define this extra `scopeWithNameTransparency`. Do you think...
>
>> It is only "hardcoded" to never let hashtags and setFuelCost escape, it just implicitly downgrades a scope on those two "dimensions".
>
> Is this a design choice or a constraint of the current implementation? I could imagine situations in which it could be useful to let a hashtag escape across Template boundaries, no? Something like:
>
>
> var innerTemplate = Template.make(() -> transparentScope(let("foo", "42")));
> var outerTemplate = Template.make(() -> scope(
> innerTemplate.asToken(),
> "// value of foo: #foo"
> ));
> outerTemplate.render();
> @robcasloz
>
> > Thanks for the explanation. I'm still failing to see how one would combine string templates and explicit scoping, but it is good enough for me at this point if you have a clear plan of how it could be done.
>
> Right, it is all quite hypothetical anyway, and I don't think quite relevant to this PR.
>
> But let me still try to explain with some quick example, how it may look like with string template:
>
> ```
> var template = Template.make((String arg1, Integer arg2) -> scope(
> // Note: no hashtag definition of arg1 and arg2 any more.
> // Note: arg1 and arg2 are essecially colocated with the scope of the Template above.
> // Now let's use arg1 and arg2 in some templated string:
> f"testing {arg1} testing {arg2 + 1} testing\n",
> // Above, we could inject variables and even computations into the string, and that produces our first token.
> // Now let's define some DataName, and then sample:
> addDataName("x", ...),
> dataNames(...)....sample((DataName dn) -> scope(
> // Note: dn is colocated with the scope of "sample".
> "testing {dn.name()} testing {dn.type()} testing\n"
> ))
> ));
> ```
>
> One concern would probably be that we need a way to define local variables in a scope, so that we can define one value (e.g. randomly generated), and use that exact value multiple times. Maybe that would then still require some modified version of `let`:
>
> ```
> let(RANDOM.next(), (Integer x) -> scope(
> "testing {x} testing {x} testing",
> ))
> ```
>
> Again: the `scope` would not directly interact with the string templates, but the Template Framework would ensure that they are colocated: local variables are basically only created as lambda arguments that are live for the "scope" of the lambda, which by design of the Template Framework, is colocated with the `scope`s.
>
> I hope that makes some sense and illustrates the alternative we would have with string templates - once they would be available ;)
OK, thanks for elaborating!
-------------
PR Comment: https://git.openjdk.org/jdk/pull/27255#issuecomment-3533085266
More information about the hotspot-compiler-dev
mailing list