1 /**
2     Meta-object that captures all the properties of a function
3 */
4 
5 module bolts.reflection.metafunction;
6 
7 import bolts.reflection.metatype;
8 import bolts.reflection.metauda;
9 
10 import bolts.meta : AliasPack, Zip;
11 import bolts.traits : isSame;
12 
13 import std.array : join;
14 import std.format : format;
15 import std.meta : AliasSeq, aliasSeqOf, ApplyLeft, staticMap, anySatisfy;
16 import std.range : iota;
17 import std.traits;
18 import std.typecons : tuple;
19 
20 version(unittest) import fluent.asserts;
21 
22 private enum ExtendedFunctionAttribute : ulong
23 {
24   firstExtended = 1UL << 32,
25   static_ = firstExtended << 0,
26 }
27 
28 private struct String(string value)
29 {
30   enum mixture = value;
31   enum mixtureArray = [ value ];
32 }
33 
34 template ParameterAttribute(alias F, int i, int j)
35 {
36   static if (is(typeof(F) P == __parameters)) {
37     static if (isExpressions!(__traits(getAttributes, P[i..i+1])[j])) {
38       enum ParameterAttribute = __traits(getAttributes, P[i..i+1])[j];
39     } else {
40       alias ParameterAttribute = __traits(getAttributes, P[i..i+1])[j];
41     }
42   }
43 }
44 
45 unittest
46 {
47   import bolts.traits : isSame;
48 
49   struct foo;
50 
51   struct bar
52   {
53     this(string name) { this.name = name; }
54     string name;
55   }
56 
57   void f(@foo @bar int, @foo @bar("xyz") @foo @("baz") @(42) real);
58 
59   static assert(isSame!(ParameterAttribute!(f, 0, 0), foo));
60   static assert(isSame!(ParameterAttribute!(f, 0, 1), bar));
61   static assert(isSame!(ParameterAttribute!(f, 1, 0), foo));
62   static assert(isSame!(ParameterAttribute!(f, 1, 1), bar("xyz")));
63   static assert(isSame!(ParameterAttribute!(f, 1, 2), foo));
64   static assert(isSame!(ParameterAttribute!(f, 1, 3), "baz"));
65   static assert(isSame!(ParameterAttribute!(f, 1, 4), 42));
66 }
67 
68 enum subMixture(alias Meta) = Meta.mixture;
69 enum subMixtureArray(alias Meta) = Meta.mixtureArray;
70 
71 private template withDefaults(alias Values, Defaults...)
72 {
73   static if (Values.length != 0) {
74     alias withDefaults = AliasSeq!(
75       Values.Unpack[0],
76       withDefaults!(
77         AliasPack!(Values.Unpack[1..$]),
78         Defaults[1..$]));
79   } else static if (Defaults.length != 0) {
80     alias withDefaults = AliasSeq!(
81       Defaults[0],
82       withDefaults!(
83         Values,
84         Defaults[1..$]));
85   } else {
86     alias withDefaults = AliasSeq!();
87   }
88 }
89 
90 private template aliasSeqReplace(int position, T...)
91   if (T.length > 1)
92 {
93   alias Seq = AliasSeq!(T[1..$]);
94   static if (position == Seq.length) {
95     alias aliasSeqReplace = AliasSeq!(Seq[0..$-1], T[0]);
96   } else static if (position == 0) {
97     alias aliasSeqReplace = AliasSeq!(T[0], Seq[1..$]);
98   } else {
99     alias aliasSeqReplace = AliasSeq!(Seq[0..position], T[0], Seq[position+1..$]);
100   }
101 }
102 
103 unittest
104 {
105   static assert(
106     is(aliasSeqReplace!(0, int, char, real, void)
107        == AliasSeq!(int, real, void)));
108   static assert(
109     is(aliasSeqReplace!(1, int, char, real, void)
110        == AliasSeq!(char, int, void)));
111   static assert(
112     is(aliasSeqReplace!(2, int, char, real, void)
113        == AliasSeq!(char, real, int)));
114 
115   static assert(
116     is(aliasSeqReplace!(0, int, char, real)
117        == AliasSeq!(int, real)));
118   static assert(
119     is(aliasSeqReplace!(1, int, char, real)
120        == AliasSeq!(char, int)));
121 
122   static assert(is(aliasSeqReplace!(0, int, char) == AliasSeq!(int)));
123 }
124 
125 alias PDV = ParameterDefaults;
126 
127 private enum Property {
128   origin,
129   attributes,
130   returnType,
131   name,
132   index,
133   parameterList,
134   udaList,
135   body_,
136 }
137 
138 private alias PropertyDefaults = AliasSeq!(
139   void,
140   0,
141   Type!void,
142   "",
143   0,
144   ParameterList!(),
145   UDAList!(),
146   [],
147 );
148 
149 /**
150    Meta-class Function
151 
152  */
153 
154 template Function(Props...)
155 {
156   alias Properties = withDefaults!(AliasPack!(Props), PropertyDefaults);
157 
158   import std.ascii : toUpper;
159 
160   static foreach (i, property; __traits(allMembers, Property)) {
161     static if (isExpressions!(PropertyDefaults[i])) {
162       mixin("enum ", property, " = Properties[Property.", property, "];");
163     } else {
164       mixin("alias ", property, " = Properties[Property.", property, "];");
165     }
166     mixin(q{
167         alias set%s%s(NewValue...) = Function!(
168           aliasSeqReplace!(Property.%s, NewValue, Properties));
169       }.format(property[0].toUpper, property[1..$], property));
170   }
171 
172   /**
173      setBody
174    */
175 
176   alias setBody(string newBody) = setBody_!([ "{", newBody, "}" ]);
177 
178 
179   // --------------------------------------------------------------------------
180   // function attributes
181 
182   alias FA = FunctionAttribute;
183   alias XA = ExtendedFunctionAttribute;
184 
185   enum attributeMixtureArray = []
186     ~ (attributes & XA.static_ ? ["static"] : [])
187     ~ (attributes & FA.pure_ ? ["pure"] : [])
188     ~ (attributes & FA.nothrow_ ? ["nothrow"] : [])
189     ~ (attributes & FA.property ? ["@property"] : [])
190     ~ (attributes & FA.trusted ? ["@trusted"] : [])
191     ~ (attributes & FA.safe ? ["@safe"] : [])
192     ~ (attributes & FA.nogc ? ["@nogc"] : [])
193     ~ (attributes & FA.system ? ["@system"] : [])
194     ~ (attributes & FA.const_ ? ["const"] : [])
195     ~ (attributes & FA.immutable_ ? ["immutable"] : [])
196     ~ (attributes & FA.inout_ ? ["inout"] : [])
197     ~ (attributes & FA.shared_ ? ["shared"] : [])
198     ~ (attributes & FA.return_ ? ["return"] : [])
199     ~ (attributes & FA.scope_ ? ["scope"] : [])
200     ~ (attributes & FA.ref_ ? ["ref"] : [])
201     ;
202 
203   enum attributeMixture = attributeMixtureArray.join(" ");
204 
205   static foreach (
206     acc;
207     AliasSeq!(
208       tuple("Static", XA.static_),
209       tuple("Const", FA.const_),
210       tuple("Immutable", FA.immutable_),
211       tuple("Pure", FA.pure_),
212       tuple("Nothrow", FA.nothrow_),
213       tuple("Trusted", FA.trusted),
214       tuple("Safe", FA.safe),
215       tuple("Nogc", FA.nogc),
216       tuple("System", FA.system),
217       tuple("Ref", FA.ref_))) {
218     mixin(q{
219         enum is%s = (attributes & acc[1]) != 0;
220         alias set%s(bool Set) = void;
221         alias set%s(bool Set : true) = Function!(
222           aliasSeqReplace!(Property.attributes, attributes | acc[1], Properties));
223         alias set%s(bool Set : false) = Function!(
224           aliasSeqReplace!(Property.attributes, attributes & ~acc[1], Properties));
225       }.format(acc[0], acc[0], acc[0], acc[0]));
226   }
227 
228   enum mutabilityAttributeArray = cast(string[]) []
229     ~ (isConst ? [ "const" ] : [])
230     ~ (isImmutable ? [ "immutable" ] : []);
231 
232   enum mutabilityAttribute = mutabilityAttributeArray.join("");
233 
234   // --------------------------------------------------------------------------
235   // UDAs
236 
237   alias udas = udaList.Unpack;
238 
239   enum udaMixtureArray = cast(string[]) [ staticMap!(subMixture, udas) ];
240 
241   enum udaMixture = udaMixtureArray.join(" ");
242 
243   enum isSameUDA(alias UDA, alias Meta) = isSame!(UDA, Meta.origin);
244 
245   enum hasUDA(UDA...) = anySatisfy!(ApplyLeft!(isSameUDA, UDA[0]), udas);
246 
247   // --------------------------------------------------------------------------
248   // parameters
249 
250   alias parameters = parameterList.Unpack;
251 
252   enum arity = parameters.length;
253 
254   alias setParameters(NewParameters...) = Function!(
255     aliasSeqReplace!(Property.parameterList, ParameterList!(NewParameters), Properties));
256 
257   alias insertParameter(int Position, NewParameter...) = setParameters!(
258       parameters[0..Position], NewParameter, parameters[Position..$]);
259 
260   alias appendParameter(NewParameter...) = insertParameter!(arity, NewParameter);
261 
262   enum argumentMixtureAt(int i) = parameters[i].argumentMixture;
263 
264   enum argumentMixtureArray = cast(string[]) [
265     staticMap!(
266       argumentMixtureAt, aliasSeqOf!(parameters.length.iota))
267   ];
268 
269   enum argumentMixture = argumentMixtureArray.join(", ");
270 
271   enum parameterMixtureArray =
272     cast(string[]) [staticMap!(subMixture, parameters) ];
273 
274   enum parameterMixture = parameterMixtureArray.join(", ");
275 
276   enum declarationMixtureArray = udaMixtureArray ~ attributeMixtureArray
277     ~ returnType.mixtureArray ~ [ "%s(%s)".format(name, parameterMixture) ];
278 
279   enum declarationMixture = declarationMixtureArray.join(" ");
280 
281   enum mixtureArray = declarationMixtureArray ~ body_;
282 
283   enum mixture = mixtureArray.join(" ");
284 
285   enum pointerMixture =
286     Function!(Properties).setName!("function").declarationMixture;
287 }
288 
289 /// Examining a function
290 unittest
291 {
292   struct small;
293   pure int add(@small int a, @small int b) { return a + b; }
294   static assert(Function!add.isPure);
295   static assert(Function!add.isNothrow);
296   static assert(Function!add.isNogc);
297   static assert(Function!add.parameters.length == 2);
298   static assert(is(Function!add.parameters[0].type.origin == int));
299   static assert(Function!add.parameters[0].UDAs.length == 1);
300   static assert(is(Function!add.parameters[0].UDAs[0].origin == small));
301 }
302 
303 /// Generating a function
304 unittest
305 {
306   pure int f() { return 42; }
307 
308   template plus1(alias F)
309   {
310     alias NewFunction = Function!(f, "F").setName!"plus1".setBody!("return F() + 1;");
311     static assert(
312       NewFunction.mixture ==
313       "pure nothrow @safe @nogc std.traits.ReturnType!(F) plus1() { return F() + 1; }");
314     mixin(NewFunction.mixture);
315   }
316   Assert.equal(plus1!f, 43);
317 }
318 
319 template makeUDAs(string Source, int Position, UDAs...)
320 {
321   static if (UDAs.length == 0) {
322     alias makeUDAs = AliasSeq!();
323   } else {
324     alias makeUDAs = AliasSeq!(
325       UDA!(
326         UDAs[0],
327         "__traits(getAttributes, %s)[%d]".format(Source, Position)
328       ),
329       makeUDAs!(Source, Position + 1, UDAs[1..$]));
330   }
331 }
332 
333 alias Function(alias Fun, string Source = __traits(identifier, Fun), int Index = 0) =
334   Function!(
335     Fun,
336     functionAttributes!Fun |
337     (__traits(isStaticFunction, Fun) ? ExtendedFunctionAttribute.static_ : 0),
338     Type!(ReturnType!Fun, "std.traits.ReturnType!(%s)".format(Source)),
339     __traits(identifier, Fun),
340     Index,
341     reflectParameterList!(Fun, Source),
342     UDAList!(makeUDAs!(Source, 0, __traits(getAttributes, Fun))));
343 
344 /// Create a Function meta-object from function Fun,
345 alias Function(alias Fun, string Aggregate, string Member, int Index) =
346   Function!(
347     Fun,
348     `__traits(getOverloads, %s, "%s")[%d]`.format(Aggregate, Member, Index),
349     Index);
350 
351 unittest
352 {
353   void foo();
354   Assert.equal(
355     Function!foo.mixture,
356     "@system std.traits.ReturnType!(foo) foo()");
357   Assert.equal(
358     Function!foo.pointerMixture,
359     "@system std.traits.ReturnType!(foo) function()");
360 }
361 
362 // returnType, setReturnType
363 unittest
364 {
365   void foo();
366   Assert.equal(
367     Function!foo.returnType.mixture,
368     "std.traits.ReturnType!(foo)");
369   Assert.equal(
370     Function!foo.setReturnType!(Type!int).returnType.mixture,
371     "int");
372   Assert.equal(
373     __traits(compiles, Function!foo.setReturnType!int),
374     false);
375 }
376 
377 unittest
378 {
379   @safe ref int foo(lazy int i, char c)
380     {
381       static int x;
382       return x;
383     }
384 
385   alias Foo = Function!(foo);
386   Assert.equal(Foo.parameters[0].typeMixture, "std.traits.Parameters!(foo)[0]");
387   Assert.equal(Foo.attributeMixture, "nothrow @safe @nogc ref");
388   Assert.equal(
389     Foo.mixture,
390     "nothrow @safe @nogc ref std.traits.ReturnType!(foo) foo(lazy std.traits.Parameters!(foo)[0] a0, std.traits.Parameters!(foo)[1] a1)");
391 
392   Assert.equal(
393     (Foo.setName!("bar")).mixture,
394     "nothrow @safe @nogc ref std.traits.ReturnType!(foo) bar(lazy std.traits.Parameters!(foo)[0] a0, std.traits.Parameters!(foo)[1] a1)");
395 
396   static assert(Foo.isSafe);
397   static assert(Foo.isRef);
398   static assert(!Foo.isPure);
399   Assert.equal((Foo.setSafe!false).attributeMixture, "nothrow @nogc ref");
400   Assert.equal((Foo.setPure!true).attributeMixture, "pure nothrow @safe @nogc ref");
401 
402   struct S
403   {
404     const void foo() {}
405     immutable void bar() {}
406     static  @nogc pure @safe void baz();
407   }
408 
409   static assert(Function!(S.foo, "foo").isConst);
410   static assert(Function!(S.bar, "foo").isImmutable);
411   Assert.equal(
412     Function!(S.foo, "foo").mixture,
413     "@system const std.traits.ReturnType!(foo) foo()");
414   static assert(Function!(S.baz, "baz").isStatic);
415   static assert(Function!(S.baz, "baz").isNogc);
416   static assert(Function!(S.baz, "baz").isPure);
417   static assert(Function!(S.baz, "baz").isSafe);
418   Assert.equal(
419     Function!(S.baz, "baz").mixture,
420     "static pure @safe @nogc std.traits.ReturnType!(baz) baz()");
421 }
422 
423 unittest
424 {
425   void foo();
426   alias F1 = Function!foo.setParameters!(Parameter!([], Type!int, "j"));
427   Assert.equal(
428     F1.mixture,
429     "@system std.traits.ReturnType!(foo) foo(int j)");
430   alias F2 = F1.insertParameter!(0, Parameter!([], Type!int, "i"));
431   Assert.equal(
432     F2.mixture,
433     "@system std.traits.ReturnType!(foo) foo(int i, int j)");
434   alias F3 = F2.appendParameter!(Parameter!([], Type!int, "k"));
435   Assert.equal(
436     "@system std.traits.ReturnType!(foo) foo(int i, int j, int k)",
437     F3.mixture);
438 }
439 
440 // ============================================================================
441 // Parameter
442 
443 mixin template mixtureFromArray()
444 {
445   enum mixture = mixtureArray.join(" ");
446 }
447 
448 template ParameterDefaultValue(alias Value, string Mixture)
449 {
450   alias origin = Value;
451 
452   static if (is(Value == void)) {
453     enum mixtureArray = cast(string[]) [];
454   } else {
455     enum mixtureArray = [ "=", Mixture ];
456   }
457 
458   mixin mixtureFromArray;
459 }
460 
461 template Parameter(
462   alias UDAList, string[] StorageClasses, alias Type_, string Name = "",
463   alias DefaultValue = ParameterDefaultValue!(void, ""))
464 {
465   alias udaList = UDAList;
466   alias UDAs = udaList.Unpack;
467   enum storageClasses = StorageClasses;
468   enum name = Name;
469   alias type = Type_;
470   alias defaultValue = DefaultValue;
471   alias setType(alias newType) = Parameter!(
472     udaList, StorageClasses, newType, Name, defaultValue);
473 
474   enum storageClassArray = StorageClasses;
475   enum storageClassMixture = storageClassArray.join(" ");
476 
477   enum typeMixture = type.mixture;
478   enum typeMixtureArray = type.mixtureArray;
479   import std.traits : Select;
480   enum argumentMixtureArray = Select!(name == "", [], [ name ]);
481   enum argumentMixture = Name;
482 
483   enum mixture = join(
484     udaList.mixtureArray
485     ~ storageClassArray
486     ~ typeMixtureArray
487     ~ argumentMixtureArray
488     ~ defaultValue.mixtureArray
489     , " ");
490 
491   enum mixtureArray = [ mixture ];
492 
493   static foreach (
494     sc; [
495       "In", "Out", "Ref", "Lazy", "Const", "Immutable", "Shared", "Inout",
496       "Scope" ]) {
497     import std.algorithm: canFind;
498     import std.string : toLower;
499     mixin(q{enum is%s = StorageClasses.canFind("%s"); }.format(sc, sc.toLower));
500   }
501 
502   alias setStorageClasses(string[] newStorageClasses) =
503     Parameter!(udaList, newStorageClasses, type, name, defaultValue);
504 }
505 
506 alias Parameter(alias Type, string Name = "") = Parameter!([], Type, Name);
507 
508 alias Parameter(string[] StorageClasses, alias Type, string Name = "") =
509   Parameter!(UDAList!(), StorageClasses, Type, Name);
510 
511 template ParameterList(T...)
512 {
513   alias Unpack = T;
514   enum mixture = mixtureArray.join(" ");
515   enum mixtureArray = cast(string[]) [ staticMap!(subMixture, T) ];
516 }
517 
518 template UDAList(T...)
519 {
520   alias Unpack = T;
521   enum mixtureArray = cast(string[]) [ staticMap!(subMixture, T) ];
522   enum mixture = mixtureArray.join(" ");
523 }
524 
525 template reflectParameterList(alias fun, string mixture)
526 {
527   // disabled because of bug in ParameterDefaults (20744)
528   // static if (is(typeof(ParameterDefaults!fun))) {
529   //   alias defaultValues = ParameterDefaults!fun;
530   // } else {
531   //   alias Void(int _) = void;
532   //   alias defaultValues = staticMap!(Void, aliasSeqOf!(Parameters!fun.length.iota));
533   // }
534 
535   static if (is(typeof(fun) parameters == __parameters)) {
536     alias reflectParameterList = ParameterList!(reflectParameter!0);
537 
538     template reflectParameter(int parameterIndex)
539     {
540       static if (parameterIndex == parameters.length) {
541         alias reflectParameter = AliasSeq!();
542       } else {
543         alias parameter = parameters[parameterIndex .. parameterIndex + 1];
544 
545         alias reflectParameter = AliasSeq!(
546           Parameter!(
547             UDAList!(reflectUDAs!0),
548             [ __traits(getParameterStorageClasses, fun, parameterIndex) ],
549             Type!(
550               parameters[parameterIndex],
551               "std.traits.Parameters!(%s)[%d]".format(mixture, parameterIndex)),
552             "a%d".format(parameterIndex),
553             // ParameterDefaultValue!(
554             //   defaultValues[parameterIndex],
555             //   "bolts.reflection.metafunction.PDV!(%s)[%d]".format(
556             //     mixture, parameterIndex))
557           ),
558           reflectParameter!(parameterIndex + 1));
559 
560         template reflectUDAs(int udaIndex)
561         {
562           static if (__traits(compiles, __traits(getAttributes, parameter))) {
563             static if (udaIndex < __traits(getAttributes, parameter).length) {
564               static if (isExpressions!(__traits(getAttributes, parameter)[udaIndex])) {
565                 enum value = __traits(getAttributes, parameter)[udaIndex];
566               } else {
567                 alias value = __traits(getAttributes, parameter)[udaIndex];
568               }
569               alias reflectUDAs = AliasSeq!(
570                 UDA!(
571                   value,
572                   "bolts.reflection.metafunction.ParameterAttribute!(%s, %d, %d)".format(
573                     mixture, parameterIndex, udaIndex)),
574                 reflectUDAs!(udaIndex + 1));
575             } else {
576               alias reflectUDAs = AliasSeq!();
577             }
578           } else {
579             alias reflectUDAs = AliasSeq!();
580           }
581         }
582       }
583     }
584   }
585 }
586 
587 unittest
588 {
589   struct attr
590   {
591     this(string name) { this.name = name; }
592     string name;
593   }
594 
595   void f(int, @attr int, lazy char, @attr("foo") @(42) out real);
596 
597   alias PL = reflectParameterList!(f, "F").Unpack;
598 
599   Assert.equal(PL.length, 4);
600   Assert.equal(PL[0].mixture, "std.traits.Parameters!(F)[0] a0");
601 
602   Assert.equal(PL[0].storageClasses.length, 0);
603   Assert.equal(PL[0].UDAs.length, 0);
604 
605   Assert.equal(PL[1].storageClasses.length, 0);
606   Assert.equal(PL[1].UDAs.length, 1);
607   Assert.equal(PL[1].UDAs[0].origin.stringof, "attr");
608   Assert.equal(PL[1].UDAs[0].mixture, "@(bolts.reflection.metafunction.ParameterAttribute!(F, 1, 0))");
609 
610   Assert.equal(PL[2].storageClasses.length, 1);
611   Assert.equal(PL[2].storageClasses[0], "lazy");
612   Assert.equal(PL[2].UDAs.length, 0);
613 
614   Assert.equal(PL[3].storageClasses.length, 1);
615   Assert.equal(PL[3].storageClasses[0], "out");
616   Assert.equal(PL[3].UDAs.length, 2);
617   Assert.equal(PL[3].UDAs[0].origin.name, "foo");
618   Assert.equal(PL[3].UDAs[1].origin, 42);
619 
620   // Assert.equal(
621   //   reflectParameterList!(f, "F").mixture,
622   //   "");
623 }
624 
625 unittest
626 {
627   void f(in Object, out Object, ref Object, lazy Object, const Object, immutable Object,
628   //     0          1           2           3            4             5
629          shared Object, inout Object, scope Object);
630   //     6              7             8
631   alias P(int Position) = Parameter!(
632     [__traits(getParameterStorageClasses, f, Position)],
633     Type!Object,
634     "");
635   //static assert(P!0.isIn);
636   static assert(P!1.isOut);
637   static assert(P!2.isRef);
638   static assert(P!3.isLazy);
639   // for the following the storage class has already become part of the type?
640   //static assert(P!4.isConst);
641   //static assert(P!5.isImmutable);
642   //static assert(P!6.isShared);
643   //static assert(P!7.isInout);
644   static assert(P!8.isScope);
645 
646   Assert.equal(P!1.mixture, "out Object");
647   Assert.equal(P!1.setStorageClasses!([]).mixture, "Object");
648 }
649 
650 unittest
651 {
652   alias P = Parameter!(["lazy"], Type!(int), "a");
653   Assert.equal(P.typeMixture, "int");
654   Assert.equal(P.argumentMixture, "a");
655   Assert.equal(P.storageClassMixture, "lazy");
656   Assert.equal(P.mixture, "lazy int a");
657 
658   Assert.equal(Parameter!(Type!(int, "T"), "a").typeMixture, "T");
659 }
660 
661 unittest
662 {
663   // building a function from scratch
664   alias F = Function!()
665     .setName!"foo"
666     .setReturnType!(Type!int)
667     .setParameters!(
668       Parameter!(Type!int),
669       Parameter!(Type!string));
670   Assert.equal(F.mixture, "int foo(int, string)");
671 }
672 
673 // ============================================================================
674 // UDAs
675 
676 unittest
677 {
678   struct foo;
679   @foo @("bar") void f();
680   alias F = Function!f;
681   Assert.equal(F.udas.length, 2);
682   Assert.equal(F.udas[0].mixture, "@(__traits(getAttributes, f)[0])");
683   Assert.equal(is(F.udas[0].origin == foo), true);
684   Assert.equal(F.udas[1].origin, "bar");
685   Assert.equal(F.hasUDA!foo, true);
686   Assert.equal(F.hasUDA!"bar", true);
687   Assert.equal(F.hasUDA!"baz", false);
688   Assert.equal(
689     F.udaMixture,
690     `@(__traits(getAttributes, f)[0]) @(__traits(getAttributes, f)[1])`);
691   mixin(F.setName!"g".mixture, ";");
692   Assert.equal(
693     __traits(getAttributes, f).stringof,
694     __traits(getAttributes, g).stringof);
695   Assert.equal(
696     Function!f.pointerMixture,
697     `@(__traits(getAttributes, f)[0]) @(__traits(getAttributes, f)[1]) @system std.traits.ReturnType!(f) function()`);
698 }
699 
700 unittest
701 {
702   struct foo
703   {
704     this(string name) { this.name = name; }
705     string name;
706   }
707 
708   void f(int, @foo @foo("xyz") @("baz") @(42) real);
709   alias F = Function!f;
710   Assert.equal(F.parameters[0].UDAs.length, 0);
711   Assert.equal(F.parameters[1].UDAs.length, 4);
712 }
713 
714 // default parameter values
715 // unittest
716 // {
717 //   int increment(int i, int by = 1);
718 //   Assert.equal(Function!(increment, "F").parameters[1].defaultValue.origin, 1);
719 //   Assert.equal(
720 //     Function!(increment, "F").parameters[1].mixture,
721 //     "std.traits.Parameters!(F)[1] a1 = bolts.reflection.metafunction.PDV!(F)[1]");
722 // }