First Draft: Static Single Assignment
Jonathan M Davis
newsgroup.d at jmdavisprog.com
Fri Dec 5 06:32:40 UTC 2025
On Thursday, December 4, 2025 2:06:34 AM Mountain Standard Time Walter Bright via dip.development wrote:
> On 12/3/2025 5:10 PM, Jonathan M Davis wrote:
> > If what you want is truly single assignment, then that's not a question of
> > mutation. It's a question of assignment.
>
> I cannot see why `final int x = 3; ++x;` should be legal.
That would depend on the goal. If the goal is truly single assignment, then
x = 42;
would violate that, but
++x;
would not, because it's not an assignment. It's mutation, yes, but it's not
assignment.
On the other hand, if the actual goal is head-const, then
++x;
would obviously violate that, because there is no indirection, and
head-const is no different from const in such a situation.
> I have not attempted to work through exactly how final would affect struct
> fields. So I'm not prepared with a solid opinion on it. But even if it does not
> work for fields, it remains useful for variables.
The problem is that if putting final on a variable of a struct type is
supposed to act like head-const, that's not possible if final is not a type
qualifier. There's simply no mechanism for the compiler to validate that a
member function is going to treat the member variables as head-const (at
least not when the compiler can't assume that it has full access to the
source code for the struct).
It can treat a final struct variable as full-on const and guarantee that by
disallowing calling any member functions which aren't const or inout, but
for code such as
final MyStruct m;
m.foo();
to have any guarantees, it has to be functionally equivalent to
const MyStruct m;
m.foo();
because there is no mechanism for marking a member function as final (at
least not in this sense, though obviously, final can be used on class member
functions for what it currently means in D).
Of course, the situation isn't quite the same if a struct's member variable
is explicitly marked as final, because then that's a question of verifying
the implementation rather than any code using the struct caring, so that's a
separate discussion, but if a variable of the struct's type is marked final,
then that implies that all of the member variables are marked as final, and
that has to be guaranteed even when the source code isn't available, so that
means that the validation is left to the function signatures, and that means
treating final the same as const with regards to structs.
For classes, the situation can be different because of the implicit
indirection, but struct's don't have that.
So, from what I can see, final is going to be utterly useless with variables
which are structs.
Depending on the rules for member variables, maybe it would still have some
value when marked explicitly on a member variable, but validating that is
different, because then it's the member function implementations which need
to be validated, and so the compiler is guaranteed to have the source code
when it does those checks, whereas with a struct variable, the validation
has to be possible with just the function signatures themselves.
> > I also cringe to think how it would work in generic code if classes are
> > treated as head-const thanks to the fact that the have a reference, but
> > structs are treated as full-on const, because they live on the stack. If
> > classes are treated as fully const, then that problem probably goes away,
> > but it also means that final is basically useless for anything other than
> > primitive types.
>
> Unlike C++, structs and classes are completely different. A final on a class
> would affect only the class reference. A final on a struct will only affect its
> fields.
Yes, and as I've tried to point out, that makes final no different from
const when it's used on a variable that's a struct. And maybe that's
acceptable, but without final being a type qualifier, I don't see how we can
do better than that.
But either way, my point about generic code is that because structs will
have to behave differently from classes with regards to final, using final
in generic code risks being problematic. Depending on the specifics, it
could easily result in it being difficult to write generic code where final
works with both classes and structs. However, I couldn't really say how bad
it would be in practice without dealing with real world code that attempted
to use final in generic code. But given how problematic const already tends
to be in generic code, I don't know how much sense it would make to use
final in generic code anyway.
- Jonathan M Davis
More information about the dip.development
mailing list