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