Skip to content

Commit 1167412

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 6a5baf3 commit 1167412

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
@@ -3451,21 +3451,49 @@ are instantiated, as long as all conflicting implementations are overridable,
34513451
and one of the following is true:
34523452
* The implementations can be combined together by calling each one of them, as
34533453
long as that can be done without risking e.g. side-effects being duplicated.
3454+
* The implementations in all but (optionally) one of the involved templates call
3455+
to a "base" method that would serve the same purpose as `default`, but whose
3456+
definition may be *overridden* such that the conflicting implementations can
3457+
be resolved by overriding these base methods and leveraging template-qualified
3458+
method implementation calls in order to define the chain of how each
3459+
implementation gets called.
3460+
3461+
This requires that the names of the base methods are unique to each template
3462+
involved, and also some thought as to how the implementations in the chain
3463+
should be ordered.
3464+
3465+
If the implementations in some templates call `default` instead of an
3466+
overridable base method, consider modifying those templates (if possible) to
3467+
each use a base method instead — a template-qualified
3468+
method implementation call can be used as the default implementation of each
3469+
base method in order to make it act like `default` in the typical case where
3470+
no conflicting templates are in play.
34543471
* The implementations can be combined by choosing one particular template's
34553472
implementation to invoke (typically the one most complex), and then adding
34563473
code around that implementation call in order to replicate the behaviour of
34573474
the implementations of the other templates. Ideally, the other templates would
34583475
provide methods that may be leveraged so that their behaviour may be
34593476
replicated without the need for excessive boilerplate.
34603477

3478+
These cases are ordered by difficulty to resolve in ascending order. In
3479+
practice, most conflicts between unrelated templates can be resolved by
3480+
modifying the templates such that the second case may apply.
3481+
3482+
> [!NOTE]
3483+
> The examples given below apply regardless of whether the method
3484+
> implementations in the original templates are `shared` or not. However, the
3485+
> implementations in the template defined to resolve the conflicts may need to
3486+
> be non-`shared` if some of the implementations involved are not `shared`;
3487+
> see the final paragraph of this subsection.
3488+
34613489
The following is an example of the first case:
34623490
```
34633491
template alter_write is write {
34643492
method write(uint64 written) {
34653493
default(alter_write(written));
34663494
}
34673495
3468-
method alter_write(uint64 curr, uint64 written) -> (uint64);
3496+
shared method alter_write(uint64 curr, uint64 written) -> (uint64);
34693497
}
34703498
34713499
template gated_write is alter_write {
@@ -3499,6 +3527,57 @@ in each (gated_write, write_1_clears) { is gated_write_1_clears; }
34993527

35003528
The following is an example of the second case:
35013529
```
3530+
template gated_write is write {
3531+
method write_allowed() -> (bool) default {
3532+
return true;
3533+
}
3534+
3535+
method write(uint64 val) default {
3536+
if (write_allowed()) {
3537+
base_write_of_gated_write(val); // instead of calling default()
3538+
}
3539+
}
3540+
3541+
method base_write_of_gated_write(uint64 val) default {
3542+
// If no conflicting template is in play such as to override this
3543+
// base method, then its behavior is as though the write implementation
3544+
// called default() instead
3545+
this.templates.write.write(val);
3546+
}
3547+
}
3548+
3549+
template write_1_clears is (write, get) {
3550+
method write(uint64 val) default {
3551+
default(get() & ~val);
3552+
}
3553+
}
3554+
3555+
template gated_write_1_clears is (gated_write, write_1_clears) {
3556+
method write(uint64 val) default {
3557+
// This makes gated_write the first link in the call chain...
3558+
this.templates.gated_write.write(val);
3559+
}
3560+
3561+
// And by overriding gated_write's base method, we make write_1_clears
3562+
// the second (and final) link in the chain
3563+
//
3564+
// If even more conflicting templates could be in play, then one could
3565+
// consider converting write_1_clears to also leverage a base method
3566+
// instead of calling default and extend the call chain; on the other
3567+
// hand, the way write_1_clears manipulates the written value may violate
3568+
// the expectations of implementations later on in the chain, so it might
3569+
// make most sense to always place write_1_clears as the final link, and
3570+
// have all other templates leverage base methods instead.
3571+
method base_write_of_gated_write(uint64 val) default {
3572+
this.templates.write_1_clears.write(val);
3573+
}
3574+
}
3575+
3576+
in each (gated_write, write_1_clears) { is gated_write_1_clears; }
3577+
```
3578+
3579+
The following is an example of the third case:
3580+
```
35023581
template very_complex_register is register {
35033582
method write_register(uint64 written, uint64 enabled_bytes,
35043583
void *aux) default {

0 commit comments

Comments
 (0)