1 /**
2     Makes compiler assume things that may not be true
3 */
4 module ddash.lang.assume;
5 
6 /**
7     The assume template takes an alias to a funciton that casts it to a different
8     attribute.
9 
10     This can mainly be used for debugging purposes. For example when you want to call a gc
11     function or an unsafe function from nogc or safe code.
12 
13     The assume template takes a lambda as a template alias argument and then creates a type that
14     you can call as attributed. e.g.
15 
16     ---
17     assume!f1.nogc_(args); // calls f1 with args as if it was nogc
18     assume!f1.pure_(args); // calls f1 with args as if it was pure
19     ---
20 
21     Since:
22         - 0.0.1
23 */
24 template assume(alias fun) {
25     import std.traits: FunctionAttribute, SetFunctionAttributes, functionLinkage, functionAttributes;
26     private auto ref assumeAttribute(FunctionAttribute assumedAttr, T)(auto ref T t) {
27         enum attrs = functionAttributes!T | assumedAttr;
28         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
29     }
30     private static impl(string attr) {
31         return `
32             enum call = "assumeAttribute!(`~attr~`)((ref Args args) { return fun(args); })(args)";
33             ` ~ q{
34             static assert(
35                 __traits(compiles, {
36                     mixin(call ~ ";");
37                 }),
38                 "function " ~ fun.stringof ~ " is not callable with args " ~ Args.stringof
39             );
40             alias R = typeof(mixin(call));
41             static if (is(R == void)) {
42                 mixin(call ~ ";");
43             } else {
44                 mixin("return " ~ call ~ ";");
45             }
46         };
47     }
48     auto ref nogc_(Args...)(auto ref Args args) {
49         mixin(impl("FunctionAttribute.nogc"));
50     }
51     auto ref pure_(Args...)(auto ref Args args) {
52         mixin(impl("FunctionAttribute.pure_"));
53     }
54 }
55 
56 ///
57 @nogc unittest {
58     static b = [1];
59     auto allocates() {
60         return [1];
61     }
62     auto a = assume!allocates.nogc_();
63     assert(a == b);
64 
65     auto something(int a) {
66         allocates;
67     }
68     assume!something.nogc_(3);
69 }
70 
71 ///
72 unittest {
73     static int thing = 0;
74     alias lambda = () => thing++;
75     () pure {
76         cast(void)assume!lambda.pure_();
77     }();
78     assert(thing == 1);
79 }