]> git.ipfire.org Git - thirdparty/gcc.git/blame - libphobos/src/std/experimental/typecons.d
libphobos: Merge phobos and druntime with upstream.
[thirdparty/gcc.git] / libphobos / src / std / experimental / typecons.d
CommitLineData
03385ed3 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);
22163f0d 116 debug
117 {
118 static if (foundFunc == -1)
119 pragma(msg, "Could not locate matching function for: ",
120 TargetMembers[i].stringof);
121 }
03385ed3 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}