1 // Written in the D programming language.
2 
3 /**
4 This module implements experimental additions/modifications to $(MREF std, _typecons).
5 
6 Use this module to test out new functionality for $(REF wrap, std, _typecons)
7 which allows for a struct to be wrapped against an interface; the
8 implementation in $(MREF std, _typecons) only allows for classes to use the wrap
9 functionality.
10 
11 Source:    $(PHOBOSSRC std/experimental/_typecons.d)
12 
13 Copyright: Copyright the respective authors, 2008-
14 License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
15 Authors:   $(HTTP erdani.org, Andrei Alexandrescu),
16            $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
17            Don Clugston,
18            Shin Fujishiro,
19            Kenji Hara
20  */
21 module std.experimental.typecons;
22 
23 import std.meta; // : AliasSeq, allSatisfy;
24 import std.traits;
25 
26 import std.typecons : Tuple, tuple, Bind, DerivedFunctionType,
27        isImplicitlyConvertible, mixinAll, staticIota,
28        GetOverloadedMethods;
29 
30 private
31 {
32     pragma(mangle, "_d_toObject")
33     extern(C) pure nothrow Object typecons_d_toObject(void* p);
34 }
35 
36 /*
37  * Avoids opCast operator overloading.
38  */
39 private template dynamicCast(T)
40 if (is(T == class) || is(T == interface))
41 {
42     @trusted
43     T dynamicCast(S)(inout S source)
44     if (is(S == class) || is(S == interface))
45     {
46         static if (is(Unqual!S : Unqual!T))
47         {
48             import std.traits : QualifierOf;
49             alias Qual = QualifierOf!S; // SharedOf or MutableOf
50             alias TmpT = Qual!(Unqual!T);
51             inout(TmpT) tmp = source;   // bypass opCast by implicit conversion
52             return *cast(T*)(&tmp);     // + variable pointer cast + dereference
53         }
54         else
55         {
56             return cast(T) typecons_d_toObject(*cast(void**)(&source));
57         }
58     }
59 }
60 
61 @system unittest
62 {
opCast(T)63     class C { @disable opCast(T)() {} }
64     auto c = new C;
65     static assert(!__traits(compiles, cast(Object) c));
66     auto o = dynamicCast!Object(c);
67     assert(c is o);
68 
opCast(T)69     interface I { @disable opCast(T)() {} Object instance(); }
opCast(T)70     interface J { @disable opCast(T)() {} Object instance(); }
instance()71     class D : I, J { Object instance() { return this; } }
72     I i = new D();
73     static assert(!__traits(compiles, cast(J) i));
74     J j = dynamicCast!J(i);
75     assert(i.instance() is j.instance());
76 }
77 
78 /*
79  * Determines if the `Source` type satisfies all interface requirements of
80  * `Targets`.
81  */
82 private template implementsInterface(Source, Targets...)
83 if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
84 {
85     import std.meta : staticMap;
86 
87     // strict upcast
88     bool implementsInterface()()
89     if (Targets.length == 1 && is(Source : Targets[0]))
90     {
91         return true;
92     }
93     // structural upcast
94     template implementsInterface()
95     if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets))
96     {
implementsInterface()97         auto implementsInterface()
98         {
99             return hasRequiredMethods!();
100         }
101 
102         // list of FuncInfo
103         alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!Targets);
104         // list of function symbols
105         alias SourceMembers = GetOverloadedMethods!Source;
106 
107         // Check whether all of SourceMembers satisfy covariance target in
108         // TargetMembers
109         template hasRequiredMethods(size_t i = 0)
110         {
111             static if (i >= TargetMembers.length)
112                 enum hasRequiredMethods = true;
113             else
114             {
115                 enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers);
116                 debug
117                 {
118                     static if (foundFunc == -1)
119                         pragma(msg, "Could not locate matching function for: ",
120                                TargetMembers[i].stringof);
121                 }
122                 enum hasRequiredMethods =
123                     foundFunc != -1 &&
124                     hasRequiredMethods!(i + 1);
125             }
126         }
127     }
128 }
129 // ditto
130 private template implementsInterface(Source, Targets...)
131 if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
132 {
133     import std.meta : staticMap;
134 
135     alias implementsInterface = .implementsInterface!(Source, staticMap!(Unqual, Targets));
136 }
137 
138 @safe unittest
139 {
140     interface Foo {
141         void foo();
142     }
143     interface Bar {
144         void bar();
145     }
146     interface FooBar : Foo, Bar {
147         void foobar();
148     }
149 
150     struct A {
fooA151         void foo() {}
152     }
153     struct B {
bar()154         void bar() {}
foobar()155         void foobar() {}
156     }
157     class C {
foo()158         void foo() {}
bar()159         void bar() {}
160     }
161     struct D {
fooD162         void foo() {}
barD163         void bar() {}
foobarD164         void foobar() {}
165     }
166     // Implements interface
167     static assert(implementsInterface!(A, Foo));
168     static assert(implementsInterface!(A, const(Foo)));
169     static assert(implementsInterface!(A, immutable(Foo)));
170     // Doesn't implement interface
171     static assert(!implementsInterface!(B, Foo));
172     static assert(implementsInterface!(B, Bar));
173     // Implements both interfaces
174     static assert(implementsInterface!(C, Foo));
175     static assert(implementsInterface!(C, Bar));
176     static assert(implementsInterface!(C, Foo, Bar));
177     static assert(implementsInterface!(C, Foo, const(Bar)));
178     static assert(!implementsInterface!(A, Foo, Bar));
179     static assert(!implementsInterface!(A, Foo, immutable(Bar)));
180     // Implements inherited
181     static assert(implementsInterface!(D, FooBar));
182     static assert(!implementsInterface!(B, FooBar));
183 }
184 
185 private enum isInterface(ConceptType) = is(ConceptType == interface);
186 
187 ///
188 template wrap(Targets...)
189 if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
190 {
191     import std.meta : ApplyLeft, staticMap;
192 
version(StdDdoc)193     version (StdDdoc)
194     {
195         /**
196          * Wrap src in an anonymous class implementing $(D_PARAM Targets).
197          *
198          * wrap creates an internal wrapper class which implements the
199          * interfaces in `Targets` using the methods of `src`, then returns a
200          * GC-allocated instance of it.
201          *
202          * $(D_PARAM Source) can be either a `class` or a `struct`, but it must
203          * $(I structurally conform) with all the $(D_PARAM Targets)
204          * interfaces; i.e. it must provide concrete methods with compatible
205          * signatures of those in $(D_PARAM Targets).
206          *
207          * If $(D_PARAM Source) is a `struct` then wrapping/unwrapping will
208          * create a copy; it is not possible to affect the original `struct`
209          * through the wrapper.
210          *
211          * The returned object additionally supports $(LREF unwrap).
212          *
213          * Note:
214          * If $(D_PARAM Targets) has only one entry and $(D_PARAM Source) is a
215          * class which explicitly implements it, wrap simply returns src
216          * upcasted to `Targets[0]`.
217          *
218          * Bugs:
219          * wrap does not support interfaces which take their own type as either
220          * a parameter type or return type in any of its methods.
221          *
222          * See_Also: $(LREF unwrap) for examples
223          */
224         auto wrap(Source)(inout Source src)
225             if (implementsInterface!(Source, Targets));
226     }
227 
228     static if (!allSatisfy!(isMutable, Targets))
229         alias wrap = .wrap!(staticMap!(Unqual, Targets));
230     else
231     {
232         // strict upcast
233         auto wrap(Source)(inout Source src)
234         if (Targets.length == 1 && is(Source : Targets[0]))
235         {
236             alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]);
237             return dynamicCast!(inout T)(src);
238         }
239 
240         // structural upcast
241         template wrap(Source)
242         if (!allSatisfy!(ApplyLeft!(isImplicitlyConvertible, Source), Targets))
243         {
wrap(inout Source src)244             auto wrap(inout Source src)
245             {
246                 static assert(implementsInterface!(Source, Targets),
247                               "Source "~Source.stringof~
248                               " does not have structural conformance to "~
249                               Targets.stringof);
250 
251                 alias T = Select!(is(Source == shared), shared Impl, Impl);
252                 return new inout T(src);
253             }
254 
255             // list of FuncInfo
256             alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!(Targets));
257             // list of function symbols
258             alias SourceMembers = GetOverloadedMethods!Source;
259 
260             static if (is(Source == class) || is(Source == interface))
261                 alias StructuralType = Object;
262             else static if (is(Source == struct))
263                 alias StructuralType = Source;
264 
265             // Check whether all of SourceMembers satisfy covariance target in TargetMembers
266             // Internal wrapper class
267             final class Impl : Structural!StructuralType, Targets
268             {
269             private:
270                 Source _wrap_source;
271 
this(inout Source s)272                 this(       inout Source s)        inout @safe pure nothrow { _wrap_source = s; }
this(shared inout Source s)273                 this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; }
274 
275                 static if (is(Source == class) || is(Source == interface))
276                 {
277                     // BUG: making private should work with NVI.
inout(Object)278                     protected inout(Object) _wrap_getSource() inout @safe
279                     {
280                         return dynamicCast!(inout Object)(_wrap_source);
281                     }
282                 }
283                 else
284                 {
285                     // BUG: making private should work with NVI.
inout(Source)286                     protected inout(Source) _wrap_getSource() inout @safe
287                     {
288                         return _wrap_source;
289                     }
290                 }
291 
292                 import std.conv : to;
293                 import std.functional : forward;
generateFun(size_t i)294                 template generateFun(size_t i)
295                 {
296                     enum name = TargetMembers[i].name;
297                     enum fa = functionAttributes!(TargetMembers[i].type);
298                     static args(int num)()
299                     {
300                         string r;
301                         bool first = true;
302                         foreach (i; staticIota!(0, num))
303                         {
304                             import std.conv : to;
305                             r ~= (first ? "" : ", ") ~ " a" ~ (i+1).to!string;
306                             first = false;
307                         }
308                         return r;
309                     }
310                     static if (fa & FunctionAttribute.property)
311                     {
312                         static if (Parameters!(TargetMembers[i].type).length == 0)
313                             enum fbody = "_wrap_source."~name;
314                         else
315                             enum fbody = "_wrap_source."~name~" = a1";
316                     }
317                     else
318                     {
319                             enum fbody = "_wrap_source."~name~"("~args!(Parameters!(TargetMembers[i].type).length)~")";
320                     }
321                     enum generateFun =
322                         "override "~wrapperSignature!(TargetMembers[i]) ~
323                         "{ return "~fbody~"; }";
324                 }
325 
326             public:
327                 mixin mixinAll!(
328                     staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
329             }
330         }
331     }
332 }
333 
334 // Build a signature that matches the provided function
335 // Each argument will be provided a name in the form a#
wrapperSignature(alias fun)336 private template wrapperSignature(alias fun)
337 {
338     enum name = fun.name;
339     enum fa = functionAttributes!(fun.type);
340     static @property stc()
341     {
342         string r;
343         if (fa & FunctionAttribute.property)    r ~= "@property ";
344         if (fa & FunctionAttribute.ref_)        r ~= "ref ";
345         if (fa & FunctionAttribute.pure_)       r ~= "pure ";
346         if (fa & FunctionAttribute.nothrow_)    r ~= "nothrow ";
347         if (fa & FunctionAttribute.trusted)     r ~= "@trusted ";
348         if (fa & FunctionAttribute.safe)        r ~= "@safe ";
349         return r;
350     }
351     static @property mod()
352     {
353         alias type = AliasSeq!(fun.type)[0];
354         string r;
355         static if (is(type == immutable))       r ~= " immutable";
356         else
357         {
358             static if (is(type == shared))      r ~= " shared";
359             static if (is(type == const))       r ~= " const";
360             else static if (is(type == inout))  r ~= " inout";
361             //else  --> mutable
362         }
363         return r;
364     }
365     alias param = Parameters!(fun.type);
366     static @property wrapperParameters()
367     {
368         string r;
369         bool first = true;
370         foreach (i, p; param)
371         {
372             import std.conv : to;
373             r ~= (first ? "" : ", ") ~ p.stringof ~ " a" ~ (i+1).to!string;
374             first = false;
375         }
376         return r;
377     }
378 
379     enum wrapperSignature =
380         stc~ReturnType!(fun.type).stringof ~ " "
381         ~ name~"("~wrapperParameters~")"~mod;
382 }
383 
384 @safe unittest
385 {
386     interface M
387     {
388         void f1();
389         void f2(string[] args, int count);
390         void f3(string[] args, int count) pure const;
391     }
392 
393     alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!M);
394     static assert(wrapperSignature!(TargetMembers[0]) == "void f1()"
395                   , wrapperSignature!(TargetMembers[0]));
396 
397     static assert(wrapperSignature!(TargetMembers[1]) == "void f2(string[] a1, int a2)"
398                   , wrapperSignature!(TargetMembers[1]));
399 
400     static assert(wrapperSignature!(TargetMembers[2]) == "pure void f3(string[] a1, int a2) const"
401                   , wrapperSignature!(TargetMembers[2]));
402 }
403 
404 // Internal class to support dynamic cross-casting
Structural(T)405 private interface Structural(T)
406 {
407     inout(T) _wrap_getSource() inout @safe pure nothrow;
408 }
409 
unwrapExceptionText(Source,Target)410 private string unwrapExceptionText(Source, Target)()
411 {
412     return Target.stringof~ " not wrapped into "~ Source.stringof;
413 }
414 
version(StdDdoc)415 version (StdDdoc)
416 {
417     /**
418      * Extract object previously wrapped by $(LREF wrap).
419      *
420      * Params:
421      *     Target = type of wrapped object
422      *     src = wrapper object returned by $(LREF wrap)
423      *
424      * Returns: the wrapped object, or null if src is not a wrapper created
425      * by $(LREF wrap) and $(D_PARAM Target) is a class
426      *
427      * Throws: $(REF ConvException, std, conv) when attempting to extract a
428      * struct which is not the wrapped type
429      *
430      * See_also: $(LREF wrap)
431      */
432     public inout(Target) unwrap(Target, Source)(inout Source src);
433 }
434 
435 ///
436 @system unittest
437 {
438     interface Quack
439     {
440         int quack();
441         @property int height();
442     }
443     interface Flyer
444     {
445         @property int height();
446     }
447     class Duck : Quack
448     {
quack()449         int quack() { return 1; }
height()450         @property int height() { return 10; }
451     }
452     class Human
453     {
quack()454         int quack() { return 2; }
height()455         @property int height() { return 20; }
456     }
457     struct HumanStructure
458     {
quackHumanStructure459         int quack() { return 3; }
heightHumanStructure460         @property int height() { return 30; }
461     }
462 
463     Duck d1 = new Duck();
464     Human h1 = new Human();
465     HumanStructure hs1;
466 
467     interface Refreshable
468     {
469         int refresh();
470     }
471     // does not have structural conformance
472     static assert(!__traits(compiles, d1.wrap!Refreshable));
473     static assert(!__traits(compiles, h1.wrap!Refreshable));
474     static assert(!__traits(compiles, hs1.wrap!Refreshable));
475 
476     // strict upcast
477     Quack qd = d1.wrap!Quack;
478     assert(qd is d1);
479     assert(qd.quack() == 1);    // calls Duck.quack
480     // strict downcast
481     Duck d2 = qd.unwrap!Duck;
482     assert(d2 is d1);
483 
484     // structural upcast
485     Quack qh = h1.wrap!Quack;
486     Quack qhs = hs1.wrap!Quack;
487     assert(qh.quack() == 2);    // calls Human.quack
488     assert(qhs.quack() == 3);    // calls HumanStructure.quack
489     // structural downcast
490     Human h2 = qh.unwrap!Human;
491     HumanStructure hs2 = qhs.unwrap!HumanStructure;
492     assert(h2 is h1);
493     assert(hs2 is hs1);
494 
495     // structural upcast (two steps)
496     Quack qx = h1.wrap!Quack;   // Human -> Quack
497     Quack qxs = hs1.wrap!Quack;   // HumanStructure -> Quack
498     Flyer fx = qx.wrap!Flyer;   // Quack -> Flyer
499     Flyer fxs = qxs.wrap!Flyer;   // Quack -> Flyer
500     assert(fx.height == 20);    // calls Human.height
501     assert(fxs.height == 30);    // calls HumanStructure.height
502     // strucural downcast (two steps)
503     Quack qy = fx.unwrap!Quack; // Flyer -> Quack
504     Quack qys = fxs.unwrap!Quack; // Flyer -> Quack
505     Human hy = qy.unwrap!Human; // Quack -> Human
506     HumanStructure hys = qys.unwrap!HumanStructure; // Quack -> HumanStructure
507     assert(hy is h1);
508     assert(hys is hs1);
509     // strucural downcast (one step)
510     Human hz = fx.unwrap!Human; // Flyer -> Human
511     HumanStructure hzs = fxs.unwrap!HumanStructure; // Flyer -> HumanStructure
512     assert(hz is h1);
513     assert(hzs is hs1);
514 }
515 
516 ///
517 @system unittest
518 {
519     import std.traits : functionAttributes, FunctionAttribute;
520     interface A { int run(); }
521     interface B { int stop(); @property int status(); }
522     class X
523     {
run()524         int run() { return 1; }
stop()525         int stop() { return 2; }
status()526         @property int status() { return 3; }
527     }
528 
529     auto x = new X();
530     auto ab = x.wrap!(A, B);
531     A a = ab;
532     B b = ab;
533     assert(a.run() == 1);
534     assert(b.stop() == 2);
535     assert(b.status == 3);
536     static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
537 }
538 
unwrap(Target)539 template unwrap(Target)
540 {
541     static if (!isMutable!Target)
542         alias unwrap = .unwrap!(Unqual!Target);
543     else
544     {
545         // strict downcast
546         auto unwrap(Source)(inout Source src)
547         if (is(Target : Source))
548         {
549             alias T = Select!(is(Source == shared), shared Target, Target);
550             return dynamicCast!(inout T)(src);
551         }
552 
553         // structural downcast for struct target
554         auto unwrap(Source)(inout Source src)
555         if (is(Target == struct))
556         {
557             alias T = Select!(is(Source == shared), shared Target, Target);
558             auto upCastSource = dynamicCast!Object(src);   // remove qualifier
559             do
560             {
561                 if (auto a = dynamicCast!(Structural!Object)(upCastSource))
562                 {
563                     upCastSource = a._wrap_getSource();
564                 }
565                 else if (auto a = dynamicCast!(Structural!T)(upCastSource))
566                 {
567                     return a._wrap_getSource();
568                 }
569                 else
570                 {
571                     static if (hasMember!(Source, "_wrap_getSource"))
572                         return unwrap!Target(src._wrap_getSource());
573                     else
574                         break;
575                 }
576             } while (upCastSource);
577             import std.conv : ConvException;
578             throw new ConvException(unwrapExceptionText!(Source,Target));
579         }
580         // structural downcast for class target
581         auto unwrap(Source)(inout Source src)
582         if (!is(Target : Source) && !is(Target == struct))
583         {
584             alias T = Select!(is(Source == shared), shared Target, Target);
585             Object upCastSource = dynamicCast!(Object)(src);   // remove qualifier
586             do
587             {
588                 // Unwrap classes
589                 if (auto a = dynamicCast!(Structural!Object)(upCastSource))
590                 {
591                     if (auto d = dynamicCast!(inout T)(upCastSource = a._wrap_getSource()))
592                         return d;
593                 }
594                 // Unwrap a structure of type T
595                 else if (auto a = dynamicCast!(Structural!T)(upCastSource))
596                 {
597                     return a._wrap_getSource();
598                 }
599                 // Unwrap class that already inherited from interface
600                 else if (auto d = dynamicCast!(inout T)(upCastSource))
601                 {
602                     return d;
603                 }
604                 // Recurse to find the struct Target within a wrapped tree
605                 else
606                 {
607                     static if (hasMember!(Source, "_wrap_getSource"))
608                         return unwrap!Target(src._wrap_getSource());
609                     else
610                         break;
611                 }
612             } while (upCastSource);
613             return null;
614         }
615     }
616 }
617 
618 @system unittest
619 {
620     // Validate const/immutable
621     class A
622     {
draw()623         int draw()              { return 1; }
draw(int v)624         int draw(int v)         { return v; }
625 
draw()626         int draw() const        { return 2; }
draw()627         int draw() shared       { return 3; }
draw()628         int draw() shared const { return 4; }
draw()629         int draw() immutable    { return 5; }
630     }
631     interface Drawable
632     {
633         int draw();
634         int draw() const;
635         int draw() shared;
636         int draw() shared const;
637         int draw() immutable;
638     }
639     interface Drawable2
640     {
641         int draw(int v);
642     }
643 
644     auto ma = new A();
645     auto sa = new shared A();
646     auto ia = new immutable A();
647     {
648                      Drawable  md = ma.wrap!Drawable;
649                const Drawable  cd = ma.wrap!Drawable;
650               shared Drawable  sd = sa.wrap!Drawable;
651         shared const Drawable scd = sa.wrap!Drawable;
652            immutable Drawable  id = ia.wrap!Drawable;
653         assert( md.draw() == 1);
654         assert( cd.draw() == 2);
655         assert( sd.draw() == 3);
656         assert(scd.draw() == 4);
657         assert( id.draw() == 5);
658     }
659     {
660         Drawable2 d = ma.wrap!Drawable2;
661         static assert(!__traits(compiles, d.draw()));
662         assert(d.draw(10) == 10);
663     }
664 }
665 @system unittest
666 {
667     // Bugzilla 10377
668     import std.algorithm, std.range;
669 
MyInputRange(T)670     interface MyInputRange(T)
671     {
672         @property T front();
673         void popFront();
674         @property bool empty();
675     }
676 
677     //auto o = iota(0,10,1).inputRangeObject();
678     //pragma(msg, __traits(allMembers, typeof(o)));
679     auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
680     assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
681 }
682 @system unittest
683 {
684     // Bugzilla 10536
685     interface Interface
686     {
687         int foo();
688     }
689     class Pluggable
690     {
foo()691         int foo() { return 1; }
692         @disable void opCast(T, this X)();  // !
693     }
694 
695     Interface i = new Pluggable().wrap!Interface;
696     assert(i.foo() == 1);
697 }
698 @system unittest
699 {
700     // Enhancement 10538
701     interface Interface
702     {
703         int foo();
704         int bar(int);
705     }
706     class Pluggable
707     {
opDispatch(string name,A...)708         int opDispatch(string name, A...)(A args) { return 100; }
709     }
710 
711     Interface i = wrap!Interface(new Pluggable());
712     assert(i.foo() == 100);
713     assert(i.bar(10) == 100);
714 }
715 
716 // Concat all Targets function members into one tuple
ConcatInterfaceMembers(Targets...)717 private template ConcatInterfaceMembers(Targets...)
718 {
719     static if (Targets.length == 0)
720         alias ConcatInterfaceMembers = AliasSeq!();
721     else static if (Targets.length == 1)
722         alias ConcatInterfaceMembers
723           = AliasSeq!(GetOverloadedMethods!(Targets[0]));
724     else
725         alias ConcatInterfaceMembers = AliasSeq!(
726                 GetOverloadedMethods!(Targets[0]),
727                 ConcatInterfaceMembers!(Targets[1..$]));
728 }
729 // Remove duplicated functions based on the identifier name and function type covariance
UniqMembers(members...)730 private template UniqMembers(members...)
731 {
732     template FuncInfo(string s, F)
733     {
734         enum name = s;
735         alias type = F;
736     }
737 
738     static if (members.length == 0)
739         alias UniqMembers = AliasSeq!();
740     else
741     {
742         alias func = members[0];
743         enum  name = __traits(identifier, func);
744         alias type = FunctionTypeOf!func;
745         template check(size_t i, mem...)
746         {
747             static if (i >= mem.length)
748                 enum ptrdiff_t check = -1;
749             else static if
750               (__traits(identifier, func) == __traits(identifier, mem[i]) &&
751               !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void))
752             {
753                 enum ptrdiff_t check = i;
754             }
755             else
756                 enum ptrdiff_t check = check!(i + 1, mem);
757         }
758         enum ptrdiff_t x = 1 + check!(0, members[1 .. $]);
759         static if (x >= 1)
760         {
761             alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x]));
762             alias remain = UniqMembers!(members[1 .. x], members[x + 1 .. $]);
763 
764             static if (remain.length >= 1 && remain[0].name == name &&
765                        !is(DerivedFunctionType!(typex, remain[0].type) == void))
766             {
767                 alias F = DerivedFunctionType!(typex, remain[0].type);
768                 alias UniqMembers = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]);
769             }
770             else
771                 alias UniqMembers = AliasSeq!(FuncInfo!(name, typex), remain);
772         }
773         else
774         {
775             alias UniqMembers = AliasSeq!(FuncInfo!(name, type), UniqMembers!(members[1 .. $]));
776         }
777     }
778 }
779 
780 // find a function from Fs that has same identifier and covariant type with f
findCovariantFunction(alias finfo,Source,Fs...)781 private template findCovariantFunction(alias finfo, Source, Fs...)
782 {
783     template check(size_t i = 0)
784     {
785         static if (i >= Fs.length)
786             enum ptrdiff_t check = -1;
787         else
788         {
789             enum ptrdiff_t check =
790                 (finfo.name == __traits(identifier, Fs[i])) &&
791                 isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type)
792               ? i : check!(i + 1);
793         }
794     }
795     enum x = check!();
796     static if (x == -1 && is(typeof(Source.opDispatch)))
797     {
798         alias Params = Parameters!(finfo.type);
799         enum ptrdiff_t findCovariantFunction =
800             is(typeof((             Source).init.opDispatch!(finfo.name)(Params.init))) ||
801             is(typeof((       const Source).init.opDispatch!(finfo.name)(Params.init))) ||
802             is(typeof((   immutable Source).init.opDispatch!(finfo.name)(Params.init))) ||
803             is(typeof((      shared Source).init.opDispatch!(finfo.name)(Params.init))) ||
804             is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init)))
805           ? ptrdiff_t.max : -1;
806     }
807     else
808         enum ptrdiff_t findCovariantFunction = x;
809 }
810 
811 /**
812 Type constructor for final (aka head-const) variables.
813 
814 Final variables cannot be directly mutated or rebound, but references
815 reached through the variable are typed with their original mutability.
816 It is equivalent to `final` variables in D1 and Java, as well as
817 `readonly` variables in C#.
818 
819 When `T` is a `const` or `immutable` type, `Final` aliases
820 to `T`.
821 */
Final(T)822 template Final(T)
823 {
824 static if (is(T == const) || is(T == immutable))
825     alias Final = T;
826 else
827 {
828     struct Final
829     {
830         import std.typecons : Proxy;
831 
832         private T final_value;
833         mixin Proxy!final_value;
834 
835         /**
836          * Construction is forwarded to the underlying type.
837          */
838         this(T other)
839         {
840             this.final_value = other;
841         }
842 
843         /// Ditto
844         this(Args...)(auto ref Args args)
845             if (__traits(compiles, T(args)))
846         {
847             static assert((!is(T == struct) && !is(T == union)) || !isNested!T,
848                 "Non-static nested type " ~ fullyQualifiedName!T ~ " must be " ~
849                 "constructed explicitly at the call-site (e.g. auto s = " ~
850                 "makeFinal(" ~ T.stringof ~ "(...));)");
851             this.final_value = T(args);
852         }
853 
854         // Attaching function attributes gives less noisy error messages
855         pure nothrow @safe @nogc
856         {
857             /++
858              + All operators, including member access, are forwarded to the
859              + underlying value of type `T` except for these mutating operators,
860              + which are disabled.
861              +/
862             void opAssign(Other)(Other other)
863             {
864                 static assert(0, typeof(this).stringof ~
865                                  " cannot be reassigned.");
866             }
867 
868             /// Ditto
869             void opOpAssign(string op, Other)(Other other)
870             {
871                 static assert(0, typeof(this).stringof ~
872                                  " cannot be reassigned.");
873             }
874 
875             /// Ditto
876             void opUnary(string op : "--")()
877             {
878                 static assert(0, typeof(this).stringof ~
879                                  " cannot be mutated.");
880             }
881 
882             /// Ditto
883             void opUnary(string op : "++")()
884             {
885                 static assert(0, typeof(this).stringof ~
886                                  " cannot be mutated.");
887             }
888         }
889 
890         /**
891          *
892          * `Final!T` implicitly converts to an rvalue of type `T` through
893          * `AliasThis`.
894          */
895         inout(T) final_get() inout
896         {
897             return final_value;
898         }
899 
900         /// Ditto
901         alias final_get this;
902 
903         /// Ditto
904         auto ref opUnary(string op)()
905             if (__traits(compiles, mixin(op ~ "T.init")))
906         {
907             return mixin(op ~ "this.final_value");
908         }
909     }
910 }
911 }
912 
913 /// Ditto
914 Final!T makeFinal(T)(T t)
915 {
916     return Final!T(t);
917 }
918 
919 /// `Final` can be used to create class references which cannot be rebound:
920 pure nothrow @safe unittest
921 {
922     static class A
923     {
924         int i;
925 
926         this(int i) pure nothrow @nogc @safe
927         {
928             this.i = i;
929         }
930     }
931 
932     auto a = makeFinal(new A(42));
933     assert(a.i == 42);
934 
935     //a = new A(24); // Reassignment is illegal,
936     a.i = 24; // But fields are still mutable.
937 
938     assert(a.i == 24);
939 }
940 
941 /// `Final` can also be used to create read-only data fields without using transitive immutability:
942 pure nothrow @safe unittest
943 {
944     static class A
945     {
946         int i;
947 
948         this(int i) pure nothrow @nogc @safe
949         {
950             this.i = i;
951         }
952     }
953 
954     static class B
955     {
956         Final!A a;
957 
958         this(A a) pure nothrow @nogc @safe
959         {
960             this.a = a; // Construction, thus allowed.
961         }
962     }
963 
964     auto b = new B(new A(42));
965     assert(b.a.i == 42);
966 
967     // b.a = new A(24); // Reassignment is illegal,
968     b.a.i = 24; // but `a` is still mutable.
969 
970     assert(b.a.i == 24);
971 }
972 
973 pure nothrow @safe unittest
974 {
975     static class A { int i; }
976     static assert(!is(Final!A == A));
977     static assert(is(Final!(const A) == const A));
978     static assert(is(Final!(immutable A) == immutable A));
979 
980     Final!A a = new A;
981     static assert(!__traits(compiles, a = new A));
982 
983     static void foo(ref A a) pure nothrow @safe @nogc {}
984     static assert(!__traits(compiles, foo(a)));
985 
986     assert(a.i == 0);
987     a.i = 42;
988     assert(a.i == 42);
989 
990     Final!int i = 42;
991     static assert(!__traits(compiles, i = 24));
992     static assert(!__traits(compiles, --i));
993     static assert(!__traits(compiles, ++i));
994     assert(i == 42);
995     int iCopy = i;
996     assert(iCopy == 42);
997     iCopy = -i; // non-mutating unary operators must work
998     assert(iCopy == -42);
999 
1000     static struct S
1001     {
1002         int i;
1003 
1004         pure nothrow @safe @nogc:
1005         this(int i){}
1006         this(string s){}
1007         this(int i, string s, float f){ this.i = i; }
1008     }
1009 
1010     Final!S sint = 42;
1011     Final!S sstr = "foo";
1012     static assert(!__traits(compiles, sint = sstr));
1013 
1014     auto sboth = Final!S(42, "foo", 3.14);
1015     assert(sboth.i == 42);
1016 
1017     sboth.i = 24;
1018     assert(sboth.i == 24);
1019 
1020     struct NestedS
1021     {
1022         int i;
1023         int get() pure nothrow @safe @nogc { return sboth.i + i; }
1024     }
1025 
1026     // Nested structs must be constructed at the call-site
1027     static assert(!__traits(compiles, Final!NestedS(6)));
1028     auto s = makeFinal(NestedS(6));
1029     assert(s.i == 6);
1030     assert(s.get == 30);
1031 
1032     class NestedC
1033     {
1034         int i;
1035 
1036         pure nothrow @safe @nogc:
1037         this(int i) { this.i = i; }
1038         int get() { return sboth.i + i; }
1039     }
1040 
1041     auto c = makeFinal(new NestedC(6));
1042     assert(c.i == 6);
1043     assert(c.get == 30);
1044 }
1045 
1046 pure nothrow @safe unittest
1047 {
1048     auto arr = makeFinal([1, 2, 3]);
1049     static assert(!__traits(compiles, arr = null));
1050     static assert(!__traits(compiles, arr ~= 4));
1051     assert((arr ~ 4) == [1, 2, 3, 4]);
1052 }
1053 
1054 // issue 17270
1055 pure nothrow @nogc @system unittest
1056 {
1057     int i = 1;
1058     Final!(int*) fp = &i;
1059     assert(*fp == 1);
1060     static assert(!__traits(compiles,
1061         fp = &i // direct assignment
1062     ));
1063     static assert(is(typeof(*fp) == int));
1064     *fp = 2; // indirect assignment
1065     assert(*fp == 2);
1066     int* p = fp;
1067     assert(*p == 2);
1068 }
1069 
1070 pure nothrow @system unittest
1071 {
1072     Final!(int[]) arr;
1073     // static assert(!__traits(compiles,
1074         // arr.length = 10; // bug!
1075     // ));
1076     static assert(!__traits(compiles,
1077         arr.ptr = null
1078     ));
1079     static assert(!__traits(compiles,
1080         arr.ptr++
1081     ));
1082 }
1083