1 // This file is part of Visual D 2 // 3 // Visual D integrates the D programming language into Visual Studio 4 // Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved 5 // 6 // Distributed under the Boost Software License, Version 1.0. 7 // See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt 8 9 module vdc.semantic; 10 11 import vdc.util; 12 import vdc.ast.mod; 13 import vdc.ast.node; 14 import vdc.ast.type; 15 import vdc.ast.aggr; 16 import vdc.ast.decl; 17 import vdc.ast.expr; 18 import vdc.ast.tmpl; 19 import vdc.ast.writer; 20 import vdc.parser.engine; 21 import vdc.logger; 22 import vdc.interpret; 23 import vdc.versions; 24 25 import stdext.util; 26 import stdext.array; 27 import stdext.path; 28 29 import std.exception; 30 import std.stdio; 31 import std.string; 32 import std.array; 33 import std.conv; 34 import std.datetime; 35 36 int semanticErrors; 37 38 alias object.AssociativeArray!(Node, const(bool)) _wa1; // fully instantiate type info for bool[Node] 39 alias object.AssociativeArray!(string, const(VersionInfo)) _wa2; // fully instantiate type info for VersionInfo[string] 40 alias object.AssociativeArray!(string, const(bool)) _wa3; // fully instantiate type info for bool[string] 41 42 class SemanticException : Exception 43 { 44 this(string msg) 45 { 46 super(msg); 47 } 48 } 49 50 class InterpretException : Exception 51 { 52 this() 53 { 54 super("cannot interpret"); 55 } 56 } 57 58 enum MessageType 59 { 60 Warning, 61 Error, 62 Message 63 } 64 65 void delegate(MessageType,string) fnSemanticWriteError = null; 66 67 string semanticErrorWriteLoc(string filename, ref const(TextPos) pos) 68 { 69 string txt = filename; 70 if(pos.line > 0) 71 txt ~= text("(", pos.line, ")"); 72 txt ~= ": "; 73 semanticErrors++; 74 return txt; 75 } 76 77 void semanticErrorLoc(T...)(string filename, ref const(TextPos) pos, T args) 78 { 79 foreach(a; args) 80 if(typeid(a) == typeid(ErrorType) || typeid(a) == typeid(ErrorValue)) 81 return; 82 83 string msg = semanticErrorWriteLoc(filename, pos); 84 msg ~= text(args); 85 if(fnSemanticWriteError) 86 fnSemanticWriteError(MessageType.Error, msg); 87 logInfo("%s", msg); // avoid interpreting % in message 88 } 89 90 void semanticErrorPos(T...)(ref const(TextPos) pos, T args) 91 { 92 string filename; 93 if(Scope.current && Scope.current.mod) 94 filename = Scope.current.mod.filename; 95 else 96 filename = "at global scope"; 97 semanticErrorLoc(filename, pos, args); 98 } 99 100 void semanticError(T...)(T args) 101 { 102 TextPos pos; 103 semanticErrorPos(pos, args); 104 } 105 106 void semanticErrorFile(T...)(string fname, T args) 107 { 108 TextPos pos; 109 semanticErrorLoc(fname, pos, args); 110 } 111 112 void semanticMessage(string msg) 113 { 114 if(fnSemanticWriteError) 115 fnSemanticWriteError(MessageType.Message, msg); 116 } 117 118 ErrorValue semanticErrorValue(T...)(T args) 119 { 120 TextPos pos; 121 semanticErrorPos(pos, args); 122 //throw new InterpretException; 123 return Singleton!(ErrorValue).get(); 124 } 125 126 ErrorType semanticErrorType(T...)(T args) 127 { 128 semanticErrorPos(TextPos(), args); 129 return Singleton!(ErrorType).get(); 130 } 131 132 alias Node Symbol; 133 134 class Context 135 { 136 Scope scop; 137 Value[Node] vars; 138 Context parent; 139 140 this(Context p) 141 { 142 parent = p; 143 } 144 145 Value getThis() 146 { 147 if(parent) 148 return parent.getThis(); 149 return null; 150 } 151 152 void setThis(Value v) 153 { 154 setValue(null, v); 155 } 156 157 Value getValue(Node n) 158 { 159 if(auto pn = n in vars) 160 return *pn; 161 if(parent) 162 return parent.getValue(n); 163 return null; 164 } 165 166 void setValue(Node n, Value v) 167 { 168 vars[n] = v; 169 } 170 } 171 172 class AggrContext : Context 173 { 174 Value instance; 175 bool virtualCall; 176 177 this(Context p, Value inst) 178 { 179 super(p); 180 instance = inst; 181 virtualCall = true; 182 } 183 184 override Value getThis() 185 { 186 if(auto t = cast(Class)instance.getType()) 187 return new ClassValue(t, static_cast!ClassInstanceValue (instance)); 188 return instance; 189 } 190 191 override Value getValue(Node n) 192 { 193 if(auto pn = n in vars) 194 return *pn; 195 if(auto decl = cast(Declarator) n) 196 //if(Value v = instance._interpretProperty(this, decl.ident)) 197 if(Value v = instance.getType().getProperty(instance, decl, virtualCall)) 198 return v; 199 if(parent) 200 return parent.getValue(n); 201 return null; 202 } 203 } 204 205 class AssertContext : Context 206 { 207 Value[Node] identVal; 208 209 this(Context p) 210 { 211 super(p); 212 } 213 } 214 215 Context nullContext; 216 AggrContext noThisContext; 217 218 Context globalContext; 219 Context threadContext; 220 Context errorContext; 221 222 class Scope 223 { 224 Scope parent; 225 226 Annotation annotations; 227 Attribute attributes; 228 Module mod; 229 Node node; 230 Set!Symbol[string] symbols; 231 Import[] imports; 232 233 // Context ctx; // compile time only 234 235 static Scope current; 236 237 this() 238 { 239 logInfo("Scope(%s) created, current=%s", cast(void*)this, cast(void*)current); 240 } 241 enum 242 { 243 SearchParentScope = 1, 244 SearchPrivateImport = 2, 245 } 246 247 Scope pushClone() 248 { 249 Scope sc = new Scope; 250 sc.annotations = annotations; 251 sc.attributes = attributes; 252 sc.mod = mod; 253 sc.parent = this; 254 return current = sc; 255 } 256 Scope push(Scope sc) 257 { 258 if(!sc) 259 return pushClone(); 260 261 assert(this !is sc); 262 sc.parent = this; 263 return current = sc; 264 } 265 266 Scope pop() 267 { 268 return current = parent; 269 } 270 271 Type getThisType() 272 { 273 if(!parent) 274 return null; 275 return parent.getThisType(); 276 } 277 278 void addSymbol(string ident, Symbol s) 279 { 280 logInfo("Scope(%s).addSymbol(%s, sym %s=%s)", cast(void*)this, ident, s, cast(void*)s); 281 282 if(auto sym = ident in symbols) 283 addunique(*sym, s); 284 else 285 symbols[ident] = Set!Symbol([s : true]); 286 } 287 288 void addImport(Import imp) 289 { 290 imports ~= imp; 291 } 292 293 struct SearchData { string ident; Scope sc; } 294 static Stack!SearchData searchStack; 295 296 alias Set!Symbol SearchSet; 297 298 void searchCollect(string ident, ref SearchSet syms) 299 { 300 string iden = ident[0..$-1]; 301 foreach(id, sym; symbols) 302 if(id.startsWith(iden)) 303 addunique(syms, sym); 304 } 305 306 void searchParents(string ident, bool inParents, bool privateImports, bool publicImports, ref SearchSet syms) 307 { 308 if(inParents && parent) 309 { 310 if(syms.length == 0) 311 syms = parent.search(ident, true, privateImports, publicImports); 312 else if(collectSymbols(ident)) 313 addunique(syms, parent.search(ident, true, privateImports, publicImports)); 314 } 315 } 316 317 static bool collectSymbols(string ident) 318 { 319 return ident.endsWith("*"); 320 } 321 322 SearchSet search(string ident, bool inParents, bool privateImports, bool publicImports) 323 { 324 // check recursive search 325 SearchData sd = SearchData(ident, this); 326 for(int d = 0; d < searchStack.depth; d++) 327 if(searchStack.stack[d] == sd) // memcmp 328 return Scope.SearchSet(); 329 330 SearchSet syms; 331 if(collectSymbols(ident)) 332 searchCollect(ident, syms); 333 else if(auto pn = ident in symbols) 334 return *pn; 335 336 searchStack.push(sd); 337 if(publicImports) 338 foreach(imp; imports) 339 { 340 if(privateImports || (imp.getProtection() & Annotation_Public)) 341 addunique(syms, imp.search(this, ident)); 342 } 343 searchParents(ident, inParents, privateImports, publicImports, syms); 344 searchStack.pop(); 345 return syms; 346 } 347 348 Scope.SearchSet matchFunctionArguments(Node id, Scope.SearchSet n) 349 { 350 Scope.SearchSet matches; 351 ArgumentList fnargs = id.getFunctionArguments(); 352 int cntFunc = 0; 353 foreach(s, b; n) 354 if(s.getParameterList()) 355 cntFunc++; 356 if(cntFunc != n.length) 357 return matches; 358 359 Node[] args; 360 if(fnargs) 361 args = fnargs.members; 362 foreach(s, b; n) 363 { 364 auto pl = s.getParameterList(); 365 if(args.length > pl.members.length) 366 continue; 367 368 if(args.length < pl.members.length) 369 // parameterlist must have default 370 if(auto param = pl.getParameter(args.length)) 371 if(!param.getInitializer()) 372 continue; 373 374 int a; 375 for(a = 0; a < args.length; a++) 376 { 377 auto atype = args[a].calcType(); 378 auto ptype = pl.members[a].calcType(); 379 if(!ptype.convertableFrom(atype, Type.ConversionFlags.kImpliciteConversion)) 380 break; 381 } 382 if(a < args.length) 383 continue; 384 matches[s] = true; 385 } 386 return matches; 387 } 388 389 Node resolveOverload(string ident, Node id, Scope.SearchSet n) 390 { 391 if(n.length == 0) 392 { 393 id.semanticError("unknown identifier " ~ ident); 394 return null; 395 } 396 foreach(s, b; n) 397 s.semanticSearches++; 398 399 if(n.length > 1) 400 { 401 auto matches = matchFunctionArguments(id, n); 402 if(matches.length == 1) 403 return matches.first(); 404 else if(matches.length > 0) 405 n = matches; // report only matching funcs 406 407 id.semanticError("ambiguous identifier " ~ ident); 408 foreach(s, b; n) 409 s.semanticError("possible candidate"); 410 411 if(!collectSymbols(ident)) 412 return null; 413 } 414 return n.first(); 415 } 416 417 Node resolve(string ident, Node id, bool inParents = true) 418 { 419 auto n = search(ident, inParents, true, true); 420 logInfo("Scope(%s).search(%s) found %s %s", cast(void*)this, ident, n.keys(), n.length > 0 ? cast(void*)n.first() : null); 421 422 return resolveOverload(ident, id, n); 423 } 424 425 Node resolveWithTemplate(string ident, Scope sc, Node id, bool inParents = true) 426 { 427 auto n = search(ident, inParents, true, true); 428 logInfo("Scope(%s).search(%s) found %s %s", cast(void*)this, ident, n.keys(), n.length > 0 ? cast(void*)n.first() : null); 429 430 auto resolved = resolveOverload(ident, id, n); 431 if(resolved && resolved.isTemplate()) 432 { 433 TemplateArgumentList args; 434 if(auto tmplid = cast(TemplateInstance)id) 435 { 436 args = tmplid.getTemplateArgumentList(); 437 } 438 else 439 { 440 args = new TemplateArgumentList; // no args 441 } 442 resolved = resolved.expandTemplate(sc, args); 443 } 444 return resolved; 445 } 446 447 Project getProject() { return mod ? mod.getProject() : null; } 448 } 449 450 struct ArgMatch 451 { 452 Value value; 453 string name; 454 455 string toString() { return "{" ~ value.toStr() ~ "," ~ name ~ "}"; } 456 } 457 458 class SourceModule 459 { 460 // filename already in Module 461 SysTime lastModified; 462 Options options; 463 464 string txt; 465 Module parsed; 466 Module analyzed; 467 468 Parser parser; 469 ParseError[] parseErrors; 470 471 bool parsing() { return parser !is null; } 472 } 473 474 version = no_syntaxcopy; 475 version = no_disconnect; 476 //version = free_ast; 477 478 class Project : Node 479 { 480 Options options; 481 int countErrors; 482 bool saveErrors; 483 484 Module mObjectModule; // object.d 485 SourceModule[string] mSourcesByModName; 486 SourceModule[string] mSourcesByFileName; 487 488 this() 489 { 490 TextSpan initspan; 491 super(initspan); 492 options = new Options; 493 494 version(none) 495 { 496 options.importDirs ~= r"c:\l\dmd-2.055\src\druntime\import\"; 497 options.importDirs ~= r"c:\l\dmd-2.055\src\phobos\"; 498 499 options.importDirs ~= r"c:\tmp\d\runnable\"; 500 options.importDirs ~= r"c:\tmp\d\runnable\imports\"; 501 502 options.importDirs ~= r"m:\s\d\rainers\dmd\test\"; 503 options.importDirs ~= r"m:\s\d\rainers\dmd\test\imports\"; 504 } 505 506 globalContext = new Context(null); 507 threadContext = new Context(null); 508 errorContext = new Context(null); 509 } 510 511 Module addSource(string fname, Module mod, ParseError[] errors, Node importFrom = null) 512 { 513 mod.filename = fname; 514 mod.imported = importFrom !is null; 515 516 SourceModule src; 517 string modname = mod.getModuleName(); 518 if(auto pm = modname in mSourcesByModName) 519 { 520 if(pm.parsed && pm.parsed.filename != fname) 521 { 522 semanticErrorFile(fname, "module name " ~ modname ~ " already used by " ~ pm.parsed.filename); 523 countErrors++; 524 //return null; 525 } 526 src = *pm; 527 } 528 else if(auto pm = fname in mSourcesByFileName) 529 src = *pm; 530 else 531 src = new SourceModule; 532 533 if(src.parsed) 534 { 535 version(no_disconnect) {} else 536 src.parsed.disconnect(); 537 version(free_ast) 538 src.parsed.free(); 539 } 540 src.parsed = mod; 541 src.parseErrors = errors; 542 543 import std.file : exists, timeLastModified; 544 545 if(exists(fname)) // could be pseudo name 546 src.lastModified = timeLastModified(fname); 547 548 if(src.analyzed) 549 { 550 removeMember(src.analyzed); 551 version(disconnect) {} else 552 src.analyzed.disconnect(); 553 version(free_ast) 554 src.analyzed.free(); 555 } 556 version(no_syntaxcopy) {} else 557 { 558 src.analyzed = mod.clone(); 559 addMember(src.analyzed); 560 } 561 src.options = options; 562 if(importFrom) 563 if(auto m = importFrom.getModule()) 564 src.options = m.getOptions(); 565 566 mSourcesByModName[modname] = src; 567 mSourcesByFileName[fname] = src; 568 version(no_syntaxcopy) 569 return src.parsed; 570 else 571 return src.analyzed; 572 } 573 574 //////////////////////////////////////////////////////////// 575 Module addText(string fname, string txt, Node importFrom = null) 576 { 577 SourceModule src; 578 if(auto pm = fname in mSourcesByFileName) 579 src = *pm; 580 else 581 src = new SourceModule; 582 583 logInfo("parsing " ~ fname); 584 585 Parser p = new Parser; 586 p.saveErrors = saveErrors; 587 src.parser = p; 588 scope(exit) src.parser = null; 589 590 p.filename = fname; 591 Node n; 592 try 593 { 594 semanticMessage(fname ~ ": parsing..."); 595 n = p.parseModule(txt); 596 } 597 catch(Exception e) 598 { 599 if(fnSemanticWriteError) 600 fnSemanticWriteError(MessageType.Error, e.msg); 601 countErrors += p.countErrors + 1; 602 return null; 603 } 604 countErrors += p.countErrors; 605 if(!n) 606 return null; 607 608 auto mod = static_cast!(Module)(n); 609 return addSource(fname, mod, p.errors, importFrom); 610 } 611 612 Module addAndParseFile(string fname, Node importFrom = null) 613 { 614 //debug writeln(fname, ":"); 615 string txt = readUtf8(fname); 616 return addText(fname, txt, importFrom); 617 } 618 619 bool addFile(string fname) 620 { 621 import std.file : exists, timeLastModified; 622 auto src = new SourceModule; 623 if(exists(fname)) // could be pseudo name 624 src.lastModified = timeLastModified(fname); 625 626 src.txt = readUtf8(fname); 627 mSourcesByFileName[fname] = src; 628 return true; 629 } 630 631 Module getModule(string modname) 632 { 633 if(auto pm = modname in mSourcesByModName) 634 return pm.analyzed; 635 return null; 636 } 637 638 SourceModule getModuleByFilename(string filename) 639 { 640 if(auto pm = filename in mSourcesByFileName) 641 return *pm; 642 return null; 643 } 644 645 Module importModule(string modname, Node importFrom) 646 { 647 if(auto mod = getModule(modname)) 648 return mod; 649 650 string dfile = replace(modname, ".", "/") ~ ".di"; 651 string srcfile = searchImportFile(dfile, importFrom); 652 if(srcfile.length == 0) 653 { 654 dfile = replace(modname, ".", "/") ~ ".d"; 655 srcfile = searchImportFile(dfile, importFrom); 656 } 657 if(srcfile.length == 0) 658 { 659 if (importFrom) 660 importFrom.semanticError("cannot find imported module " ~ modname); 661 else 662 .semanticError("cannot find imported module " ~ modname); 663 return null; 664 } 665 srcfile = normalizePath(srcfile); 666 return addAndParseFile(srcfile, importFrom); 667 } 668 669 string searchImportFile(string dfile, Node importFrom) 670 { 671 import std.file : exists; 672 if(exists(dfile)) 673 return dfile; 674 675 Options opt = options; 676 if(importFrom) 677 if(auto mod = importFrom.getModule()) 678 opt = mod.getOptions(); 679 680 foreach(dir; opt.importDirs) 681 if(exists(dir ~ dfile)) 682 return dir ~ dfile; 683 return null; 684 } 685 686 void initScope() 687 { 688 getObjectModule(null); 689 scop = new Scope; 690 } 691 692 Module getObjectModule(Module importFrom) 693 { 694 if(!mObjectModule) 695 mObjectModule = importModule("object", importFrom); 696 return mObjectModule; 697 } 698 699 // for error messages 700 override string getModuleFilename() 701 { 702 return "<global scope>"; 703 } 704 705 void semantic() 706 { 707 try 708 { 709 size_t cnt = members.length; // do not fully analyze imported modules 710 initScope(); 711 712 for(size_t m = 0; m < cnt; m++) 713 members[m].semantic(scop); 714 } 715 catch(InterpretException) 716 { 717 semanticError("unhandled interpret exception, semantic analysis aborted"); 718 } 719 } 720 //////////////////////////////////////////////////////////// 721 void update() 722 { 723 } 724 725 void disconnectAll() 726 { 727 foreach(s; mSourcesByFileName) 728 { 729 if(s.parsed) 730 s.parsed.disconnect(); 731 if(s.analyzed) 732 s.analyzed.disconnect(); 733 } 734 } 735 736 //////////////////////////////////////////////////////////// 737 void writeCpp(string fname) 738 { 739 string src; 740 CCodeWriter writer = new CCodeWriter(getStringSink(src)); 741 writer.writeDeclarations = true; 742 writer.writeImplementations = false; 743 744 for(int m = 0; m < members.length; m++) 745 { 746 writer.writeReferencedOnly = getMember!Module(m).imported; 747 writer(members[m]); 748 writer.nl; 749 } 750 751 writer.writeDeclarations = false; 752 writer.writeImplementations = true; 753 for(int m = 0; m < members.length; m++) 754 { 755 writer.writeReferencedOnly = getMember!Module(m).imported; 756 writer(members[m]); 757 writer.nl; 758 } 759 760 Node mainNode; 761 for(int m = 0; m < members.length; m++) 762 if(members[m].scop) 763 { 764 if(auto pn = "main" in members[m].scop.symbols) 765 { 766 if(pn.length > 1 || mainNode) 767 semanticError("multiple candidates for main function"); 768 else 769 mainNode = (*pn).first(); 770 } 771 } 772 if(mainNode) 773 { 774 writer("int main(int argc, char**argv)"); 775 writer.nl; 776 writer("{"); 777 writer.nl; 778 { 779 CodeIndenter indent = CodeIndenter(writer); 780 Module mod = mainNode.getModule(); 781 mod.writeNamespace(writer); 782 writer("main();"); 783 writer.nl; 784 writer("return 0;"); 785 writer.nl; 786 } 787 writer("}"); 788 writer.nl; 789 } 790 791 import std.file : write; 792 write(fname, src); 793 } 794 795 override void toD(CodeWriter writer) 796 { 797 throw new SemanticException("Project.toD not implemeted"); 798 } 799 800 int run() 801 { 802 Scope.SearchSet funcs; 803 foreach(m; mSourcesByModName) 804 addunique(funcs, m.analyzed.search("main")); 805 if(funcs.length == 0) 806 { 807 semanticError("no function main"); 808 return -1; 809 } 810 if(funcs.length > 1) 811 { 812 semanticError("multiple functions main"); 813 return -2; 814 } 815 TupleValue args = new TupleValue; 816 if(auto cn = cast(CallableNode)funcs.first()) 817 if(auto pl = cn.getParameterList()) 818 if(pl.members.length > 0) 819 { 820 auto tda = new TypeDynamicArray; 821 tda.setNextType(getTypeString!char()); 822 auto dav = new DynArrayValue(tda); 823 args.addValue(dav); 824 } 825 826 try 827 { 828 Value v = funcs.first().interpret(nullContext).opCall(nullContext, args); 829 if(v is theVoidValue) 830 return 0; 831 return v.toInt(); 832 } 833 catch(InterpretException) 834 { 835 semanticError("cannot run main, interpretation aborted"); 836 return -1; 837 } 838 } 839 } 840 841 struct VersionInfo 842 { 843 TextPos defined; // line -1 if not defined yet 844 TextPos firstUsage; // line int.max if not used yet 845 } 846 847 struct VersionDebug 848 { 849 int level; 850 VersionInfo[string] identifiers; 851 852 bool reset(int lev, string[] ids) 853 { 854 if(lev == level && ids.length == identifiers.length) 855 { 856 bool different = false; 857 foreach(id; ids) 858 if(id !in identifiers) 859 different = true; 860 if(!different) 861 return false; 862 } 863 864 level = lev; 865 identifiers = identifiers.init; 866 foreach(id; ids) 867 identifiers[id] = VersionInfo(); 868 869 return true; 870 } 871 872 bool preDefined(string ident) const 873 { 874 if(auto vi = ident in identifiers) 875 return vi.defined.line >= 0; 876 return false; 877 } 878 879 bool defined(string ident, TextPos pos) 880 { 881 if(auto vi = ident in identifiers) 882 { 883 if(pos < vi.defined) 884 semanticErrorPos(pos, "identifier " ~ ident ~ " used before defined"); 885 886 if(pos < vi.firstUsage) 887 vi.firstUsage = pos; 888 889 return vi.defined.line >= 0; 890 } 891 VersionInfo vi; 892 vi.defined.line = -1; 893 vi.firstUsage = pos; 894 identifiers[ident] = vi; 895 return false; 896 } 897 898 void define(string ident, TextPos pos) 899 { 900 if(auto vi = ident in identifiers) 901 { 902 if(pos > vi.firstUsage) 903 semanticErrorPos(pos, "identifier " ~ ident ~ " defined after usage"); 904 if(pos < vi.defined) 905 vi.defined = pos; 906 } 907 908 VersionInfo vi; 909 vi.firstUsage.line = int.max; 910 vi.defined = pos; 911 identifiers[ident] = vi; 912 } 913 } 914 915 class Options 916 { 917 string[] importDirs; 918 string[] stringImportDirs; 919 920 public /* debug & version handling */ { 921 bool unittestOn; 922 bool x64; 923 bool debugOn; 924 bool coverage; 925 bool doDoc; 926 bool noBoundsCheck; 927 bool gdcCompiler; 928 bool noDeprecated; 929 bool mixinAnalysis; 930 bool UFCSExpansions; 931 VersionDebug debugIds; 932 VersionDebug versionIds; 933 934 int changeCount; 935 936 bool setImportDirs(string[] dirs) 937 { 938 if(dirs == importDirs) 939 return false; 940 941 importDirs = dirs.dup; 942 changeCount++; 943 return true; 944 } 945 bool setStringImportDirs(string[] dirs) 946 { 947 if(dirs == stringImportDirs) 948 return false; 949 950 stringImportDirs = dirs.dup; 951 changeCount++; 952 return true; 953 } 954 bool setVersionIds(int level, string[] versionids) 955 { 956 if(!versionIds.reset(level, versionids)) 957 return false; 958 changeCount++; 959 return true; 960 } 961 bool setDebugIds(int level, string[] debugids) 962 { 963 if(!debugIds.reset(level, debugids)) 964 return false; 965 changeCount++; 966 return true; 967 } 968 969 bool versionEnabled(string ident) 970 { 971 int pre = versionPredefined(ident); 972 if(pre == 0) 973 return versionIds.defined(ident, TextPos()); 974 975 return pre > 0; 976 } 977 978 bool versionEnabled(int level) 979 { 980 return level <= versionIds.level; 981 } 982 983 bool debugEnabled(string ident) 984 { 985 return debugIds.defined(ident, TextPos()); 986 } 987 988 bool debugEnabled(int level) 989 { 990 return level <= debugIds.level; 991 } 992 993 int versionPredefined(string ident) 994 { 995 int* p = ident in sPredefinedVersions; 996 if(!p) 997 return 0; 998 if(*p) 999 return *p; 1000 1001 switch(ident) 1002 { 1003 case "unittest": 1004 return unittestOn ? 1 : -1; 1005 case "assert": 1006 return unittestOn || debugOn ? 1 : -1; 1007 case "D_Coverage": 1008 return coverage ? 1 : -1; 1009 case "D_Ddoc": 1010 return doDoc ? 1 : -1; 1011 case "D_NoBoundsChecks": 1012 return noBoundsCheck ? 1 : -1; 1013 case "CRuntime_DigitalMars": 1014 case "Win32": 1015 case "X86": 1016 case "D_InlineAsm_X86": 1017 return x64 ? -1 : 1; 1018 case "CRuntime_Microsoft": 1019 case "Win64": 1020 case "X86_64": 1021 case "D_InlineAsm_X86_64": 1022 case "D_LP64": 1023 return x64 ? 1 : -1; 1024 case "GNU": 1025 return gdcCompiler ? 1 : -1; 1026 case "DigitalMars": 1027 return gdcCompiler ? -1 : 1; 1028 default: 1029 assert(false, "inconsistent predefined versions"); 1030 } 1031 } 1032 1033 } 1034 }