From 4515917fe5ba373eafdad92634a8513e7a3d6e8f Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 10 Aug 2018 12:51:51 +0900 Subject: [PATCH 1/7] Remove trailing whitespace --- src/dmd/root/outbuffer.d | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dmd/root/outbuffer.d b/src/dmd/root/outbuffer.d index 4ef3528687a5..1695f889e45d 100644 --- a/src/dmd/root/outbuffer.d +++ b/src/dmd/root/outbuffer.d @@ -459,4 +459,3 @@ char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe } while (value); return buf[i .. $]; } - From 37332953cacc22900c60c37dc5cb65f1f3ae349d Mon Sep 17 00:00:00 2001 From: Geod24 Date: Tue, 14 Aug 2018 01:28:34 +0900 Subject: [PATCH 2/7] dmd.utils: Add toDString to slice a C-string --- src/dmd/utils.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dmd/utils.d b/src/dmd/utils.d index d07ad29d2b67..6c438ba75a4f 100644 --- a/src/dmd/utils.d +++ b/src/dmd/utils.d @@ -132,3 +132,9 @@ extern (C++) void escapePath(OutBuffer* buf, const(char)* fname) fname++; } } + +/// Slices a `\0`-terminated C-string, excluding the terminator +const(char)[] toDString (const(char)* s) pure nothrow @nogc +{ + return s ? s[0 .. strlen(s)] : null; +} From cb962ffd00b715148def8e5371127e01ef1c7341 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 10 Aug 2018 12:31:06 +0900 Subject: [PATCH 3/7] ModuleDeclaration: Provide a toString method --- src/dmd/dmodule.d | 16 +++++++++++----- src/dmd/module.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/dmd/dmodule.d b/src/dmd/dmodule.d index 52c4c2c72e0e..aa7a6b422041 100644 --- a/src/dmd/dmodule.d +++ b/src/dmd/dmodule.d @@ -38,6 +38,7 @@ import dmd.root.port; import dmd.semantic2; import dmd.semantic3; import dmd.target; +import dmd.utils; import dmd.visitor; version(Windows) { @@ -1337,19 +1338,24 @@ struct ModuleDeclaration this.isdeprecated = isdeprecated; } - extern (C++) const(char)* toChars() + extern (C++) const(char)* toChars() const { OutBuffer buf; if (packages && packages.dim) { - for (size_t i = 0; i < packages.dim; i++) + foreach (pid; *packages) { - Identifier pid = (*packages)[i]; - buf.writestring(pid.toChars()); + buf.writestring(pid.toString()); buf.writeByte('.'); } } - buf.writestring(id.toChars()); + buf.writestring(id.toString()); return buf.extractString(); } + + /// Provide a human readable representation + extern (D) const(char)[] toString() const + { + return this.toChars().toDString; + } } diff --git a/src/dmd/module.h b/src/dmd/module.h index 7482adee9cb7..24dc6f7e2817 100644 --- a/src/dmd/module.h +++ b/src/dmd/module.h @@ -177,7 +177,7 @@ struct ModuleDeclaration bool isdeprecated; // if it is a deprecated module Expression *msg; - const char *toChars(); + const char *toChars() const; }; #endif /* DMD_MODULE_H */ From d351c61414b7e98bd038207f0b0ad3995d184711 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Tue, 14 Aug 2018 14:30:20 +0900 Subject: [PATCH 4/7] Move DArray definition to DCompat If we want to use DArray from dmd.root it cannot reside in globals.h, so it is moved to a standalone header. --- src/dmd/globals.h | 9 +-------- src/dmd/root/dcompat.h | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 src/dmd/root/dcompat.h diff --git a/src/dmd/globals.h b/src/dmd/globals.h index 2f1d86c44056..1ed47a94e214 100644 --- a/src/dmd/globals.h +++ b/src/dmd/globals.h @@ -15,6 +15,7 @@ #pragma once #endif +#include "root/dcompat.h" #include "ctfloat.h" #include "outbuffer.h" #include "filename.h" @@ -292,14 +293,6 @@ typedef uint32_t d_uns32; typedef int64_t d_int64; typedef uint64_t d_uns64; -// Represents a D [ ] array -template -struct DArray -{ - size_t length; - T *ptr; -}; - // file location struct Loc { diff --git a/src/dmd/root/dcompat.h b/src/dmd/root/dcompat.h new file mode 100644 index 000000000000..e5026ead7d9d --- /dev/null +++ b/src/dmd/root/dcompat.h @@ -0,0 +1,25 @@ +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/dlang/dmd/blob/master/src/mars.h + */ + +#ifndef DMD_ROOT_DCOMPAT_H +#define DMD_ROOT_DCOMPAT_H + +#ifdef __DMC__ +#pragma once +#endif + +/// Represents a D [ ] array +template +struct DArray +{ + size_t length; + T *ptr; +}; + +#endif /* DMD_ROOT_DCOMPAT_H */ From 961bee22d734de69ad1f871b906331cac732528f Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 10 Aug 2018 12:45:04 +0900 Subject: [PATCH 5/7] RootObject: Add a toString method At the moment it is simply doing an `strlen` on the result of `toChars`, but as more code uses it, we can start to move away from that. Another reason to do it this way is because code can rely on the string being null terminated, so this way `x.toString().ptr` is still a drop-in replacement of `x.toChars()`. --- src/.dscanner.ini | 2 +- src/dmd/identifier.d | 2 +- src/dmd/root/object.h | 4 ++++ src/dmd/root/rootobject.d | 8 ++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/.dscanner.ini b/src/.dscanner.ini index d6e14cdc02e9..45dbfd836814 100644 --- a/src/.dscanner.ini +++ b/src/.dscanner.ini @@ -115,5 +115,5 @@ redundant_storage_classes="enabled" redundant_storage_classes="-dmd.id,-dmd.toir,-dmd.tokens" trust_too_much="-dmd.root.longdouble" builtin_property_names_check="-dmd.backend.obj,-dmd.backend.code,-dmd.backend.cc" -object_const_check="-dmd.dtemplate,-dmd.backend.outbuf" +object_const_check="-dmd.dtemplate,-dmd.backend.outbuf,-dmd.root.rootobject" final_attribute_check="-dmd.statement" ; https://github.com/dlang/dmd/pull/856 diff --git a/src/dmd/identifier.d b/src/dmd/identifier.d index 3f7a7cd8a425..bd4648a6aa15 100644 --- a/src/dmd/identifier.d +++ b/src/dmd/identifier.d @@ -74,7 +74,7 @@ nothrow: return name; } - extern (D) const(char)[] toString() const pure + extern (D) override const(char)[] toString() const pure { return name[0 .. len]; } diff --git a/src/dmd/root/object.h b/src/dmd/root/object.h index b990693b3d41..e3067e91dbe5 100644 --- a/src/dmd/root/object.h +++ b/src/dmd/root/object.h @@ -16,6 +16,7 @@ #pragma once #endif +#include "dcompat.h" #include typedef size_t hash_t; @@ -57,6 +58,9 @@ class RootObject virtual void print(); virtual const char *toChars(); + /// This function is `extern(D)` and should not be called from C++, + /// as the ABI does not match on some platforms + virtual DArray toString(); virtual void toBuffer(OutBuffer *buf); /** diff --git a/src/dmd/root/rootobject.d b/src/dmd/root/rootobject.d index 44f2d7cdc7e4..7ece1b69836b 100644 --- a/src/dmd/root/rootobject.d +++ b/src/dmd/root/rootobject.d @@ -62,6 +62,14 @@ extern (C++) class RootObject assert(0); } + /// + extern(D) const(char)[] toString() + { + import core.stdc.string : strlen; + auto p = this.toChars(); + return p[0 .. strlen(p)]; + } + void toBuffer(OutBuffer* buf) nothrow pure @nogc @safe { assert(0); From 12ac64c842b4f96553e57a2f7f0607af1cb88e94 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 10 Aug 2018 12:37:21 +0900 Subject: [PATCH 6/7] hdrgen: Add some `xxxToString` methods to wrap `xxxToChars` strlen must die, right ? --- src/dmd/hdrgen.d | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/dmd/hdrgen.d b/src/dmd/hdrgen.d index 550a44d563a6..ef0fef36b18c 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -3347,6 +3347,16 @@ extern (C++) const(char)* stcToChars(ref StorageClass stc) return null; } + +/** + * Returns: + * a human readable representation of `stc` + */ +extern (D) const(char)[] stcToString(ref StorageClass stc) +{ + return stcToChars(stc).toDString; +} + extern (C++) void trustToBuffer(OutBuffer* buf, TRUST trust) { const(char)* p = trustToChars(trust); @@ -3354,7 +3364,19 @@ extern (C++) void trustToBuffer(OutBuffer* buf, TRUST trust) buf.writestring(p); } +/** + * Returns: + * a human readable representation of `trust`, + * which is the token `trust` corresponds to + */ extern (C++) const(char)* trustToChars(TRUST trust) +{ + /// Works because we return a literal + return trustToString(trust).ptr; +} + +/// Ditto +extern (D) string trustToString(TRUST trust) { final switch (trust) { @@ -3416,7 +3438,18 @@ extern (C++) void protectionToBuffer(OutBuffer* buf, Prot prot) } } +/** + * Returns: + * a human readable representation of `kind` + */ extern (C++) const(char)* protectionToChars(Prot.Kind kind) +{ + // Null terminated because we return a literal + return protectionToString(kind).ptr; +} + +/// Ditto +extern (D) string protectionToString(Prot.Kind kind) { final switch (kind) { From a586bcf066f18a442595edb4a20a72d41164b078 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 10 Aug 2018 13:02:50 +0900 Subject: [PATCH 7/7] Convert the JSON code to use D slices instead of pointers The conversion is pretty straightforward, but required in order to convert globals. Since globals are still C-style string, a local toDString is used. It can later be removed as globals are converted to D slices. --- src/dmd/json.d | 160 ++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/src/dmd/json.d b/src/dmd/json.d index 1937393edee9..d222c623be6a 100644 --- a/src/dmd/json.d +++ b/src/dmd/json.d @@ -33,6 +33,8 @@ import dmd.id; import dmd.identifier; import dmd.mtype; import dmd.root.outbuffer; +import dmd.root.rootobject; +import dmd.utils; import dmd.visitor; version(Windows) { @@ -47,13 +49,14 @@ private extern (C++) final class ToJsonVisitor : Visitor public: OutBuffer* buf; int indentLevel; - const(char)* filename; + const(char)[] filename; extern (D) this(OutBuffer* buf) { this.buf = buf; } + void indent() { if (buf.offset >= 1 && buf.data[buf.offset - 1] == '\n') @@ -83,11 +86,10 @@ public: buf.writeByte('\"'); } - void stringPart(const(char)* s) + extern(D) void stringPart(const(char)[] s) { - for (; *s; s++) + foreach (char c; s) { - char c = cast(char)*s; switch (c) { case '\n': @@ -128,7 +130,7 @@ public: /********************************* * Encode string into buf, and wrap it in double quotes. */ - void value(const(char)* s) + extern(D) void value(const(char)[] s) { stringStart(); stringPart(s); @@ -153,7 +155,7 @@ public: /********************************* * Item is an intented value and a comma, for use in arrays */ - void item(const(char)* s) + extern(D) void item(const(char)[] s) { indent(); value(s); @@ -221,7 +223,7 @@ public: } // Json object property functions - void propertyStart(const(char)* name) + extern(D) void propertyStart(const(char)[] name) { indent(); value(name); @@ -235,7 +237,7 @@ public: name = the name of the object property s = the string value of the object property */ - void property(const(char)* name, const(char)* s) + extern(D) void property(const(char)[] name, const(char)[] s) { if (s is null) return; @@ -251,7 +253,7 @@ public: name = the name of the object property s = the string value of the object property */ - void requiredProperty(const(char)* name, const(char)* s) + extern(D) void requiredProperty(const(char)[] name, const(char)[] s) { propertyStart(name); if (s is null) @@ -261,21 +263,21 @@ public: comma(); } - void property(const(char)* name, int i) + extern(D) void property(const(char)[] name, int i) { propertyStart(name); value(i); comma(); } - void propertyBool(const(char)* name, const bool b) + extern(D) void propertyBool(const(char)[] name, const bool b) { propertyStart(name); valueBool(b); comma(); } - void property(const(char)* name, TRUST trust) + extern(D) void property(const(char)[] name, TRUST trust) { final switch (trust) { @@ -295,7 +297,7 @@ public: } } - void property(const(char)* name, PURE purity) + extern(D) void property(const(char)[] name, PURE purity) { final switch (purity) { @@ -318,7 +320,7 @@ public: } } - void property(const(char)* name, const LINK linkage) + extern(D) void property(const(char)[] name, const LINK linkage) { final switch (linkage) { @@ -352,7 +354,7 @@ public: } } - void propertyStorageClass(const(char)* name, StorageClass stc) + extern(D) void propertyStorageClass(const(char)[] name, StorageClass stc) { stc &= STCStorageClass; if (stc) @@ -361,22 +363,21 @@ public: arrayStart(); while (stc) { - const(char)* p = stcToChars(stc); - assert(p); + auto p = stcToString(stc); + assert(p.length); item(p); } arrayEnd(); } } - void property(const(char)* linename, const(char)* charname, Loc* loc) + extern(D) void property(const(char)[] linename, const(char)[] charname, Loc* loc) { if (loc) { - const(char)* filename = loc.filename; - if (filename) + if (auto filename = loc.filename.toDString) { - if (!this.filename || strcmp(filename, this.filename)) + if (filename != this.filename) { this.filename = filename; property("file", filename); @@ -391,26 +392,26 @@ public: } } - void property(const(char)* name, Type type) + extern(D) void property(const(char)[] name, Type type) { if (type) { - property(name, type.toChars()); + property(name, type.toString()); } } - void property(const(char)* name, const(char)* deconame, Type type) + extern(D) void property(const(char)[] name, const(char)[] deconame, Type type) { if (type) { if (type.deco) - property(deconame, type.deco); + property(deconame, type.deco.toDString); else - property(name, type.toChars()); + property(name, type.toString()); } } - void property(const(char)* name, Parameters* parameters) + extern(D) void property(const(char)[] name, Parameters* parameters) { if (parameters is null || parameters.dim == 0) return; @@ -423,11 +424,11 @@ public: Parameter p = (*parameters)[i]; objectStart(); if (p.ident) - property("name", p.ident.toChars()); + property("name", p.ident.toString()); property("type", "deco", p.type); propertyStorageClass("storageClass", p.storageClass); if (p.defaultArg) - property("default", p.defaultArg.toChars()); + property("default", p.defaultArg.toString()); objectEnd(); } } @@ -441,17 +442,17 @@ public: return; if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes { - property("name", s.toChars()); - property("kind", s.kind()); + property("name", s.toString()); + property("kind", s.kind.toDString); } if (s.prot().kind != Prot.Kind.public_) // TODO: How about package(names)? - property("protection", protectionToChars(s.prot().kind)); + property("protection", protectionToString(s.prot().kind)); if (EnumMember em = s.isEnumMember()) { if (em.origValue) - property("value", em.origValue.toChars()); + property("value", em.origValue.toString()); } - property("comment", s.comment); + property("comment", s.comment.toDString); property("line", "char", &s.loc); } @@ -466,11 +467,11 @@ public: // Emit originalType if it differs from type if (d.type != d.originalType && d.originalType) { - const(char)* ostr = d.originalType.toChars(); + auto ostr = d.originalType.toString(); if (d.type) { - const(char)* tstr = d.type.toChars(); - if (strcmp(tstr, ostr)) + auto tstr = d.type.toString(); + if (ostr != tstr) { //printf("tstr = %s, ostr = %s\n", tstr, ostr); property("originalType", ostr); @@ -487,7 +488,7 @@ public: if (td.onemember && td.onemember.isCtorDeclaration()) property("name", "this"); // __ctor -> this else - property("name", td.ident.toChars()); // Foo(T) -> Foo + property("name", td.ident.toString()); // Foo(T) -> Foo } /* ========================================================================== */ @@ -499,11 +500,11 @@ public: { objectStart(); if (s.md) - property("name", s.md.toChars()); - property("kind", s.kind()); - filename = s.srcfile.toChars(); + property("name", s.md.toString()); + property("kind", s.kind.toDString); + filename = s.srcfile.toString(); property("file", filename); - property("comment", s.comment); + property("comment", s.comment.toDString); propertyStart("members"); arrayStart(); for (size_t i = 0; i < s.members.dim; i++) @@ -526,20 +527,20 @@ public: for (size_t i = 0; i < s.packages.dim; i++) { Identifier pid = (*s.packages)[i]; - stringPart(pid.toChars()); + stringPart(pid.toString()); buf.writeByte('.'); } } - stringPart(s.id.toChars()); + stringPart(s.id.toString()); stringEnd(); comma(); - property("kind", s.kind()); - property("comment", s.comment); + property("kind", s.kind.toDString); + property("comment", s.comment.toDString); property("line", "char", &s.loc); if (s.prot().kind != Prot.Kind.public_) - property("protection", protectionToChars(s.prot().kind)); + property("protection", protectionToString(s.prot().kind)); if (s.aliasId) - property("alias", s.aliasId.toChars()); + property("alias", s.aliasId.toString()); bool hasRenamed = false; bool hasSelective = false; for (size_t i = 0; i < s.aliases.dim; i++) @@ -562,7 +563,7 @@ public: Identifier name = s.names[i]; Identifier _alias = s.aliases[i]; if (_alias) - property(_alias.toChars(), name.toChars()); + property(_alias.toString(), name.toString()); } objectEnd(); } @@ -571,11 +572,10 @@ public: // import foo : target1; propertyStart("selective"); arrayStart(); - for (size_t i = 0; i < s.names.dim; i++) + foreach (i, name; s.names) { - Identifier name = s.names[i]; if (!s.aliases[i]) - item(name.toChars()); + item(name.toString()); } arrayEnd(); } @@ -634,7 +634,7 @@ public: { if (cd.baseClass && cd.baseClass.ident != Id.Object) { - property("base", cd.baseClass.toPrettyChars(true)); + property("base", cd.baseClass.toPrettyChars(true).toDString); } if (cd.interfaces.length) { @@ -642,7 +642,7 @@ public: arrayStart(); foreach (b; cd.interfaces) { - item(b.sym.toPrettyChars(true)); + item(b.sym.toPrettyChars(true).toDString); } arrayEnd(); } @@ -676,7 +676,7 @@ public: for (size_t i = 0; i < d.foverrides.dim; i++) { FuncDeclaration fd = d.foverrides[i]; - item(fd.toPrettyChars()); + item(fd.toPrettyChars().toDString); } arrayEnd(); } @@ -705,7 +705,7 @@ public: { TemplateParameter s = (*d.parameters)[i]; objectStart(); - property("name", s.ident.toChars()); + property("name", s.ident.toString()); TemplateTypeParameter type = s.isTemplateTypeParameter(); if (type) { @@ -722,9 +722,9 @@ public: property("kind", "value"); property("type", "deco", value.valType); if (value.specValue) - property("specValue", value.specValue.toChars()); + property("specValue", value.specValue.toString()); if (value.defaultValue) - property("defaultValue", value.defaultValue.toChars()); + property("defaultValue", value.defaultValue.toString()); } TemplateAliasParameter _alias = s.isTemplateAliasParameter(); if (_alias) @@ -732,9 +732,9 @@ public: property("kind", "alias"); property("type", "deco", _alias.specType); if (_alias.specAlias) - property("specAlias", _alias.specAlias.toChars()); + property("specAlias", _alias.specAlias.toString()); if (_alias.defaultAlias) - property("defaultAlias", _alias.defaultAlias.toChars()); + property("defaultAlias", _alias.defaultAlias.toString()); } TemplateTupleParameter tuple = s.isTemplateTupleParameter(); if (tuple) @@ -747,7 +747,7 @@ public: Expression expression = d.constraint; if (expression) { - property("constraint", expression.toChars()); + property("constraint", expression.toString()); } propertyStart("members"); arrayStart(); @@ -806,7 +806,7 @@ public: objectStart(); jsonProperties(d); if (d._init) - property("init", d._init.toChars()); + property("init", d._init.toString()); if (d.isField()) property("offset", d.offset); if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT) @@ -850,8 +850,8 @@ public: private void generateCompilerInfo() { objectStart(); - requiredProperty("vendor", global.compiler.vendor); - requiredProperty("version", global._version); + requiredProperty("vendor", global.compiler.vendor.toDString); + requiredProperty("version", global._version.toDString); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); property("size_t", size_t.sizeof); @@ -900,7 +900,7 @@ public: { foreach (const versionid; *global.versionids) { - item(versionid.toChars()); + item(versionid.toString()); } } arrayEnd(); @@ -921,10 +921,10 @@ public: private void generateBuildInfo() { objectStart(); - requiredProperty("cwd", getcwd(null, 0)); - requiredProperty("argv0", global.params.argv0); - requiredProperty("config", global.inifilename); - requiredProperty("libName", global.params.libname); + requiredProperty("cwd", getcwd(null, 0).toDString); + requiredProperty("argv0", global.params.argv0.toDString); + requiredProperty("config", global.inifilename.toDString); + requiredProperty("libName", global.params.libname.toDString); propertyStart("importPaths"); arrayStart(); @@ -932,7 +932,7 @@ public: { foreach (importPath; *global.params.imppath) { - item(importPath); + item(importPath.toDString); } } arrayEnd(); @@ -941,7 +941,7 @@ public: arrayStart(); foreach (objfile; global.params.objfiles) { - item(objfile); + item(objfile.toDString); } arrayEnd(); @@ -949,7 +949,7 @@ public: arrayStart(); foreach (lib; global.params.libfiles) { - item(lib); + item(lib.toDString); } arrayEnd(); @@ -957,13 +957,13 @@ public: arrayStart(); foreach (ddocFile; global.params.ddocfiles) { - item(ddocFile); + item(ddocFile.toDString); } arrayEnd(); - requiredProperty("mapFile", global.params.mapfile); - requiredProperty("resourceFile", global.params.resfile); - requiredProperty("defFile", global.params.deffile); + requiredProperty("mapFile", global.params.mapfile.toDString); + requiredProperty("resourceFile", global.params.resfile.toDString); + requiredProperty("defFile", global.params.deffile.toDString); objectEnd(); } @@ -981,8 +981,8 @@ public: foreach (m; Module.amodules) { objectStart(); - requiredProperty("name", m.md ? m.md.toChars() : null); - requiredProperty("file", m.srcfile.toChars()); + requiredProperty("name", m.md ? m.md.toString() : null); + requiredProperty("file", m.srcfile.toString()); propertyBool("isRoot", m.isRoot()); if(m.contentImportedFiles.dim > 0) { @@ -990,7 +990,7 @@ public: arrayStart(); foreach (file; m.contentImportedFiles) { - item(file); + item(file.toDString); } arrayEnd(); } @@ -1088,7 +1088,7 @@ JsonFieldFlags tryParseJsonField(const(char)* fieldName) Determines and returns the compiler interface which is one of `dmd`, `ldc`, `gdc` or `sdc`. Returns `null` if no interface can be determined. */ -private const(char)* determineCompilerInterface() +private extern(D) string determineCompilerInterface() { if (!strcmp(global.compiler.vendor, "Digital Mars D")) return "dmd";