relax disabled Final!T unary operators

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Thu Mar 23 17:35:39 PDT 2017


On Thu, Mar 23, 2017 at 11:30:43PM +0000, Q. Schroll via Digitalmars-d wrote:
> In std.experimental.typecons.Final the operators ++ and -- are
> disabled. I suspect, this was done with simple types as int in mind,
> where increment is nothing different from += 1, which is by definition
> an assignment. From the distant standpoint, there is no reason to
> disable them at all. They are modifying the object, but calling a
> modifying method on a Final!  struct/class is not disabled either. An
> operation need not be disabled because it is equivalent to an
> assignment.

>From another point of view, though, a class that overloads `++` in a
const way is pretty screwed up in terms of abusing the meaning of `++`.
I'm not sure if we should go out of our way to support that kind of
abuse of operator overloading!

Not to mention that the compiler automatically translates:

	Object obj;
	++obj;

to the equivalent of:

	{ Object tmp = obj; obj.opUnary!"++"(); return tmp; }

so even if Final allows you to call a const opUnary!"++", the
preincrement case may still be rejected due to the rebinding in the
above lowering.  Then we'd end up with the asymmetric situation where
obj++ is allowed but ++obj is not.


> I agree that this behavior is natural. Final!int should not be
> modifiable at all, that's precisely what immutable int does. This
> leads to the following conclusion:
>
> Making Final an alias to immutable for types without indirections.
> They cast implicitly to mutable. E.g. Being allowed to assign the
> components of Tuple!(int, int), but not the tuple itself is ridiculous
> because that's the same.

The same is true for static arrays, which are value types, and thus, if
the element type is POD, have no indirections. Yet currently,
Final!(int[4]) allows you to modify array elements.


> My conclusion: Final only makes sense for things that have
> indirections.
[...]

I agree.

The grey area, though, is with structs.  What should be the correct
behaviour of Final!S when S is a struct?  Since structs are value types,
it could be argued that only reference members of the struct ought to be
modifiable, but the struct itself should not be modifiable.  Yet the
current implementation allows you to modify struct members freely -- you
just can't reassign the entire struct. Which, as you say, is kinda
ridiculous because that amounts to essentially the same thing, we're
just forcing the user to manually assign struct fields individually
instead of having the compiler do it for you. And it's kinda pointless
to use Final in this case.

But it's not clear how to implement prohibiting the modification of
struct members, e.g.:

	struct S {
		int x;
		void method() { x++; }
	}

	Final!S s;
	s.x++;		// suppose we prohibit this
	s.method();	// then should we still allow this?

I'm tempted to say that Final should only be applied to class and
pointer types (because head const, the original charter of Final,
doesn't seem to make sense for types that are inherently by-value).  But
that would exclude structs that wrap around pointers, e.g., RefCounted,
which would seem to be a crippling defect, as we'd *want* to support
those kinds of "smart pointer" types.


T

-- 
Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy


More information about the Digitalmars-d mailing list