1// Written in the D programming language.
2
3/**
4This module implements experimental additions/modifications to $(MREF std, _typecons).
5
6Use this module to test out new functionality for $(REF wrap, std, _typecons)
7which allows for a struct to be wrapped against an interface; the
8implementation in $(MREF std, _typecons) only allows for classes to use the wrap
9functionality.
10
11Source:    $(PHOBOSSRC std/experimental/_typecons.d)
12
13Copyright: Copyright the respective authors, 2008-
14License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
15Authors:   $(HTTP erdani.org, Andrei Alexandrescu),
16           $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
17           Don Clugston,
18           Shin Fujishiro,
19           Kenji Hara
20 */
21module std.experimental.typecons;
22
23import std.meta; // : AliasSeq, allSatisfy;
24import std.traits;
25
26import std.typecons : Tuple, tuple, Bind, DerivedFunctionType,
27       isImplicitlyConvertible, mixinAll, staticIota,
28       GetOverloadedMethods;
29
30private
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 */
39private template dynamicCast(T)
40if (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{
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
69    interface I { @disable opCast(T)() {} Object instance(); }
70    interface J { @disable opCast(T)() {} Object 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 */
82private template implementsInterface(Source, Targets...)
83if (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    {
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
130private template implementsInterface(Source, Targets...)
131if (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 {
151        void foo() {}
152    }
153    struct B {
154        void bar() {}
155        void foobar() {}
156    }
157    class C {
158        void foo() {}
159        void bar() {}
160    }
161    struct D {
162        void foo() {}
163        void bar() {}
164        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
185private enum isInterface(ConceptType) = is(ConceptType == interface);
186
187///
188template wrap(Targets...)
189if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
190{
191    import std.meta : ApplyLeft, staticMap;
192
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        {
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
272                this(       inout Source s)        inout @safe pure nothrow { _wrap_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.
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.
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;
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#
336private 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
405private interface Structural(T)
406{
407    inout(T) _wrap_getSource() inout @safe pure nothrow;
408}
409
410private string unwrapExceptionText(Source, Target)()
411{
412    return Target.stringof~ " not wrapped into "~ Source.stringof;
413}
414
415version (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    {
449        int quack() { return 1; }
450        @property int height() { return 10; }
451    }
452    class Human
453    {
454        int quack() { return 2; }
455        @property int height() { return 20; }
456    }
457    struct HumanStructure
458    {
459        int quack() { return 3; }
460        @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    {
524        int run() { return 1; }
525        int stop() { return 2; }
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
539template 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    {
623        int draw()              { return 1; }
624        int draw(int v)         { return v; }
625
626        int draw() const        { return 2; }
627        int draw() shared       { return 3; }
628        int draw() shared const { return 4; }
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
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    {
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    {
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
717private 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
730private 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
781private 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/**
812Type constructor for final (aka head-const) variables.
813
814Final variables cannot be directly mutated or rebound, but references
815reached through the variable are typed with their original mutability.
816It is equivalent to `final` variables in D1 and Java, as well as
817`readonly` variables in C#.
818
819When `T` is a `const` or `immutable` type, `Final` aliases
820to `T`.
821*/
822template Final(T)
823{
824static if (is(T == const) || is(T == immutable))
825    alias Final = T;
826else
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
914Final!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:
920pure 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:
942pure 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
973pure 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
1046pure 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
1055pure 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
1070pure 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