Optional tags and attributes

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Jan 17 18:36:58 PST 2014


On Sat, Jan 18, 2014 at 01:52:55AM +0000, Rikki Cattermole wrote:
> On Saturday, 18 January 2014 at 00:57:27 UTC, Stanislav Blinov
> wrote:
> >Obviously I didn't explain myself clearly. I know how to determine
> >if a function is nothrow or pure or @safe or anything else thanks
> >to D's awesomeness :) But what I want is a way to *use* that
> >knowledge when declaring my own functions. Or rather, tell the
> >compiler "Wait, I really want this to be nothrow, but I don't know
> >if that function will throw. Here's a check for you, please make
> >me nothrow if it passes". After all, tags are not just for
> >enforcing correctness at compile time, they can be used (once
> >verified) for optimization too. So it'd be nice to find a way to
> >provide all the nice info to the compiler whenever possible. It's
> >not just about nothrow, but also pure, @safe/@system/@trusted,
> >hell, even public/protected/private for that matter. :)
> >
> >>That way you can have two declarations but with one being
> >>opposite of the if.
> >
> >...Or four in case I'd also want pure/not pure, or nine if I'd
> >also want @safe/not @safe...
> 
> Okay, I'll explain what I was inferring. If the methods your calling
> are lets say nothrow which can be checked by a pure function lets
> say. Using the template if statement we can check that it does
> throw. For example:
> 
> void myfunc(T)(T arg) nothrow if (checkIfNothrow!T) {}
> void myfunc(T)(T arg) if (checkIfNoModifiers!T) {}
> void myfunc(T)(T arg) pure if (checkIfPure!T) {}
> 
> Basically you have to define all combinations. That is what I was
> meaning.
> Preferably you'll use q{} your code that is actually used. And use a
> template mixin to generate all these statements, so you don't have
> to!

What's wrong with just letting the compiler infer the maximal function
attributes?

	void fun(T)(T arg)
		if (is(typeof(arg.method())))
	{
		arg.method();
	}
	
	int globvar;
	struct Impure {
		void method() @safe nothrow { globvar++; }
	}
	
	struct Thrower {
		void method() pure @safe { throw new Exception("abc"); }
	}
	
	struct Unsafe {
		void method() pure nothrow {
			int[2] arr;
			int* ptr = arr.ptr;
			ptr++;
			*ptr = 123;
		}
	}
	
	pragma(msg, typeof(fun!Impure));
	pragma(msg, typeof(fun!Thrower));
	pragma(msg, typeof(fun!Unsafe));

Compiler output:

	nothrow @safe void(Impure arg)
	pure @safe void(Thrower arg)
	pure nothrow @system void(Unsafe arg)

Notice how the best attributes have been inferred for fun(), depending
on what type was passed in, even though we never annotated fun() with
any attributes. That is, fun()'s attributes change depending on how
T.method() was implemented. So if T.method happens to be pure, safe, and
nothrow, then so will fun(). If fun() calls multiple methods, then its
final attributes will be the intersection of the attributes of all the
method calls plus its own code (so if you threw in fun(), then it would
become nothrow regardless of whether T.method is nothrow or not, but if
fun() itself never throws, then it will be marked nothrow depending on
whether T.method throws).

Sadly, this only works for template functions currently -- so I still
had to annotate the various method()'s by hand, but if you were to turn
them into template functions too, their attributes will also be inferred
automatically.


T

-- 
Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen


More information about the Digitalmars-d mailing list