1 module d.common.type; 2 3 public import d.common.builtintype; 4 public import d.common.qualifier; 5 6 // Because bitfields won't work with the current stringof semantic. 7 // It is needed to import all that instanciate TypeDescriptor. 8 import d.ast.type, d.ir.type; 9 10 mixin template TypeMixin(K, Payload) { 11 private: 12 import util.bitfields; 13 alias Desc = TypeDescriptor!K; 14 15 union { 16 Desc desc; 17 ulong raw_desc; 18 } 19 20 Payload payload; 21 22 static assert(K.Builtin == 0, 23 K.stringof ~ " must have a member Builtin of value 0."); 24 static assert(K.Function != 0, K.stringof ~ " must have a Function kind."); 25 26 static assert(Payload.sizeof == ulong.sizeof, 27 "Payload must be the same size as ulong."); 28 static assert(is(typeof(Payload.init.next) == typeof(&this)), 29 "Payload.next must be a pointer to the next type element."); 30 static assert(is(typeof(Payload.init.params) == ParamType*), 31 "Payload.params must be a pointer to parameter's types."); 32 33 bool isPackable() const { 34 return (raw_desc & (-1L << Desc.DataSize)) == 0; 35 } 36 37 auto getConstructedMixin(this T)(K k, TypeQualifier q) in { 38 assert(raw_desc != 0, "You can't construct type on None."); 39 } do { 40 // XXX: Consider caching in context, and stick in payload 41 // instead of heap if it fit. 42 return isPackable() 43 ? T(Desc(k, q, raw_desc), payload) 44 : T(Desc(k, q), new T(desc, payload)); 45 } 46 47 auto getElementMixin(this T)() { 48 auto data = desc.data; 49 if (data == 0) { 50 assert(payload.next !is null, "None shouldn't have been packed."); 51 return *payload.next; 52 } 53 54 union U { 55 ulong raw; 56 Desc desc; 57 } 58 59 return T(U(data).desc, payload); 60 } 61 62 alias ParamType = PackedType!(typeof(this), ParamTuple); 63 64 template getPackedBitfield(U...) { 65 auto getPackedBitfield(A...)(A args) inout { 66 import std.traits; 67 alias T = Unqual!(typeof(this)); 68 return inout(PackedType!(T, U))(desc, args, payload); 69 } 70 } 71 72 struct PackedType(T, U...) { 73 private: 74 alias BaseDesc = TypeDescriptor!K; 75 alias Desc = TypeDescriptor!(K, U); 76 77 union { 78 Desc desc; 79 ulong raw_desc; 80 } 81 82 Payload payload; 83 84 this(A...)(BaseDesc bd, A args, inout Payload p) inout { 85 union BU { 86 BaseDesc base; 87 ulong raw; 88 } 89 90 Desc d; 91 foreach (i, a; args) { 92 mixin("d." ~ U[3 * i + 1] ~ " = a;"); 93 } 94 95 auto raw = BU(bd).raw; 96 auto redux = raw & (-1UL >> SizeOfBitField!U); 97 if (raw == redux) { 98 union U { 99 Desc desc; 100 ulong raw; 101 } 102 103 raw_desc = raw | U(d).raw; 104 payload = p; 105 } else { 106 desc = d; 107 payload = inout(Payload)(new inout(T)(bd, p)); 108 } 109 } 110 111 this(Desc d, inout Payload p) inout { 112 desc = d; 113 payload = p; 114 } 115 116 template PackedMixin(T...) { 117 static if (T.length == 0) { 118 enum PackedMixin = ""; 119 } else { 120 enum PackedMixin = "\n@property auto " ~ T[1] 121 ~ "() const { return desc." ~ T[1] ~ "; }" 122 ~ PackedMixin!(T[3 .. $]); 123 } 124 } 125 126 public: 127 mixin TypeAccessorMixin!K; 128 mixin(PackedMixin!U); 129 130 auto getType() inout { 131 union BU { 132 ulong raw; 133 BaseDesc desc; 134 } 135 136 auto u = BU(raw_desc & (-1UL >> SizeOfBitField!U)); 137 if (u.raw == 0 && payload.next !is null) { 138 return *payload.next; 139 } 140 141 return inout(T)(u.desc, payload); 142 } 143 } 144 145 struct UnionTypeTpl(T, U...) { 146 private: 147 template TagFields(uint i, U...) { 148 import std.conv; 149 static if (U.length == 0) { 150 enum TagFields = 151 "\n\t" ~ T.stringof ~ " = " ~ to!string(i) ~ ","; 152 } else { 153 enum S = U[0].stringof; 154 static assert((S[0] & 0x80) == 0, 155 S ~ " must not start with an unicode."); 156 static assert(U[0].sizeof <= size_t.sizeof, 157 "Elements must be of pointer size or smaller."); 158 import std.ascii; 159 enum Name = (S == "typeof(null)") 160 ? "Undefined" 161 : toUpper(S[0]) ~ S[1 .. $]; 162 enum TagFields = "\n\t" ~ Name ~ " = " ~ to!string(i) ~ "," 163 ~ TagFields!(i + 1, U[1 .. $]); 164 } 165 } 166 167 mixin("public enum Tag {" ~ TagFields!(0, U) ~ "\n}"); 168 169 import std.traits; 170 alias Tags = EnumMembers!Tag; 171 172 // Using uint here as Tag is not accessible where the bitfield is mixed in. 173 import std.typetuple; 174 alias TagTuple = TypeTuple!(uint, "tag", EnumSize!Tag); 175 176 alias Desc = TypeDescriptor!(K, TagTuple); 177 178 union { 179 Desc desc; 180 ulong raw_desc; 181 } 182 183 union { 184 U u; 185 Payload payload; 186 } 187 188 // XXX: probably worthy of adding to phobos. 189 template isImplicitelyConvertibleFrom(T) { 190 import std.traits; 191 enum isImplicitelyConvertibleFrom(U) = 192 isImplicitlyConvertible!(T, U); 193 } 194 195 import std.typetuple, std.traits; 196 enum canConstructFrom(P) = 197 anySatisfy!(isImplicitelyConvertibleFrom!(Unqual!P), U); 198 199 public: 200 this(inout T t) inout { 201 import std.conv; 202 auto packed = t.getPackedBitfield!TagTuple(U.length.to!Tag()); 203 desc = packed.desc; 204 payload = packed.payload; 205 } 206 207 this(P)(inout P p) inout if (canConstructFrom!P) { 208 // Sanity check, in case canConstructFrom is bogous. 209 bool constructed = false; 210 211 import std.traits; 212 alias UT = Unqual!P; 213 foreach (E; Tags[0 .. $ - 1]) { 214 static if (is(UT : U[E])) { 215 Desc d; 216 d.tag = E; 217 218 // Assign d instead of setting tag to be inout compatible. 219 desc = d; 220 u[E] = p; 221 222 constructed = true; 223 } 224 } 225 226 assert(constructed, "canConstructFrom is bogous."); 227 } 228 229 @property 230 auto tag() const { 231 import std.conv; 232 return desc.tag.to!Tag(); 233 } 234 235 @property 236 auto get(Tag E)() inout in { 237 assert(tag == E); 238 } do { 239 static if (E == U.length) { 240 alias R = inout(PackedType!(T, TagTuple)); 241 return R(desc, payload).getType(); 242 } else { 243 return u[E]; 244 } 245 } 246 } 247 248 alias FunctionType = FunctionTypeTpl!(typeof(this)); 249 250 struct FunctionTypeTpl(T) { 251 private: 252 enum Pad = ulong.sizeof * 8 - Desc.DataSize; 253 enum CountSize = ulong.sizeof * 8 - Pad - 8; 254 255 import std.bitmanip; 256 mixin(bitfields!( 257 // sdfmt off 258 Linkage, "lnk", 3, 259 bool, "variadic", 1, 260 bool, "dpure", 1, 261 ulong, "ctxCount", 3, 262 ulong, "paramCount", CountSize, 263 uint, "", Pad, // Pad for TypeKind and qualifier 264 // sdfmt on 265 )); 266 267 ParamType* params; 268 269 this(Desc desc, inout ParamType* params) inout { 270 // /!\ Black magic ahead. 271 auto raw_desc = cast(ulong*) &desc; 272 273 // Remove the TypeKind and qualifier 274 *raw_desc = (*raw_desc >> Pad); 275 276 // This should point to an area of memory that have 277 // the correct layout for the bitfield. 278 auto f = cast(FunctionType*) raw_desc; 279 280 // unqual trick required for bitfield 281 auto unqual_this = cast(FunctionType*) &this; 282 unqual_this.lnk = f.lnk; 283 unqual_this.variadic = f.variadic; 284 unqual_this.dpure = f.dpure; 285 unqual_this.ctxCount = f.ctxCount; 286 unqual_this.paramCount = f.paramCount; 287 288 this.params = params; 289 } 290 291 public: 292 this(Linkage linkage, ParamType returnType, ParamType[] params, 293 bool isVariadic) { 294 lnk = linkage; 295 variadic = isVariadic; 296 dpure = false; 297 ctxCount = 0; 298 paramCount = params.length; 299 this.params = (params ~ returnType).ptr; 300 } 301 302 this(Linkage linkage, ParamType returnType, ParamType ctxType, 303 ParamType[] params, bool isVariadic) { 304 lnk = linkage; 305 variadic = isVariadic; 306 dpure = false; 307 ctxCount = 1; 308 paramCount = params.length; 309 this.params = (ctxType ~ params ~ returnType).ptr; 310 } 311 312 T getType(TypeQualifier q = TypeQualifier.Mutable) { 313 ulong d = *cast(ulong*) &this; 314 auto p = Payload(cast(T*) params); 315 return T(Desc(K.Function, q, d), p); 316 } 317 318 FunctionType getDelegate(ulong contextCount = 1) in { 319 assert(contextCount <= paramCount + ctxCount); 320 } do { 321 auto t = this; 322 t.ctxCount = contextCount; 323 t.paramCount = paramCount + ctxCount - contextCount; 324 return t; 325 } 326 327 FunctionType getFunction() { 328 return getDelegate(0); 329 } 330 331 @property 332 Linkage linkage() const { 333 return lnk; 334 } 335 336 auto withLinkage(Linkage linkage) inout { 337 // Bypass type qualifier for params, but it's alright because 338 // we do not touch any of the params and put the qualifier back. 339 auto r = *(cast(FunctionType*) &this); 340 r.lnk = linkage; 341 return *(cast(inout(FunctionType)*) &r); 342 } 343 344 @property 345 bool isVariadic() const { 346 return variadic; 347 } 348 349 @property 350 bool isPure() const { 351 return dpure; 352 } 353 354 @property 355 auto returnType() inout { 356 return params[ctxCount + paramCount]; 357 } 358 359 @property 360 auto contexts() inout { 361 return params[0 .. ctxCount]; 362 } 363 364 @property 365 auto parameters() inout { 366 return params[ctxCount .. ctxCount + paramCount]; 367 } 368 } 369 370 public: 371 mixin TypeAccessorMixin!K; 372 373 alias UnionType(T...) = UnionTypeTpl!(typeof(this), T); 374 375 auto getParamType(ParamKind kind) inout { 376 return getPackedBitfield!ParamTuple(kind); 377 } 378 379 auto asFunctionType() inout in { 380 assert(kind == K.Function, "Not a function."); 381 } do { 382 return inout(FunctionType)(desc, payload.params); 383 } 384 } 385 386 mixin template TypeAccessorMixin(K) { 387 @property 388 K kind() const { 389 return desc.kind; 390 } 391 392 @property 393 TypeQualifier qualifier() const { 394 return desc.qualifier; 395 } 396 397 auto unqual(this T)() { 398 Desc d = desc; 399 d.qualifier = TypeQualifier.Mutable; 400 return T(d, payload); 401 } 402 403 @property 404 BuiltinType builtin() inout in { 405 assert(kind == K.Builtin); 406 } do { 407 return cast(BuiltinType) desc.data; 408 } 409 } 410 411 struct TypeDescriptor(K, T...) { 412 import util.bitfields; 413 enum DataSize = ulong.sizeof * 8 - 3 - EnumSize!K - SizeOfBitField!T; 414 415 import std.bitmanip; 416 mixin(bitfields!( 417 // sdfmt off 418 K, "kind", EnumSize!K, 419 TypeQualifier, "qualifier", 3, 420 ulong, "data", DataSize, 421 T, 422 // sdfmt on 423 )); 424 425 static assert(TypeDescriptor.sizeof == ulong.sizeof); 426 427 this(K k, TypeQualifier q, ulong d = 0) { 428 kind = k; 429 qualifier = q; 430 data = d; 431 } 432 } 433 434 import std.typetuple; 435 alias ParamTuple = TypeTuple!( 436 // sdfmt off 437 ParamKind, "paramKind", 2, 438 // sdfmt on 439 );