First Draft: Static Single Assignment - library solution?

monkyyy crazymonkyyy at gmail.com
Tue Jan 13 19:11:20 UTC 2026


On Wednesday, 10 December 2025 at 21:50:14 UTC, Nick Treleaven 
wrote:
> On Saturday, 6 December 2025 at 12:00:44 UTC, Nick Treleaven 
> wrote:
>> On Friday, 5 December 2025 at 12:20:29 UTC, monkyyy wrote:
>>> I suggest asking for a template wizard to handle whatever 
>>> your compiler dev usecase with a uda hack.
>>
>> I don't think that's possible
>
> About the UDA - that would require some mechanism to enforce 
> it. I don't think the language can do that, particularly not 
> with local variables.

static assert exists, given any `isFoo` template, it can be 
forced into an `assertFoo`

>
>> - making a struct Final which acts like a type constructor. 
>> E.g. you can only have one `alias this`, and it needs to 
>> rvalue convert to mutable and lvalue convert to const.
>
> Today I made a `Final` struct template. It's incomplete. What 
> is there, I found issues with, and there are probably more. 
> Initial ones:
>
> 1. const lvalue conversion is not implicit. When a head-const 
> value is needed outside `Final!T`, it just makes an rvalue. 
> Which is OK for certain types of T, but obviously larger values 
> will do too much copying.
>
> 2. The operator overloads have to be conservative, e.g. opUnary 
> returns an rvalue for structs because how can it tell at 
> compile-time if a `ref T.opUnary` is referring to part of T's 
> memory?
>
> https://github.com/ntrel/stuff/blob/master/final.d
>

>>// Bug: doesn't prevent assigning to a struct result which has 
>>opAssign defined
>>// https://github.com/dlang/dmd/issues/21507

VERY much disagree, youd break any code that was a wrapper of a 
pointer

> Perhaps someone else can do better, but this design doesn't 
> seem robust enough for general use, even if it was finished.

>> ref opIndex()(size_t i) if (is(T == U[], U)) => v[i];
>> auto opIndex()(size_t i) if (is(T == U[n], U, size_t n)) => 
>> v[i];

??? why have these do different behavior.. whatever

```d
import std;
T recast(T,S)(ref S s){
	return *cast(T*)(&s);
}

template classifydata(T){
	static if( is(T == U[], U)){
		enum classifydata=1;
	} else {
	static if( is(T == U[N], U,size_t N)){
		enum classifydata=2;
	} else {
	static if( is(T == U[A], U,A)){
		enum classifydata=3;
	} else {
		enum classifydata=0;
	}}}
}
unittest{
	static assert(classifydata!(int)==0);
	static assert(classifydata!(int[])==1);
	static assert(classifydata!(int[3])==2);
	static assert(classifydata!(int[int])==3);
}
struct Final(T){
	T data;
	enum whatthis=classifydata!T;
	this(T t){
		data=t;
	}
	static if(whatthis==0){
		T get()=> data;
		alias get this;
	}
	static if(whatthis==1){
		void opOpAssign(string op:"~",S)(S a){
			data~=a;
	}}
	static if(whatthis>0){//whatthis==1||whatthis==2){
		auto opIndex(I)(I i){
			alias S=typeof(T.init[0]);
			return data[i].recast!(Final!S);
	}}
	static if(whatthis==3){
		auto opIndexAssign(S,I)(S s,I i){
			assert( ! (i in data));//runtime is probaly the best for aa's
			data[i]=s;
	}}

}

unittest{
	auto foo=Final!int(3);
	foo.writeln;
	auto bar=Final!(int[])([1,2,3]);
	bar~=4;
	bar.writeln;
	//bar[2]=3;
	bar[2].writeln;
}
unittest{
	Final!(int[string]) foo;
	foo["hi"]=3;
	foo["bye"]=5;
	foo.writeln;
	//foo["hi"]=7;
}
unittest{
	Final!(int[][3]) foo;
	foo[0]~=1;
	foo[1]~=[2,3];
	foo.writeln;
	//foo[0][0]=7;
}
```

youd have to track down the datastructures the compiler uses to 
update "classify data" but this is the direction id suggest trying




More information about the dip.development mailing list