1 module d.semantic.declaration; 2 3 import d.semantic.semantic; 4 5 import d.ast.conditional; 6 import d.ast.declaration; 7 8 import d.ir.dscope; 9 import d.ir.symbol; 10 import d.ir.type; 11 12 alias Module = d.ir.symbol.Module; 13 14 // Conflict with Interface in object.di 15 alias Interface = d.ir.symbol.Interface; 16 17 enum AggregateType { 18 None, 19 Union, 20 Struct, 21 Class, 22 } 23 24 struct DeclarationVisitor { 25 private SemanticPass pass; 26 alias pass this; 27 28 alias Step = SemanticPass.Step; 29 30 uint fieldIndex; 31 uint methodIndex; 32 33 CtUnit[] ctUnits; 34 ConditionalBranch[] cdBranches; 35 36 private { 37 import std.bitmanip; 38 mixin(bitfields!( 39 Linkage, "linkage", 3, 40 Visibility, "visibility", 3, 41 Storage, "storage", 2, 42 AggregateType, "aggregateType", 2, 43 CtUnitLevel, "ctLevel", 2, 44 InTemplate, "inTemplate", 1, 45 bool, "addThis", 1, 46 bool, "addContext", 1, 47 bool, "isRef", 1, 48 bool, "isOverride", 1, 49 bool, "isAbstract", 1, 50 bool, "isProperty", 1, 51 bool, "isNoGC", 1, 52 uint, "", 12, 53 )); 54 } 55 56 static assert(DeclarationVisitor.init.linkage == Linkage.D); 57 static assert(DeclarationVisitor.init.visibility == Visibility.Private); 58 static assert(DeclarationVisitor.init.storage == Storage.Local); 59 static assert(DeclarationVisitor.init.aggregateType == AggregateType.None); 60 static assert(DeclarationVisitor.init.addThis == false); 61 static assert(DeclarationVisitor.init.addContext == false); 62 63 this(SemanticPass pass) { 64 this.pass = pass; 65 } 66 67 Symbol[] flatten(S)( 68 Declaration[] decls, 69 S dscope, 70 ) if (is(S : Symbol) && is(S : Scope)) { 71 static assert( 72 !is(S : Class), 73 "Classes need to have fieldIndex and methodIndex", 74 ); 75 76 uint fi = is(S : Aggregate) ? dscope.hasContext : 0; 77 return flattenImpl(decls, dscope, fi, 0); 78 } 79 80 Symbol[] flatten( 81 Declaration[] decls, 82 Class c, 83 uint fieldIndex, 84 uint methodIndex, 85 ) { 86 return flattenImpl(decls, c, fieldIndex, methodIndex); 87 } 88 89 private Symbol[] flattenImpl(S)( 90 Declaration[] decls, 91 S dscope, 92 uint fieldIndex, 93 uint methodIndex, 94 ) if (is(S : Symbol) && is(S : Scope)) { 95 visibility = Visibility.Public; 96 97 aggregateType = 98 is(S : Class) ? AggregateType.Class 99 : is(S : Struct) ? AggregateType.Struct 100 : is(S : Union) ? AggregateType.Union 101 : AggregateType.None; 102 103 linkage = dscope.linkage; 104 105 // What a mess ! 106 if (aggregateType > AggregateType.None) { 107 inTemplate = dscope.inTemplate; 108 addThis = true; 109 } 110 111 static if (is(S : Module)) { 112 storage = Storage.Static; 113 } 114 115 static if (is(S : TemplateInstance)) { 116 inTemplate = InTemplate.Yes; 117 addThis = dscope.hasThis; 118 addContext = dscope.hasContext; 119 storage = dscope.storage; 120 } 121 122 this.fieldIndex = fieldIndex; 123 this.methodIndex = methodIndex; 124 125 auto oldScope = currentScope; 126 scope(exit) currentScope = oldScope; 127 currentScope = dscope; 128 129 auto ctus = flattenDecls(decls); 130 dscope.step = Step.Populated; 131 132 dscope.setPoisoningMode(); 133 scope(exit) dscope.clearPoisoningMode(); 134 135 return DeclarationFlattener!S(&this, dscope).lowerToSymbols(ctus); 136 } 137 138 // FIXME: Pass the function down here. 139 Symbol[] flatten(Declaration d) { 140 addContext = true; 141 142 import std.range; 143 auto ctus = flattenDecls(only(d)); 144 assert(ctus.length == 1); 145 146 auto u = ctus[0]; 147 assert(u.level == CtUnitLevel.Done); 148 assert(u.type == CtUnitType.Symbols); 149 150 import std.algorithm, std.range; 151 return u.symbols.map!(su => su.s).array(); 152 } 153 154 private auto flattenDecls(R)(R decls) { 155 auto oldCtLevel = ctLevel; 156 scope(exit) ctLevel = oldCtLevel; 157 158 ctLevel = CtUnitLevel.Done; 159 160 auto oldCtUnits = ctUnits; 161 scope(exit) ctUnits = oldCtUnits; 162 163 ctUnits = [CtUnit()]; 164 165 foreach(d; decls) { 166 visit(d); 167 } 168 169 return ctUnits; 170 } 171 172 void visit(Declaration d) { 173 return this.dispatch(d); 174 } 175 176 private void select(D, S)(D d, S s) if(is(D : Declaration) && is(S : Symbol)) { 177 auto unit = &(ctUnits[$ - 1]); 178 assert(unit.type == CtUnitType.Symbols); 179 180 if (unit.level == CtUnitLevel.Done) { 181 scheduler.schedule(d, s); 182 } 183 184 unit.symbols ~= SymbolUnit(d, s); 185 } 186 187 void visit(FunctionDeclaration d) { 188 auto stc = d.storageClass; 189 auto storage = getStorage(stc); 190 191 Function f; 192 193 auto isStatic = storage.isGlobal; 194 if (isStatic || aggregateType != AggregateType.Class || d.name.isReserved) { 195 f = new Function( 196 d.location, 197 currentScope, 198 FunctionType.init, 199 d.name, 200 [], 201 ); 202 } else { 203 uint index = -1; 204 if (!isOverride && !stc.isOverride) { 205 index = methodIndex++; 206 } 207 208 f = new Method( 209 d.location, 210 currentScope, 211 index, 212 FunctionType.init, 213 d.name, 214 [], 215 ); 216 } 217 218 f.linkage = getLinkage(stc); 219 f.visibility = getVisibility(stc); 220 f.inTemplate = inTemplate; 221 222 f.hasThis = isStatic ? false : addThis; 223 f.hasContext = isStatic ? false : addContext; 224 225 f.isAbstract = isAbstract || stc.isAbstract; 226 f.isProperty = isProperty || stc.isProperty; 227 228 addOverloadableSymbol(f); 229 select(d, f); 230 } 231 232 void visit(VariableDeclaration d) { 233 auto stc = d.storageClass; 234 auto storage = getStorage(stc); 235 236 if (aggregateType == AggregateType.None || storage.isGlobal) { 237 auto v = new Variable(d.location, Type.get(BuiltinType.None), d.name); 238 v.linkage = getLinkage(stc); 239 v.visibility = getVisibility(stc); 240 v.storage = storage; 241 v.inTemplate = inTemplate; 242 243 addSymbol(v); 244 select(d, v); 245 } else { 246 auto f = new Field( 247 d.location, 248 fieldIndex, 249 Type.get(BuiltinType.None), 250 d.name, 251 ); 252 253 // Union have all their fields at the same index. 254 if (aggregateType > AggregateType.Union) { 255 fieldIndex++; 256 } 257 258 f.linkage = getLinkage(stc); 259 f.visibility = getVisibility(stc); 260 f.inTemplate = inTemplate; 261 262 addSymbol(f); 263 select(d, f); 264 } 265 } 266 267 void visit(StructDeclaration d) { 268 auto s = new Struct(d.location, currentScope, d.name, []); 269 s.linkage = linkage; 270 s.visibility = visibility; 271 s.inTemplate = inTemplate; 272 273 s.hasContext = storage.isGlobal ? false : addContext; 274 275 addSymbol(s); 276 select(d, s); 277 } 278 279 void visit(UnionDeclaration d) { 280 auto u = new Union(d.location, currentScope, d.name, []); 281 u.linkage = linkage; 282 u.visibility = visibility; 283 u.inTemplate = inTemplate; 284 285 u.hasContext = storage.isGlobal ? false : addContext; 286 287 addSymbol(u); 288 select(d, u); 289 } 290 291 void visit(ClassDeclaration d) { 292 auto c = new Class(d.location, currentScope, d.name, []); 293 c.linkage = linkage; 294 c.visibility = visibility; 295 c.inTemplate = inTemplate; 296 297 c.hasThis = storage.isGlobal ? false : addThis; 298 c.hasContext = storage.isGlobal ? false : addContext; 299 300 addSymbol(c); 301 select(d, c); 302 } 303 304 void visit(InterfaceDeclaration d) { 305 auto i = new Interface(d.location, currentScope, d.name, [], []); 306 i.linkage = linkage; 307 i.visibility = visibility; 308 i.inTemplate = inTemplate; 309 310 addSymbol(i); 311 select(d, i); 312 } 313 314 void visit(EnumDeclaration d) { 315 if (d.name.isDefined) { 316 auto e = new Enum( 317 d.location, 318 currentScope, 319 d.name, 320 Type.get(BuiltinType.None), 321 [], 322 ); 323 324 e.linkage = linkage; 325 e.visibility = visibility; 326 327 addSymbol(e); 328 select(d, e); 329 } else { 330 // XXX: Code duplication with symbols. Refactor. 331 import d.ast.expression : AstExpression, AstBinaryExpression, AstBinaryOp; 332 AstExpression previous; 333 AstExpression one; 334 foreach(vd; d.entries) { 335 auto v = new Variable( 336 vd.location, 337 Type.get(BuiltinType.None), 338 vd.name, 339 ); 340 341 v.visibility = visibility; 342 343 if (!vd.value) { 344 import d.ir.expression; 345 if (previous) { 346 if (!one) { 347 one = new IntegerLiteral( 348 vd.location, 349 1, 350 BuiltinType.Int, 351 ); 352 } 353 354 vd.value = new AstBinaryExpression( 355 vd.location, 356 AstBinaryOp.Add, 357 previous, 358 one, 359 ); 360 } else { 361 vd.value = new IntegerLiteral( 362 vd.location, 363 0, 364 BuiltinType.Int, 365 ); 366 } 367 } 368 369 v.storage = Storage.Enum; 370 previous = vd.value; 371 372 addSymbol(v); 373 select(vd, v); 374 } 375 } 376 } 377 378 void visit(TemplateDeclaration d) { 379 auto t = new Template( 380 d.location, 381 currentScope, 382 d.name, 383 [], 384 d.declarations, 385 ); 386 387 t.linkage = linkage; 388 t.visibility = visibility; 389 t.hasThis = addThis; 390 t.inTemplate = inTemplate; 391 t.storage = storage; 392 393 addOverloadableSymbol(t); 394 select(d, t); 395 } 396 397 void visit(IdentifierAliasDeclaration d) { 398 auto a = new SymbolAlias(d.location, d.name, null); 399 400 a.linkage = linkage; 401 a.visibility = visibility; 402 a.inTemplate = inTemplate; 403 404 addSymbol(a); 405 select(d, a); 406 } 407 408 void visit(TypeAliasDeclaration d) { 409 auto a = new TypeAlias(d.location, d.name, Type.get(BuiltinType.None)); 410 411 a.linkage = linkage; 412 a.visibility = visibility; 413 a.inTemplate = inTemplate; 414 415 addSymbol(a); 416 select(d, a); 417 } 418 419 void visit(ValueAliasDeclaration d) { 420 auto a = new ValueAlias(d.location, d.name, null); 421 422 a.linkage = linkage; 423 a.visibility = visibility; 424 a.inTemplate = inTemplate; 425 426 addSymbol(a); 427 select(d, a); 428 } 429 430 void visit(AliasThisDeclaration d) { 431 assert( 432 aggregateType != AggregateType.None, 433 "alias this can only appear in aggregates" 434 ); 435 436 // TODO: have a better scheme to do this in order to: 437 // - keep the location of the alias for error messages. 438 // - not redo identifier resolution all the time. 439 auto a = cast(Aggregate) currentScope; 440 assert(a !is null, "Aggergate expected"); 441 442 a.aliasThis ~= d.name; 443 } 444 445 void visit(GroupDeclaration d) { 446 auto oldStorage = storage; 447 auto oldVisibility = visibility; 448 auto oldLinkage = linkage; 449 450 auto oldIsRef = isRef; 451 auto oldIsOverride = isOverride; 452 auto oldIsAbstract = isAbstract; 453 auto oldIsProperty = isProperty; 454 auto oldIsNoGC = isNoGC; 455 456 scope(exit) { 457 storage = oldStorage; 458 visibility = oldVisibility; 459 linkage = oldLinkage; 460 461 isRef = oldIsRef; 462 isOverride = oldIsOverride; 463 isAbstract = oldIsAbstract; 464 isProperty = oldIsProperty; 465 isNoGC = oldIsNoGC; 466 } 467 468 auto stc = d.storageClass; 469 470 storage = getStorage(stc); 471 // qualifier = getQualifier(stc); 472 visibility = getVisibility(stc); 473 linkage = getLinkage(stc); 474 475 isRef = isRef || stc.isRef; 476 isOverride = isOverride || stc.isOverride; 477 isAbstract = isAbstract || stc.isAbstract; 478 isProperty = isProperty || stc.isProperty; 479 isNoGC = isNoGC || stc.isNoGC; 480 481 foreach(decl; d.declarations) { 482 visit(decl); 483 } 484 } 485 486 private Storage getStorage(StorageClass stc) { 487 if (stc.isStatic && stc.isEnum) { 488 assert(0, "cannot be static AND enum"); 489 } else if (stc.isStatic) { 490 return Storage.Static; 491 } else if (stc.isEnum) { 492 return Storage.Enum; 493 } 494 495 return storage; 496 } 497 /+ 498 private TypeQualifier getQualifier(StorageClass stc) { 499 return stc.hasQualifier 500 ? qualifier.add(stc.qualifier) 501 : qualifier; 502 } 503 +/ 504 private Visibility getVisibility(StorageClass stc) { 505 return stc.hasVisibility 506 ? stc.visibility 507 : visibility; 508 } 509 510 private Linkage getLinkage(StorageClass stc) { 511 return stc.hasLinkage 512 ? stc.linkage 513 : linkage; 514 } 515 516 void visit(ImportDeclaration d) { 517 foreach(name; d.modules) { 518 currentScope.addImport(importModule(name)); 519 } 520 } 521 522 void visit(UnittestDeclaration d) { 523 // Do something only if unittest are enabled. 524 if (!enableUnittest) { 525 return; 526 } 527 528 auto stc = d.storageClass; 529 auto storage = getStorage(stc); 530 531 auto f = new Function( 532 d.location, 533 currentScope, 534 FunctionType.init, 535 d.name, 536 [], 537 ); 538 539 f.inTemplate = inTemplate; 540 select(d, f); 541 } 542 543 void visit(StaticIfDeclaration d) { 544 import std.algorithm : max; 545 546 auto finalCtLevel = CtUnitLevel.Conditional; 547 auto oldCtLevel = ctLevel; 548 scope(exit) ctLevel = max(finalCtLevel, oldCtLevel); 549 550 ctLevel = CtUnitLevel.Conditional; 551 552 auto finalCtUnits = ctUnits; 553 scope(exit) ctUnits = finalCtUnits; 554 555 auto unit = CtUnit(); 556 unit.type = CtUnitType.StaticIf; 557 unit.staticIf = d; 558 559 cdBranches ~= ConditionalBranch(d, true); 560 scope(exit) { 561 cdBranches = cdBranches[0 .. $ - 1]; 562 } 563 564 ctUnits = [CtUnit()]; 565 ctUnits[0].level = CtUnitLevel.Conditional; 566 567 foreach(item; d.items) { 568 visit(item); 569 } 570 571 unit.items = ctUnits; 572 573 finalCtLevel = max(finalCtLevel, ctLevel); 574 ctLevel = CtUnitLevel.Conditional; 575 576 cdBranches = cdBranches[0 .. $ - 1] ~ ConditionalBranch(d, false); 577 578 ctUnits = [CtUnit()]; 579 ctUnits[0].level = CtUnitLevel.Conditional; 580 581 foreach(item; d.elseItems) { 582 visit(item); 583 } 584 585 unit.elseItems = ctUnits; 586 587 finalCtLevel = max(finalCtLevel, ctLevel); 588 unit.level = finalCtLevel; 589 590 auto previous = finalCtUnits[$ - 1]; 591 assert(previous.type == CtUnitType.Symbols); 592 593 auto next = CtUnit(); 594 next.level = previous.level; 595 596 finalCtUnits ~= unit; 597 finalCtUnits ~= next; 598 } 599 600 void visit(StaticAssert!Declaration d) { 601 auto unit = CtUnit(); 602 unit.level = CtUnitLevel.Done; 603 unit.type = CtUnitType.StaticAssert; 604 unit.staticAssert = d; 605 606 ctUnits ~= unit; 607 ctUnits ~= CtUnit(); 608 } 609 610 void visit(Version!Declaration d) { 611 foreach(v; versions) { 612 if(d.versionId == v) { 613 foreach(item; d.items) { 614 visit(item); 615 } 616 617 return; 618 } 619 } 620 621 // Version has not been found. 622 foreach(item; d.elseItems) { 623 visit(item); 624 } 625 } 626 627 void visit(Mixin!Declaration d) { 628 ctLevel = CtUnitLevel.Unknown; 629 630 auto unit = CtUnit(); 631 unit.level = CtUnitLevel.Unknown; 632 unit.type = CtUnitType.Mixin; 633 unit.mixinDecl = d; 634 635 ctUnits ~= unit; 636 ctUnits ~= CtUnit(); 637 } 638 639 private: 640 void addSymbol(Symbol s) { 641 if (cdBranches.length) { 642 currentScope.addConditionalSymbol(s, cdBranches); 643 } else { 644 currentScope.addSymbol(s); 645 } 646 } 647 648 void addOverloadableSymbol(Symbol s) { 649 if (cdBranches.length) { 650 currentScope.addConditionalSymbol(s, cdBranches); 651 } else { 652 currentScope.addOverloadableSymbol(s); 653 } 654 } 655 } 656 657 private : 658 659 enum CtUnitLevel { 660 Done, 661 Conditional, 662 Unknown, 663 } 664 665 enum CtUnitType { 666 Symbols, 667 StaticAssert, 668 StaticIf, 669 Mixin, 670 } 671 672 struct SymbolUnit { 673 Declaration d; 674 Symbol s; 675 } 676 677 struct CtUnit { 678 import std.bitmanip; 679 mixin(bitfields!( 680 CtUnitLevel, "level", 2, 681 CtUnitType, "type", 2, 682 uint, "", 4, 683 )); 684 685 union { 686 SymbolUnit[] symbols; 687 StaticAssert!Declaration staticAssert; 688 Mixin!Declaration mixinDecl; 689 struct { 690 // TODO: special declaration subtype here. 691 StaticIfDeclaration staticIf; 692 CtUnit[] items; 693 CtUnit[] elseItems; 694 }; 695 } 696 } 697 698 struct DeclarationFlattener(S) if(is(S : Scope)) { 699 private DeclarationVisitor* dv; 700 alias dv this; 701 702 S dscope; 703 704 this(DeclarationVisitor* dv, S dscope) { 705 this.dv = dv; 706 this.dscope = dscope; 707 } 708 709 // At this point, CTFE can yield, and change object state, 710 // so we pass things as parameters. 711 private Symbol[] lowerToSymbols(CtUnit[] ctus) { 712 // Process level 2 construct 713 ctus = lowerStaticIfs(lowerMixins(ctus)); 714 assert(ctus[0].type == CtUnitType.Symbols); 715 716 Symbol[] syms; 717 718 foreach(u; ctus) { 719 assert(u.level == CtUnitLevel.Done); 720 final switch(u.type) with(CtUnitType) { 721 case Symbols: 722 import std.algorithm, std.range; 723 syms ~= u.symbols.map!(su => su.s).array(); 724 break; 725 726 case StaticAssert: 727 checkStaticAssert(u.staticAssert); 728 break; 729 730 case StaticIf, Mixin: 731 assert(0, "invalid ctUnit"); 732 } 733 } 734 735 return syms; 736 } 737 738 private CtUnit[] lowerStaticIfs(CtUnit[] ctus) { 739 CtUnit[] cdUnits; 740 cdUnits.reserve(ctus.length); 741 foreach(u; ctus) { 742 assert(u.level != CtUnitLevel.Unknown); 743 744 if (u.level != CtUnitLevel.Conditional) { 745 assert(u.level == CtUnitLevel.Done); 746 747 cdUnits ~= u; 748 continue; 749 } 750 751 final switch(u.type) with(CtUnitType) { 752 case StaticIf : 753 cdUnits ~= lowerStaticIf(u); 754 break; 755 756 case Mixin, Symbols, StaticAssert : 757 assert(0, "invalid ctUnit"); 758 } 759 } 760 761 return cdUnits; 762 } 763 764 private auto lowerStaticIf(bool forMixin = false)(CtUnit unit) in { 765 assert(unit.type == CtUnitType.StaticIf); 766 } do { 767 auto d = unit.staticIf; 768 769 import d.ir.expression, d.semantic.caster, d.semantic.expression; 770 auto condition = evalIntegral(buildExplicitCast( 771 pass, 772 d.condition.location, 773 Type.get(BuiltinType.Bool), 774 ExpressionVisitor(pass).visit(d.condition), 775 )); 776 777 CtUnit[] items; 778 if (condition) { 779 dscope.resolveConditional(d, true); 780 items = unit.items; 781 } else { 782 dscope.resolveConditional(d, false); 783 items = unit.elseItems; 784 } 785 786 foreach(ref u; items) { 787 if (u.type == CtUnitType.Symbols && u.level == CtUnitLevel.Conditional) { 788 foreach(su; u.symbols) { 789 import d.semantic.symbol; 790 SymbolVisitor(pass).visit(su.d, su.s); 791 } 792 793 u.level = CtUnitLevel.Done; 794 } 795 } 796 797 static if (forMixin) { 798 return lowerMixins(items); 799 } else { 800 return lowerStaticIfs(items); 801 } 802 } 803 804 private CtUnit[] lowerMixins(CtUnit[] ctus) { 805 CtUnit[] cdUnits; 806 cdUnits.reserve(ctus.length); 807 foreach(u; ctus) { 808 if (u.level != CtUnitLevel.Unknown) { 809 cdUnits ~= u; 810 continue; 811 } 812 813 final switch(u.type) with(CtUnitType) { 814 case StaticIf : 815 cdUnits ~= lowerStaticIf!true(u); 816 break; 817 818 case Mixin : 819 cdUnits ~= lowerMixin(u.mixinDecl); 820 break; 821 822 case Symbols, StaticAssert : 823 assert(0, "invalid ctUnit"); 824 } 825 } 826 827 return cdUnits; 828 } 829 830 private auto lowerMixin(Mixin!Declaration d) { 831 import d.semantic.expression : ExpressionVisitor; 832 auto str = evalString(ExpressionVisitor(pass).visit(d.value)); 833 834 // XXX: in order to avoid identifier resolution weirdness. 835 auto location = d.location; 836 auto base = context.registerMixin(location, str ~ '\0'); 837 838 import source.dlexer; 839 auto trange = lex(base, context); 840 841 import d.parser.base; 842 trange.match(TokenType.Begin); 843 844 Declaration[] decls; 845 while(trange.front.type != TokenType.End) { 846 import d.parser.declaration; 847 decls ~= trange.parseDeclaration(); 848 } 849 850 return lowerMixins(flattenDecls(decls)); 851 } 852 853 private void checkStaticAssert(StaticAssert!Declaration a) { 854 import d.semantic.caster, d.semantic.expression; 855 auto condition = evalIntegral(buildExplicitCast( 856 pass, 857 a.condition.location, 858 Type.get(BuiltinType.Bool), 859 ExpressionVisitor(pass).visit(a.condition), 860 )); 861 862 if (condition) { 863 return; 864 } 865 866 import source.exception; 867 if (a.message is null) { 868 throw new CompileException(a.location, "assertion failure"); 869 } 870 871 auto msg = evalString(buildExplicitCast( 872 pass, 873 a.condition.location, 874 Type.get(BuiltinType.Char).getSlice(TypeQualifier.Immutable), 875 ExpressionVisitor(pass).visit(a.message), 876 )); 877 878 throw new CompileException(a.location, "assertion failure: " ~ msg); 879 } 880 }