Skip to content

Commit e536d54

Browse files
committed
Document the use of "base methods" to resolve hierarchy conflicts
This is *much* more realistic and better use of TQMIC:s than those I originally thought of, and the one pattern that have actually ended up being used. It's high time we document it proper. Thanks to Gustav for coming up with it!
1 parent f095d46 commit e536d54

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

doc/1.4/language.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3399,21 +3399,49 @@ are instantiated, as long as all conflicting implementations are overridable,
33993399
and one of the following is true:
34003400
* The implementations can be combined together by calling each one of them, as
34013401
long as that can be done without risking e.g. side-effects being duplicated.
3402+
* The implementations in all but (optionally) one of the involved templates call
3403+
to a "base" method that would serve the same purpose as `default`, but whose
3404+
definition may be *overridden* such that the conflicting implementations can
3405+
be resolved by overriding these base methods and leveraging template-qualified
3406+
method implementation calls in order to define the chain of how each
3407+
implementation gets called.
3408+
3409+
This requires that the names of the base methods are unique to each template
3410+
involved, and also some thought as to how the implementations in the chain
3411+
should be ordered.
3412+
3413+
If the implementations in some templates call `default` instead of an
3414+
overridable base method, consider modifying those templates (if possible) to
3415+
each use a base method instead — a template-qualified
3416+
method implementation call can be used as the default implementation of each
3417+
base method in order to make it act like `default` in the typical case where
3418+
no conflicting templates are in play.
34023419
* The implementations can be combined by choosing one particular template's
34033420
implementation to invoke (typically the one most complex), and then adding
34043421
code around that implementation call in order to replicate the behaviour of
34053422
the implementations of the other templates. Ideally, the other templates would
34063423
provide methods that may be leveraged so that their behaviour may be
34073424
replicated without the need for excessive boilerplate.
34083425

3426+
These cases are ordered by difficulty to resolve in ascending order. In
3427+
practice, most conflicts between unrelated templates can be resolved by
3428+
modifying the templates such that the second case may apply.
3429+
3430+
> [!NOTE]
3431+
> The examples given below apply regardless of whether the method
3432+
> implementations in the original templates are `shared` or not. However, the
3433+
> implementations in the template defined to resolve the conflicts may need to
3434+
> be non-`shared` if some of the implementations involved are not `shared`;
3435+
> see the final paragraph of this subsection.
3436+
34093437
The following is an example of the first case:
34103438
```
34113439
template alter_write is write {
34123440
method write(uint64 written) {
34133441
default(alter_write(written));
34143442
}
34153443
3416-
method alter_write(uint64 curr, uint64 written) -> (uint64);
3444+
shared method alter_write(uint64 curr, uint64 written) -> (uint64);
34173445
}
34183446
34193447
template gated_write is alter_write {
@@ -3447,6 +3475,57 @@ in each (gated_write, write_1_clears) { is gated_write_1_clears; }
34473475

34483476
The following is an example of the second case:
34493477
```
3478+
template gated_write is write {
3479+
method write_allowed() -> (bool) default {
3480+
return true;
3481+
}
3482+
3483+
method write(uint64 val) default {
3484+
if (write_allowed()) {
3485+
base_write_of_gated_write(val); // instead of calling default()
3486+
}
3487+
}
3488+
3489+
method base_write_of_gated_write(uint64 val) default {
3490+
// If no conflicting template is in play such as to override this
3491+
// base method, then its behavior is as though the write implementation
3492+
// called default() instead
3493+
this.templates.write.write(val);
3494+
}
3495+
}
3496+
3497+
template write_1_clears is (write, get) {
3498+
method write(uint64 val) default {
3499+
default(get() & ~val);
3500+
}
3501+
}
3502+
3503+
template gated_write_1_clears is (gated_write, write_1_clears) {
3504+
method write(uint64 val) default {
3505+
// This makes gated_write the first link in the call chain...
3506+
this.templates.gated_write.write(val);
3507+
}
3508+
3509+
// And by overriding gated_write's base method, we make write_1_clears
3510+
// the second (and final) link in the chain
3511+
//
3512+
// If even more conflicting templates could be in play, then one could
3513+
// consider converting write_1_clears to also leverage a base method
3514+
// instead of calling default and extend the call chain; on the other
3515+
// hand, the way write_1_clears manipulates the written value may violate
3516+
// the expectations of implementations later on in the chain, so it might
3517+
// make most sense to always place write_1_clears as the final link, and
3518+
// have all other templates leverage base methods instead.
3519+
method base_write_of_gated_write(uint64 val) default {
3520+
this.templates.write_1_clears.write(val);
3521+
}
3522+
}
3523+
3524+
in each (gated_write, write_1_clears) { is gated_write_1_clears; }
3525+
```
3526+
3527+
The following is an example of the third case:
3528+
```
34503529
template very_complex_register is register {
34513530
method write_register(uint64 written, uint64 enabled_bytes,
34523531
void *aux) default {

0 commit comments

Comments
 (0)