diff --git a/changelog/headers.dd b/changelog/headers.dd new file mode 100644 index 000000000000..17fd31b8d40d --- /dev/null +++ b/changelog/headers.dd @@ -0,0 +1,104 @@ +Added experimental `C++` header generation from `extern(C|C++)` declarations + +DMD can now write C++ header files which contain bindings for declarations +in existing D files that were marked as `extern(C)` or `extern(C++)`. + +This feature is available via the following command line switches: +- `-HC`: Write the generated headers to standard output +- `-HCf=`: Write the generated headers to file +- `-HCd=`: Write the generated headers to file (not implemented yet) + +Consider the following example: + +``` +module a; + +extern(C) int foo(int a) { ... } +extern(C++) void bar() { ... } +void ignored() { ... } +``` +``` +module b; + +extern (C++) struct S +{ + string name; + this (string name) { ... } + bool bar() { ... } +} +``` +``` +module c; + +import a, b; + +extern (C++) class C +{ + S[] s; + this () {} + bool bar() { ...} +} + +``` + +Compiling these modules with `dmd -c -o- -HC a.d b.d c.d` will generate the following header: + +``` +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long long long +#define _d_ulong unsigned long long +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; +#define _d_null NULL + +// Parsing module a +// Parsing module b +// Parsing module c +struct S; +extern "C" _d_int foo(_d_int a); + +extern _d_void bar(); + +// ignoring function a.ignored because of linkage +struct S +{ + DArray< _d_char > name; + S(DArray< _d_char > name); + _d_bool bar(); + S() : name() {} +}; + +class C +{ +public: + DArray< S > s; + C(); + virtual _d_bool bar(); +}; +``` + +Refer to the $(LINK2 $(ROOT_DIR)/spec/cpp_interface.html, documentation) for further information regarding C++ interoperation. + +Note that this feature is considered experimental and might not work correctly sometimes. +Please open an issue in the $(LINK2 https://issues.dlang.org, bug tracker) if you encounter a bug while using this feature. diff --git a/src/build.d b/src/build.d index 8fbea2d6156b..27a92419112b 100755 --- a/src/build.d +++ b/src/build.d @@ -1154,7 +1154,7 @@ auto sourceFiles() cli.d clone.d compiler.d complex.d cond.d constfold.d cppmangle.d cppmanglewin.d ctfeexpr.d ctorflow.d dcast.d dclass.d declaration.d delegatize.d denum.d dimport.d dinifile.d dinterpret.d dmacro.d dmangle.d dmodule.d doc.d dscope.d dstruct.d dsymbol.d dsymbolsem.d - dtemplate.d dversion.d env.d escape.d expression.d expressionsem.d func.d hdrgen.d impcnvtab.d + dtemplate.d dtoh.d dversion.d env.d escape.d expression.d expressionsem.d func.d hdrgen.d impcnvtab.d imphint.d init.d initsem.d inline.d inlinecost.d intrange.d json.d lambdacomp.d lib.d libelf.d libmach.d libmscoff.d libomf.d link.d mars.d mtype.d nogc.d nspace.d ob.d objc.d opover.d optimize.d parse.d parsetimevisitor.d permissivevisitor.d printast.d safe.d sapply.d scanelf.d scanmach.d diff --git a/src/dmd/cli.d b/src/dmd/cli.d index a299f42fec96..dd1b87cbaa08 100644 --- a/src/dmd/cli.d +++ b/src/dmd/cli.d @@ -370,6 +370,15 @@ dmd -cov -unittest myprog.d Option("Hf=", "write 'header' file to filename" ), + Option("HC", + "generate C++ 'header' file" + ), + Option("HCd=", + "write C++ 'header' file to directory" + ), + Option("HCf=", + "write C++ 'header' file to filename" + ), Option("-help", "print help and exit" ), diff --git a/src/dmd/dtoh.d b/src/dmd/dtoh.d new file mode 100644 index 000000000000..869142211434 --- /dev/null +++ b/src/dmd/dtoh.d @@ -0,0 +1,1697 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * This module contains the implementation of the C++ header generation available through + * the command line switch -Hc. + * + * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtohd, _dtoh.d) + * Documentation: https://dlang.org/phobos/dmd_dtoh.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtoh.d + */ +module dmd.dtoh; + +import core.stdc.stdio; +import core.stdc.string; +import core.stdc.ctype; + +import dmd.astcodegen; +import dmd.arraytypes; +import dmd.globals; +import dmd.identifier; +import dmd.json; +import dmd.mars; +import dmd.root.array; +import dmd.root.file; +import dmd.root.filename; +import dmd.root.rmem; +import dmd.visitor; +import dmd.tokens; + +import dmd.root.outbuffer; +import dmd.utils; + +//debug = Debug_DtoH; +enum isBuildingCompiler = false; + +private struct DMDType +{ + __gshared Identifier c_long; + __gshared Identifier c_ulong; + __gshared Identifier c_longlong; + __gshared Identifier c_ulonglong; + __gshared Identifier c_long_double; + __gshared Identifier AssocArray; + __gshared Identifier Array; + + static void _init() + { + c_long = Identifier.idPool("__c_long"); + c_ulong = Identifier.idPool("__c_ulong"); + c_longlong = Identifier.idPool("__c_longlong"); + c_ulonglong = Identifier.idPool("__c_ulonglong"); + c_long_double = Identifier.idPool("__c_long_double"); + + if (isBuildingCompiler) + { + AssocArray = Identifier.idPool("AssocArray"); + Array = Identifier.idPool("Array"); + } + + } +} + +private struct DMDModule +{ + __gshared Identifier identifier; + __gshared Identifier root; + __gshared Identifier visitor; + __gshared Identifier parsetimevisitor; + __gshared Identifier permissivevisitor; + __gshared Identifier strictvisitor; + __gshared Identifier transitivevisitor; + __gshared Identifier dmd; + static void _init() + { + identifier = Identifier.idPool("identifier"); + root = Identifier.idPool("root"); + visitor = Identifier.idPool("visitor"); + parsetimevisitor = Identifier.idPool("parsetimevisitor"); + permissivevisitor = Identifier.idPool("permissivevisitor"); + strictvisitor = Identifier.idPool("strictvisitor"); + transitivevisitor = Identifier.idPool("transitivevisitor"); + dmd = Identifier.idPool("dmd"); + } +} + +private struct DMDClass +{ + __gshared Identifier ID; ////Identifier + __gshared Identifier Visitor; + __gshared Identifier ParseTimeVisitor; + static void _init() + { + ID = Identifier.idPool("Identifier"); + Visitor = Identifier.idPool("Visitor"); + ParseTimeVisitor = Identifier.idPool("ParseTimeVisitor"); + } + +} + +private bool isIdentifierClass(ASTCodegen.ClassDeclaration cd) +{ + return (cd.ident == DMDClass.ID && + cd.parent !is null && + cd.parent.ident == DMDModule.identifier && + cd.parent.parent && cd.parent.parent.ident == DMDModule.dmd && + !cd.parent.parent.parent); +} + +private bool isVisitorClass(ASTCodegen.ClassDeclaration cd) +{ + for (auto cdb = cd; cdb; cdb = cdb.baseClass) + { + if (cdb.ident == DMDClass.Visitor || + cdb.ident == DMDClass.ParseTimeVisitor) + return true; + } + return false; +} + +private bool isIgnoredModule(ASTCodegen.Module m) +{ + if (!m) + return true; + + // Ignore dmd.root + if (m.parent && m.parent.ident == DMDModule.root && + m.parent.parent && m.parent.parent.ident == DMDModule.dmd && + !m.parent.parent.parent) + { + return true; + } + + // Ignore dmd.visitor and derivatives + if ((m.ident == DMDModule.visitor || + m.ident == DMDModule.parsetimevisitor || + m.ident == DMDModule.permissivevisitor || + m.ident == DMDModule.strictvisitor || + m.ident == DMDModule.transitivevisitor) && + m.parent && m.parent.ident == DMDModule.dmd && + !m.parent.parent) + { + return true; + } + return false; +} + +private bool isFrontendModule(ASTCodegen.Module m) +{ + if (!m || !m.parent) + return false; + + // Ignore dmd.root + if (m.parent.ident == DMDModule.root && + m.parent.parent && m.parent.parent.ident == DMDModule.dmd && + !m.parent.parent.parent) + { + return false; + } + + // Ignore dmd.visitor and derivatives + if ((m.ident == DMDModule.visitor || + m.ident == DMDModule.parsetimevisitor || + m.ident == DMDModule.permissivevisitor || + m.ident == DMDModule.strictvisitor || + m.ident == DMDModule.transitivevisitor) && + m.parent && m.parent.ident == DMDModule.dmd && + !m.parent.parent) + { + return false; + } + return ((m.parent.ident == DMDModule.dmd && !m.parent.parent) || + (m.parent.parent.ident == DMDModule.dmd && !m.parent.parent.parent)); +} + +private void initialize() +{ + __gshared bool initialized; + + if (!initialized) + { + initialized = true; + + DMDType._init(); + if (isBuildingCompiler) + { + DMDModule._init(); + DMDClass._init(); + } + } +} + +void genCppHdrFiles(ref Modules ms) +{ + initialize(); + + OutBuffer buf; + buf.writestring("#pragma once\n"); + buf.writeByte('\n'); + buf.printf("// Automatically generated by dmd -HC\n"); + buf.writeByte('\n'); + buf.writestring("#include \n"); + buf.writestring("#include \n"); + buf.writestring("#include \n"); + buf.writestring("#include \n"); + buf.writeByte('\n'); + buf.writestring("#define _d_void void\n"); + buf.writestring("#define _d_bool bool\n"); + buf.writestring("#define _d_byte signed char\n"); + buf.writestring("#define _d_ubyte unsigned char\n"); + buf.writestring("#define _d_short short\n"); + buf.writestring("#define _d_ushort unsigned short\n"); + buf.writestring("#define _d_int int\n"); + buf.writestring("#define _d_uint unsigned\n"); + if (global.params.isLP64) + { + buf.writestring("#define _d_long long\n"); + buf.writestring("#define _d_ulong unsigned long\n"); + } + else + { + buf.writestring("#define _d_long long long\n"); + buf.writestring("#define _d_ulong unsigned long long\n"); + } + buf.writestring("#define _d_float float\n"); + buf.writestring("#define _d_double double\n"); + buf.writestring("#define _d_real long double\n"); + buf.writestring("#define _d_char char\n"); + buf.writestring("#define _d_wchar wchar_t\n"); + buf.writestring("#define _d_dchar unsigned\n"); + buf.writestring("typedef _d_long d_int64;\n"); + buf.writestring("\n"); + buf.writestring("#define _d_null NULL\n"); + buf.writestring("\n\n"); + + OutBuffer check; + OutBuffer done; + OutBuffer decl; + scope v = new ToCppBuffer!ASTCodegen(&check, &buf, &done, &decl); + foreach (m; ms) + { + //printf("// Parsing module %s\n", m.toPrettyChars()); + buf.printf("// Parsing module %s\n", m.toPrettyChars()); + m.accept(v); + } + buf.write(&done); + buf.write(&decl); + //printf("%s\n", decl.peekSlice().ptr); + + + debug (Debug_DtoH) + { + buf.writestring(` +#if OFFSETS + template + size_t getSlotNumber(int dummy, ...) + { + T c; + va_list ap; + va_start(ap, dummy); + void *f = va_arg(ap, void*); + for (size_t i = 0; ; i++) + { + if ( (*(void***)&c)[i] == f) + return i; + } + va_end(ap); + } + + void testOffsets() + { +`); + buf.write(&check); + buf.writestring(` + } +#endif +`); + } + + if (global.params.cxxhdrname is null) + { + // Write to stdout; assume it succeeds + size_t n = fwrite(buf[].ptr, 1, buf.length, stdout); + assert(n == buf.length); // keep gcc happy about return values + } + else + { + const(char)[] name = FileName.combine(global.params.cxxhdrdir, global.params.cxxhdrname); + writeFile(Loc.initial, name, buf[]); + } +} + +/**************************************************** + */ +extern(C++) final class ToCppBuffer(AST) : Visitor +{ + alias visit = Visitor.visit; +public: + bool[void*] visited; + bool[void*] forwarded; + OutBuffer* fwdbuf; + OutBuffer* checkbuf; + OutBuffer* donebuf; + OutBuffer* buf; + AST.AggregateDeclaration adparent; + AST.ClassDeclaration cdparent; + AST.TemplateDeclaration tdparent; + Identifier ident; + LINK linkage = LINK.d; + bool forwardedAA; + + this(OutBuffer* checkbuf, OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf) + { + this.checkbuf = checkbuf; + this.fwdbuf = fwdbuf; + this.donebuf = donebuf; + this.buf = buf; + } + + private void indent() + { + if (adparent) + buf.writestring(" "); + } + + override void visit(AST.Dsymbol s) + { + debug (Debug_DtoH) + { + printf("[AST.Dsymbol enter] %s\n", s.toChars()); + scope(exit) printf("[AST.Dsymbol exit] %s\n", s.toChars()); + } + + if (isBuildingCompiler && s.getModule() && s.getModule().isFrontendModule()) + { + indent(); + buf.printf("// ignored %s %s\n", s.kind(), s.toPrettyChars()); + } + } + + override void visit(AST.Import i) + { + debug (Debug_DtoH) + { + printf("[AST.Import enter] %s\n", i.toChars()); + scope(exit) printf("[AST.Import exit] %s\n", i.toChars()); + } + } + + override void visit(AST.AttribDeclaration pd) + { + debug (Debug_DtoH) + { + printf("[AST.AttribDeclaration enter] %s\n", pd.toChars()); + scope(exit) printf("[AST.AttribDeclaration exit] %s\n", pd.toChars()); + } + Dsymbols* decl = pd.include(null); + if (!decl) + return; + + foreach (s; *decl) + { + if (adparent || s.prot().kind >= AST.Prot.Kind.public_) + s.accept(this); + } + } + + override void visit(AST.LinkDeclaration ld) + { + debug (Debug_DtoH) + { + printf("[AST.LinkDeclaration enter] %s\n", ld.toChars()); + scope(exit) printf("[AST.LinkDeclaration exit] %s\n", ld.toChars()); + } + auto save = linkage; + linkage = ld.linkage; + if (ld.linkage != LINK.c && ld.linkage != LINK.cpp) + { + indent(); + buf.printf("// ignoring %s block because of linkage\n", ld.toPrettyChars()); + } + else + { + visit(cast(AST.AttribDeclaration)ld); + } + linkage = save; + } + + override void visit(AST.Module m) + { + debug (Debug_DtoH) + { + printf("[AST.Module enter] %s\n", m.toChars()); + scope(exit) printf("[AST.Module exit] %s\n", m.toChars()); + } + foreach (s; *m.members) + { + if (s.prot().kind < AST.Prot.Kind.public_) + continue; + s.accept(this); + } + } + + override void visit(AST.FuncDeclaration fd) + { + debug (Debug_DtoH) + { + printf("[AST.FuncDeclaration enter] %s\n", fd.toChars()); + scope(exit) printf("[AST.FuncDeclaration exit] %s\n", fd.toChars()); + } + if (cast(void*)fd in visited) + return; + if (isBuildingCompiler && fd.getModule() && fd.getModule().isIgnoredModule()) + return; + + // printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars()); + visited[cast(void*)fd] = true; + + auto tf = cast(AST.TypeFunction)fd.type; + indent(); + if (!tf || !tf.deco) + { + buf.printf("// ignoring function %s because semantic hasn't been run\n", fd.toPrettyChars()); + return; + } + if (tf.linkage != LINK.c && tf.linkage != LINK.cpp) + { + buf.printf("// ignoring function %s because of linkage\n", fd.toPrettyChars()); + return; + } + if (!adparent && !fd.fbody) + { + buf.printf("// ignoring function %s because it's extern\n", fd.toPrettyChars()); + return; + } + + if (tf.linkage == LINK.c) + buf.writestring("extern \"C\" "); + else if (!adparent) + buf.writestring("extern "); + if (adparent && fd.isStatic()) + buf.writestring("static "); + if (adparent && fd.vtblIndex != -1) + { + if (!fd.isOverride()) + buf.writestring("virtual "); + + auto s = adparent.search(Loc.initial, fd.ident); + if (!(adparent.storage_class & AST.STC.abstract_) && + !(cast(AST.ClassDeclaration)adparent).isAbstract() && + s is fd && !fd.overnext) + { + auto save = buf; + buf = checkbuf; + buf.writestring(" assert(getSlotNumber<"); + buf.writestring(adparent.ident.toChars()); + buf.writestring(">(0, &"); + buf.writestring(adparent.ident.toChars()); + buf.writestring("::"); + buf.writestring(fd.ident.toChars()); + buf.printf(") == %d);\n", fd.vtblIndex); + buf = save; + } + } + + if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11) + buf.printf("private: "); + funcToBuffer(tf, fd); + if (adparent && tf.isConst()) + { + bool fdOverridesAreConst = true; + foreach (fdv; fd.foverrides) + { + auto tfv = cast(AST.TypeFunction)fdv.type; + if (!tfv.isConst()) + { + fdOverridesAreConst = false; + break; + } + } + + buf.writestring(fdOverridesAreConst ? " const" : " /* const */"); + } + if (adparent && fd.isAbstract()) + buf.writestring(" = 0"); + if (adparent && fd.isDisabled && global.params.cplusplus >= CppStdRevision.cpp11) + buf.printf(" = delete"); + buf.printf(";\n"); + if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11) + buf.printf("public:\n"); + if (!adparent) + buf.printf("\n"); + } + + override void visit(AST.UnitTestDeclaration utd) + { + debug (Debug_DtoH) + { + printf("[AST.UnitTestDeclaration enter] %s\n", utd.toChars()); + scope(exit) printf("[AST.UnitTestDeclaration exit] %s\n", utd.toChars()); + } + } + + override void visit(AST.VarDeclaration vd) + { + debug (Debug_DtoH) + { + printf("[AST.VarDeclaration enter] %s\n", vd.toChars()); + scope(exit) printf("[AST.VarDeclaration exit] %s\n", vd.toChars()); + } + if (cast(void*)vd in visited) + return; + if (isBuildingCompiler && vd.getModule() && vd.getModule().isIgnoredModule()) + return; + + visited[cast(void*)vd] = true; + + if (vd.alignment != uint.max) + { + indent(); + buf.printf("// Ignoring var %s alignment %u\n", vd.toChars(), vd.alignment); + } + + if (vd.storage_class & AST.STC.manifest && + vd.type.isintegral() && + vd._init && vd._init.isExpInitializer()) + { + indent(); + buf.writestring("#define "); + buf.writestring(vd.ident.toChars()); + buf.writestring(" "); + auto e = AST.initializerToExpression(vd._init); + if (e.type.ty == AST.Tbool) + buf.printf("%d", e.toInteger()); + else + AST.initializerToExpression(vd._init).accept(this); + buf.writestring("\n"); + if (!adparent) + buf.printf("\n"); + return; + } + + if (tdparent && vd.type && !vd.type.deco) + { + indent(); + if (linkage != LINK.c && linkage != LINK.cpp) + { + buf.printf("// ignoring variable %s because of linkage\n", vd.toPrettyChars()); + return; + } + typeToBuffer(vd.type, vd.ident); + buf.writestring(";\n"); + return; + } + + if (vd.storage_class & (AST.STC.static_ | AST.STC.extern_ | AST.STC.tls | AST.STC.gshared) || + vd.parent && vd.parent.isModule()) + { + indent(); + if (vd.linkage != LINK.c && vd.linkage != LINK.cpp) + { + buf.printf("// ignoring variable %s because of linkage\n", vd.toPrettyChars()); + return; + } + if (vd.storage_class & AST.STC.tls) + { + buf.printf("// ignoring variable %s because of thread-local storage\n", vd.toPrettyChars()); + return; + } + if (vd.linkage == LINK.c) + buf.writestring("extern \"C\" "); + else if (!adparent) + buf.writestring("extern "); + if (adparent) + buf.writestring("static "); + typeToBuffer(vd.type, vd.ident); + buf.writestring(";\n"); + if (!adparent) + buf.printf("\n"); + return; + } + + if (adparent && vd.type && vd.type.deco) + { + indent(); + auto save = cdparent; + cdparent = vd.isField() ? adparent.isClassDeclaration() : null; + typeToBuffer(vd.type, vd.ident); + cdparent = save; + buf.writestring(";\n"); + + if (auto t = vd.type.isTypeStruct()) + includeSymbol(t.sym); + + auto savex = buf; + buf = checkbuf; + buf.writestring(" assert(offsetof("); + buf.writestring(adparent.ident.toChars()); + buf.writestring(", "); + buf.writestring(vd.ident.toChars()); + buf.printf(") == %d);\n", vd.offset); + buf = savex; + return; + } + visit(cast(AST.Dsymbol)vd); + } + + override void visit(AST.TypeInfoDeclaration tid) + { + debug (Debug_DtoH) + { + printf("[AST.TypeInfoDeclaration enter] %s\n", tid.toChars()); + scope(exit) printf("[AST.TypeInfoDeclaration exit] %s\n", tid.toChars()); + } + } + + override void visit(AST.AliasDeclaration ad) + { + debug (Debug_DtoH) + { + printf("[AST.AliasDeclaration enter] %s\n", ad.toChars()); + scope(exit) printf("[AST.AliasDeclaration exit] %s\n", ad.toChars()); + } + if (isBuildingCompiler && ad.getModule() && ad.getModule().isIgnoredModule()) + return; + + if (auto t = ad.type) + { + if (t.ty == AST.Tdelegate) + { + visit(cast(AST.Dsymbol)ad); + return; + } + buf.writestring("typedef "); + typeToBuffer(t, ad.ident); + buf.writestring(";\n"); + if (!adparent) + buf.printf("\n"); + return; + } + if (!ad.aliassym) + { + assert(0); + } + if (auto ti = ad.aliassym.isTemplateInstance()) + { + visitTi(ti); + return; + } + if (auto sd = ad.aliassym.isStructDeclaration()) + { + buf.writestring("typedef "); + sd.type.accept(this); + buf.writestring(" "); + buf.writestring(ad.ident.toChars()); + buf.writestring(";\n"); + if (!adparent) + buf.printf("\n"); + return; + } + if (ad.aliassym.isDtorDeclaration()) + { + // Ignore. It's taken care of while visiting FuncDeclaration + return; + } + indent(); + buf.printf("// ignored %s %s\n", ad.aliassym.kind(), ad.aliassym.toPrettyChars()); + } + + override void visit(AST.AnonDeclaration ad) + { + debug (Debug_DtoH) + { + printf("[AST.AnonDeclaration enter] %s\n", ad.toChars()); + scope(exit) printf("[AST.AnonDeclaration exit] %s\n", ad.toChars()); + } + indent(); + buf.writestring(ad.isunion ? "union\n" : "struct\n"); + indent(); + buf.writestring("{\n"); + foreach (s; *ad.decl) + { + indent(); + s.accept(this); + } + indent(); + buf.writestring("};\n"); + } + + private bool memberField(AST.VarDeclaration vd) + { + if (!vd.type || !vd.type.deco || !vd.ident) + return false; + if (!vd.isField()) + return false; + if (vd.type.ty == AST.Tfunction) + return false; + if (vd.type.ty == AST.Tsarray) + return false; + return true; + } + + override void visit(AST.StructDeclaration sd) + { + debug (Debug_DtoH) + { + printf("[AST.StructDeclaration enter] %s\n", sd.toChars()); + scope(exit) printf("[AST.StructDeclaration exit] %s\n", sd.toChars()); + } + if (sd.isInstantiated()) + return; + if (cast(void*)sd in visited) + return; + if (!sd.type || !sd.type.deco) + return; + if (isBuildingCompiler && sd.getModule() && sd.getModule().isIgnoredModule()) + return; + + visited[cast(void*)sd] = true; + if (linkage != LINK.c && linkage != LINK.cpp) + { + buf.printf("// ignoring non-cpp struct %s because of linkage\n", sd.toChars()); + return; + } + + buf.writestring(sd.isUnionDeclaration() ? "union" : "struct"); + pushAlignToBuffer(sd.alignment); + buf.writestring(sd.ident.toChars()); + if (!sd.members) + { + buf.writestring(";\n\n"); + return; + } + + buf.writestring("\n{\n"); + auto save = adparent; + adparent = sd; + foreach (m; *sd.members) + { + m.accept(this); + } + adparent = save; + // Generate default ctor + if (!sd.noDefaultCtor) + { + buf.printf(" %s()", sd.ident.toChars()); + size_t varCount; + bool first = true; + foreach (m; *sd.members) + { + if (auto vd = m.isVarDeclaration()) + { + if (!memberField(vd)) + continue; + varCount++; + + if (!vd._init && !vd.type.isTypeBasic() && !vd.type.isTypePointer && !vd.type.isTypeStruct && + !vd.type.isTypeClass && !vd.type.isTypeDArray && !vd.type.isTypeSArray) + { + continue; + } + if (vd._init && vd._init.isVoidInitializer()) + continue; + + if (first) + { + buf.printf(" : "); + first = false; + } + else + { + buf.printf(", "); + } + buf.printf("%s(", vd.ident.toChars()); + + if (vd._init) + { + AST.initializerToExpression(vd._init).accept(this); + } + buf.printf(")"); + } + } + buf.printf(" {}\n"); + } + + version (none) + { + if (varCount) + { + buf.printf(" %s(", sd.ident.toChars()); + bool first = true; + foreach (m; *sd.members) + { + if (auto vd = m.isVarDeclaration()) + { + if (!memberField(vd)) + continue; + if (first) + first = false; + else + buf.writestring(", "); + assert(vd.type); + assert(vd.ident); + typeToBuffer(vd.type, vd.ident); + } + } + buf.printf(") {"); + foreach (m; *sd.members) + { + if (auto vd = m.isVarDeclaration()) + { + if (!memberField(vd)) + continue; + buf.printf(" this->%s = %s;", vd.ident.toChars(), vd.ident.toChars()); + } + } + buf.printf(" }\n"); + } + } + buf.writestring("};\n"); + + popAlignToBuffer(sd.alignment); + buf.writestring("\n"); + + auto savex = buf; + buf = checkbuf; + buf.writestring(" assert(sizeof("); + buf.writestring(sd.ident.toChars()); + buf.printf(") == %d);\n", sd.size(Loc.initial)); + buf = savex; + } + + private void pushAlignToBuffer(uint alignment) + { + // DMD ensures alignment is a power of two + //assert(alignment > 0 && ((alignment & (alignment - 1)) == 0), + // "Invalid alignment size"); + + // When no alignment is specified, `uint.max` is the default + if (alignment == uint.max) + { + buf.writeByte(' '); + return; + } + + buf.writestring("\n#if defined(__GNUC__) || defined(__clang__)\n"); + // The equivalent of `#pragma pack(push, n)` is `__attribute__((packed, aligned(n)))` + // NOTE: removing the packed attribute will might change the resulting size + buf.printf(" __attribute__((packed, aligned(%d)))\n", alignment); + buf.writestring("#elif defined(_MSC_VER)\n"); + buf.printf(" __declspec(align(%d))\n", alignment); + buf.writestring("#elif defined(__DMC__)\n"); + buf.printf(" #pragma pack(push, %d)\n", alignment); + //buf.printf("#pragma pack(%d)\n", alignment); + buf.writestring("#endif\n"); + } + + private void popAlignToBuffer(uint alignment) + { + if (alignment == uint.max) + return; + + buf.writestring("#if defined(__DMC__)\n"); + buf.writestring(" #pragma pack(pop)\n"); + //buf.writestring("#pragma pack()\n"); + buf.writestring("#endif\n"); + } + + private void includeSymbol(AST.Dsymbol ds) + { + debug (Debug_DtoH) + { + printf("[includeSymbol(AST.Dsymbol) enter] %s\n", ds.toChars()); + scope(exit) printf("[includeSymbol(AST.Dsymbol) exit] %s\n", ds.toChars()); + } + if (cast(void*) ds in visited) + return; + + OutBuffer decl; + auto save = buf; + buf = &decl; + ds.accept(this); + buf = save; + donebuf.writestring(decl.peekChars()); + } + + override void visit(AST.ClassDeclaration cd) + { + debug (Debug_DtoH) + { + printf("[AST.ClassDeclaration enter] %s\n", cd.toChars()); + scope(exit) printf("[AST.ClassDeclaration exit] %s\n", cd.toChars()); + } + if (cast(void*)cd in visited) + return; + if (isBuildingCompiler) + { + if (cd.getModule() && cd.getModule().isIgnoredModule()) + return; + if (cd.isVisitorClass()) + return; + } + + visited[cast(void*)cd] = true; + if (!cd.isCPPclass()) + { + buf.printf("// ignoring non-cpp class %s\n", cd.toChars()); + return; + } + + buf.writestring("class "); + buf.writestring(cd.ident.toChars()); + if (cd.baseClass) + { + buf.writestring(" : public "); + buf.writestring(cd.baseClass.ident.toChars()); + + includeSymbol(cd.baseClass); + } + if (!cd.members) + { + buf.writestring(";\n\n"); + return; + } + + buf.writestring("\n{\npublic:\n"); + auto save = adparent; + adparent = cd; + foreach (m; *cd.members) + { + m.accept(this); + } + adparent = save; + + // Generate special static inline function. + if (isBuildingCompiler && cd.isIdentifierClass()) + { + buf.writestring(" static inline Identifier *idPool(const char *s) { return idPool(s, strlen(s)); }\n"); + } + + buf.writestring("};\n\n"); + } + + override void visit(AST.EnumDeclaration ed) + { + debug (Debug_DtoH) + { + printf("[AST.EnumDeclaration enter] %s\n", ed.toChars()); + scope(exit) printf("[AST.EnumDeclaration exit] %s\n", ed.toChars()); + } + if (cast(void*)ed in visited) + return; + + if (isBuildingCompiler && ed.getModule() && ed.getModule().isIgnoredModule()) + return; + + visited[cast(void*)ed] = true; + + //if (linkage != LINK.c && linkage != LINK.cpp) + //{ + //buf.printf("// ignoring non-cpp enum %s because of linkage\n", ed.toChars()); + //return; + //} + + bool hasBaseType = false; + + switch (ed.memtype.ty) + { + case AST.Tbool, AST.Tvoid: + case AST.Tchar, AST.Twchar, AST.Tdchar: + case AST.Tint8, AST.Tuns8: + case AST.Tint16, AST.Tuns16: + case AST.Tint64, AST.Tuns64: + case AST.Tfloat32, AST.Tfloat64, AST.Tfloat80: + hasBaseType = true; + break; + case AST.Tint32, AST.Tuns32, AST.Tenum: // by default, the base is an int + break; + default: + import dmd.root.string : toDString; + printf ("%s\n", ed.ident.toChars()); + assert(0, ed.memtype.kind.toDString); + } + + if (ed.isSpecial()) + return; + const(char)* ident = null; + if (ed.ident) + ident = ed.ident.toChars(); + if (!ident) + { + buf.writestring("enum"); + } + else if (hasBaseType) + { + //printf("typedef _d_%s %s;\n", ed.memtype.kind, ident); + if (global.params.cplusplus >= CppStdRevision.cpp11) + { + //printf("Using cpp 11 and beyond\n"); + buf.printf("enum %s : %s", ident, ed.memtype.kind); + } + else + { + //printf("Using cpp 98\n"); + buf.writestring("typedef _d_"); + buf.writestring(ed.memtype.kind); + buf.writeByte(' '); + buf.writestring(ident); + buf.writestring(";\n"); + buf.writestring("enum"); + } + } + else + { + buf.writestring("enum "); + buf.writestring(ident); + } + + if (!ed.members) + { + buf.writestring(";\n\n"); + return; + } + + buf.writestring("\n{\n"); + foreach (i, m; *ed.members) + { + if (i) + buf.writestring(",\n"); + buf.writestring(" "); + if (ident && global.params.cplusplus == CppStdRevision.cpp98) + { + foreach (c; ident[0 .. strlen(ident)]) + buf.writeByte(toupper(c)); + } + m.accept(this); + } + buf.writestring("\n};\n\n"); + + //printf("Enum %s min %d max %d\n", ident, ed.minval.toInteger(), ed.maxval.toInteger()); + } + + override void visit(AST.EnumMember em) + { + debug (Debug_DtoH) + { + printf("[AST.EnumMember enter] %s\n", em.toChars()); + scope(exit) printf("[AST.EnumMember exit] %s\n", em.toChars()); + } + buf.writestring(em.ident.toChars()); + buf.writestring(" = "); + //if (cast(AST.StringExp)em.value) + //{ + //em.value.error("cannot convert string enum"); + //return ; + //} + auto ie = cast(AST.IntegerExp)em.value; + visitInteger(ie.toInteger(), em.ed.memtype); + } + + private void typeToBuffer(AST.Type t, Identifier ident) + { + debug (Debug_DtoH) + { + printf("[typeToBuffer(AST.Type) enter] %s ident %s\n", t.toChars(), ident.toChars()); + scope(exit) printf("[typeToBuffer(AST.Type) exit] %s ident %s\n", t.toChars(), ident.toChars()); + } + this.ident = ident; + t.accept(this); + if (this.ident) + { + buf.writeByte(' '); + buf.writestring(ident.toChars()); + } + this.ident = null; + if (auto tsa = t.isTypeSArray()) + { + buf.writeByte('['); + tsa.dim.accept(this); + buf.writeByte(']'); + } + } + + override void visit(AST.Type t) + { + debug (Debug_DtoH) + { + printf("[AST.Type enter] %s\n", t.toChars()); + scope(exit) printf("[AST.Type exit] %s\n", t.toChars()); + } + printf("Invalid type: %s\n", t.toPrettyChars()); + assert(0); + } + + override void visit(AST.TypeIdentifier t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeIdentifier enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeIdentifier exit] %s\n", t.toChars()); + } + buf.writestring(t.ident.toChars()); + } + + override void visit(AST.TypeBasic t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeBasic enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeBasic exit] %s\n", t.toChars()); + } + if (!cdparent && t.isConst()) + buf.writestring("const "); + switch (t.ty) + { + case AST.Tbool, AST.Tvoid: + case AST.Tchar, AST.Twchar, AST.Tdchar: + case AST.Tint8, AST.Tuns8: + case AST.Tint16, AST.Tuns16: + case AST.Tint32, AST.Tuns32: + case AST.Tint64, AST.Tuns64: + case AST.Tfloat32, AST.Tfloat64, AST.Tfloat80: + buf.writestring("_d_"); + buf.writestring(t.dstring); + break; + default: + //t.print(); + assert(0); + } + } + + override void visit(AST.TypePointer t) + { + debug (Debug_DtoH) + { + printf("[AST.TypePointer enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypePointer exit] %s\n", t.toChars()); + } + auto ts = t.next.isTypeStruct(); + if (ts && !strcmp(ts.sym.ident.toChars(), "__va_list_tag")) + { + buf.writestring("va_list"); + return; + } + t.next.accept(this); + if (t.next.ty != AST.Tfunction) + buf.writeByte('*'); + if (!cdparent && t.isConst()) + buf.writestring(" const"); + } + + override void visit(AST.TypeSArray t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeSArray enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeSArray exit] %s\n", t.toChars()); + } + t.next.accept(this); + } + + override void visit(AST.TypeAArray t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeAArray enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeAArray exit] %s\n", t.toChars()); + } + AST.Type.tvoidptr.accept(this); + } + + override void visit(AST.TypeFunction tf) + { + debug (Debug_DtoH) + { + printf("[AST.TypeFunction enter] %s\n", tf.toChars()); + scope(exit) printf("[AST.TypeFunction exit] %s\n", tf.toChars()); + } + tf.next.accept(this); + buf.writeByte('('); + buf.writeByte('*'); + if (ident) + buf.writestring(ident.toChars()); + ident = null; + buf.writeByte(')'); + buf.writeByte('('); + foreach (i; 0 .. AST.Parameter.dim(tf.parameterList.parameters)) + { + if (i) + buf.writestring(", "); + auto fparam = AST.Parameter.getNth(tf.parameterList.parameters, i); + fparam.accept(this); + } + if (tf.parameterList.varargs) + { + if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1) + buf.writestring(", "); + buf.writestring("..."); + } + buf.writeByte(')'); + } + + private void enumToBuffer(AST.EnumDeclaration ed) + { + debug (Debug_DtoH) + { + printf("[enumToBuffer(AST.EnumDeclaration) enter] %s\n", ed.toChars()); + scope(exit) printf("[enumToBuffer(AST.EnumDeclaration) exit] %s\n", ed.toChars()); + } + if (ed.isSpecial()) + { + buf.writestring(ed.toChars()); + return; + } + + if (ed.ident == DMDType.c_long) + buf.writestring("long"); + else if (ed.ident == DMDType.c_ulong) + buf.writestring("unsigned long"); + else if (ed.ident == DMDType.c_longlong) + buf.writestring("long long"); + else if (ed.ident == DMDType.c_ulonglong) + buf.writestring("unsigned long long"); + else if (ed.ident == DMDType.c_long_double) + buf.writestring("long double"); + else + { + //ed.print(); + assert(0); + } + } + + override void visit(AST.TypeEnum t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeEnum enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeEnum exit] %s\n", t.toChars()); + } + if (cast(void*)t.sym !in forwarded) + { + forwarded[cast(void*)t.sym] = true; + auto save = buf; + buf = fwdbuf; + //printf("Visiting enum %s from module %s %s\n", t.sym.toPrettyChars(), t.toChars(), t.sym.loc.toChars()); + t.sym.accept(this); + buf = save; + } + if (!cdparent && t.isConst()) + buf.writestring("const "); + enumToBuffer(t.sym); + } + + override void visit(AST.TypeStruct t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeStruct enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeStruct exit] %s\n", t.toChars()); + } + if (cast(void*)t.sym !in forwarded && !t.sym.parent.isTemplateInstance()) + { + forwarded[cast(void*)t.sym] = true; + fwdbuf.writestring(t.sym.isUnionDeclaration() ? "union " : "struct "); + fwdbuf.writestring(t.sym.toChars()); + fwdbuf.writestring(";\n"); + } + + if (!cdparent && t.isConst()) + buf.writestring("const "); + if (auto ti = t.sym.parent.isTemplateInstance()) + { + visitTi(ti); + return; + } + buf.writestring(t.sym.toChars()); + } + + override void visit(AST.TypeDArray t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeDArray enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeDArray exit] %s\n", t.toChars()); + } + if (!cdparent && t.isConst()) + buf.writestring("const "); + buf.writestring("DArray< "); + t.next.accept(this); + buf.writestring(" >"); + } + + private void visitTi(AST.TemplateInstance ti) + { + debug (Debug_DtoH) + { + printf("[visitTi(AST.TemplateInstance) enter] %s\n", ti.toChars()); + scope(exit) printf("[visitTi(AST.TemplateInstance) exit] %s\n", ti.toChars()); + } + + // FIXME: Restricting this to DMD seems wrong ... + if (isBuildingCompiler) + { + if (ti.tempdecl.ident == DMDType.AssocArray) + { + if (!forwardedAA) + { + forwardedAA = true; + fwdbuf.writestring("struct AA;\n"); + } + buf.writestring("AA*"); + return; + } + if (ti.tempdecl.ident == DMDType.Array) + { + buf.writestring("Array"); + } + else + goto LprintTypes; + } + else + { + LprintTypes: + foreach (o; *ti.tiargs) + { + if (!AST.isType(o)) + return; + } + buf.writestring(ti.tempdecl.ident.toChars()); + } + buf.writeByte('<'); + foreach (i, o; *ti.tiargs) + { + if (i) + buf.writestring(", "); + if (auto tt = AST.isType(o)) + { + tt.accept(this); + } + else + { + //ti.print(); + //o.print(); + assert(0); + } + } + buf.writeByte('>'); + } + + override void visit(AST.TemplateDeclaration td) + { + debug (Debug_DtoH) + { + printf("[AST.TemplateDeclaration enter] %s\n", td.toChars()); + scope(exit) printf("[AST.TemplateDeclaration exit] %s\n", td.toChars()); + } + if (cast(void*)td in visited) + return; + visited[cast(void*)td] = true; + + if (isBuildingCompiler && td.getModule() && td.getModule().isIgnoredModule()) + return; + + if (!td.parameters || !td.onemember || !td.onemember.isStructDeclaration()) + { + visit(cast(AST.Dsymbol)td); + return; + } + + // Explicitly disallow templates with non-type parameters or specialization. + foreach (p; *td.parameters) + { + if (!p.isTemplateTypeParameter() || p.specialization()) + { + visit(cast(AST.Dsymbol)td); + return; + } + } + + if (linkage != LINK.c && linkage != LINK.cpp) + { + buf.printf("// ignoring template %s because of linkage\n", td.toPrettyChars()); + return; + } + + auto sd = td.onemember.isStructDeclaration(); + auto save = tdparent; + tdparent = td; + indent(); + buf.writestring("template <"); + bool first = true; + foreach (p; *td.parameters) + { + if (first) + first = false; + else + buf.writestring(", "); + buf.writestring("typename "); + buf.writestring(p.ident.toChars()); + } + buf.writestring(">\n"); + buf.writestring(sd.isUnionDeclaration() ? "union " : "struct "); + buf.writestring(sd.ident.toChars()); + if (sd.members) + { + buf.writestring("\n{\n"); + auto savex = adparent; + adparent = sd; + foreach (m; *sd.members) + { + m.accept(this); + } + adparent = savex; + buf.writestring("};\n\n"); + } + else + { + buf.writestring(";\n\n"); + } + tdparent = save; + } + + override void visit(AST.TypeClass t) + { + debug (Debug_DtoH) + { + printf("[AST.TypeClass enter] %s\n", t.toChars()); + scope(exit) printf("[AST.TypeClass exit] %s\n", t.toChars()); + } + if (cast(void*)t.sym !in forwarded) + { + forwarded[cast(void*)t.sym] = true; + fwdbuf.writestring("class "); + fwdbuf.writestring(t.sym.toChars()); + fwdbuf.writestring(";\n"); + } + + if (!cdparent && t.isConst()) + buf.writestring("const "); + buf.writestring(t.sym.toChars()); + buf.writeByte('*'); + if (!cdparent && t.isConst()) + buf.writestring(" const"); + } + + private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd) + { + debug (Debug_DtoH) + { + printf("[funcToBuffer(AST.TypeFunction) enter] %s\n", tf.toChars()); + scope(exit) printf("[funcToBuffer(AST.TypeFunction) exit] %s\n", tf.toChars()); + } + + Identifier ident = fd.ident; + + assert(tf.next); + if (fd.isCtorDeclaration() || fd.isDtorDeclaration()) + { + if (fd.isDtorDeclaration()) + { + buf.writeByte('~'); + } + buf.writestring(adparent.toChars()); + } + else + { + tf.next.accept(this); + if (tf.isref) + buf.writeByte('&'); + buf.writeByte(' '); + buf.writestring(ident.toChars()); + } + + buf.writeByte('('); + foreach (i; 0 .. AST.Parameter.dim(tf.parameterList.parameters)) + { + if (i) + buf.writestring(", "); + auto fparam = AST.Parameter.getNth(tf.parameterList.parameters, i); + fparam.accept(this); + } + if (tf.parameterList.varargs) + { + if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1) + buf.writestring(", "); + buf.writestring("..."); + } + buf.writeByte(')'); + } + + override void visit(AST.Parameter p) + { + debug (Debug_DtoH) + { + printf("[AST.Parameter enter] %s\n", p.toChars()); + scope(exit) printf("[AST.Parameter exit] %s\n", p.toChars()); + } + ident = p.ident; + p.type.accept(this); + assert(!(p.storageClass & ~(AST.STC.ref_))); + if (p.storageClass & AST.STC.ref_) + buf.writeByte('&'); + buf.writeByte(' '); + if (ident) + buf.writestring(ident.toChars()); + ident = null; + version (all) + { + if (p.defaultArg && p.defaultArg.op >= TOK.int32Literal && p.defaultArg.op < TOK.struct_) + { + //printf("%s %d\n", p.defaultArg.toChars, p.defaultArg.op); + buf.writestring(" = "); + buf.writestring(p.defaultArg.toChars()); + } + } + else + { + if (p.defaultArg) + { + //printf("%s %d\n", p.defaultArg.toChars, p.defaultArg.op); + //return; + buf.writestring("/*"); + buf.writestring(" = "); + buf.writestring(p.defaultArg.toChars()); + //p.defaultArg.accept(this); + buf.writestring("*/"); + } + } + } + + override void visit(AST.Expression e) + { + debug (Debug_DtoH) + { + printf("[AST.Expression enter] %s\n", e.toChars()); + scope(exit) printf("[AST.Expression exit] %s\n", e.toChars()); + } + assert(0); + } + + override void visit(AST.NullExp e) + { + debug (Debug_DtoH) + { + printf("[AST.NullExp enter] %s\n", e.toChars()); + scope(exit) printf("[AST.NullExp exit] %s\n", e.toChars()); + } + buf.writestring("_d_null"); + } + + override void visit(AST.ArrayLiteralExp e) + { + debug (Debug_DtoH) + { + printf("[AST.ArrayLiteralExp enter] %s\n", e.toChars()); + scope(exit) printf("[AST.ArrayLiteralExp exit] %s\n", e.toChars()); + } + buf.writestring("arrayliteral"); + } + + override void visit(AST.StringExp e) + { + debug (Debug_DtoH) + { + printf("[AST.StringExp enter] %s\n", e.toChars()); + scope(exit) printf("[AST.StringExp exit] %s\n", e.toChars()); + } + assert(e.sz == 1 || e.sz == 2); + if (e.sz == 2) + buf.writeByte('L'); + buf.writeByte('"'); + + for (size_t i = 0; i < e.len; i++) + { + uint c = e.charAt(i); + switch (c) + { + case '"': + case '\\': + buf.writeByte('\\'); + goto default; + default: + if (c <= 0xFF) + { + if (c <= 0x7F && isprint(c)) + buf.writeByte(c); + else + buf.printf("\\x%02x", c); + } + else if (c <= 0xFFFF) + { + buf.printf("\\x%02x\\x%02x", c & 0xFF, c >> 8); + } + else + { + buf.printf("\\x%02x\\x%02x\\x%02x\\x%02x", + c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24); + } + break; + } + } + buf.writeByte('"'); + } + + override void visit(AST.RealExp e) + { + debug (Debug_DtoH) + { + printf("[AST.RealExp enter] %s\n", e.toChars()); + scope(exit) printf("[AST.RealExp exit] %s\n", e.toChars()); + } + + // TODO: Needs to implemented, used e.g. for struct member initializers + buf.writestring("0"); + } + + override void visit(AST.IntegerExp e) + { + debug (Debug_DtoH) + { + printf("[AST.IntegerExp enter] %s\n", e.toChars()); + scope(exit) printf("[AST.IntegerExp exit] %s\n", e.toChars()); + } + visitInteger(e.toInteger, e.type); + } + + private void visitInteger(dinteger_t v, AST.Type t) + { + debug (Debug_DtoH) + { + printf("[visitInteger(AST.Type) enter] %s\n", t.toChars()); + scope(exit) printf("[visitInteger(AST.Type) exit] %s\n", t.toChars()); + } + switch (t.ty) + { + case AST.Tenum: + auto te = cast(AST.TypeEnum)t; + buf.writestring("("); + enumToBuffer(te.sym); + buf.writestring(")"); + visitInteger(v, te.sym.memtype); + break; + case AST.Tbool: + buf.writestring(v ? "true" : "false"); + break; + case AST.Tint8: + buf.printf("%d", cast(byte)v); + break; + case AST.Tuns8: + case AST.Tchar: + buf.printf("%uu", cast(ubyte)v); + break; + case AST.Tint16: + buf.printf("%d", cast(short)v); + break; + case AST.Tuns16: + buf.printf("%uu", cast(ushort)v); + break; + case AST.Tint32: + buf.printf("%d", cast(int)v); + break; + case AST.Tuns32: + buf.printf("%uu", cast(uint)v); + break; + case AST.Tint64: + buf.printf("%lldLL", v); + break; + case AST.Tuns64: + buf.printf("%lluLLU", v); + break; + default: + //t.print(); + assert(0); + } + } + + override void visit(AST.StructLiteralExp sle) + { + debug (Debug_DtoH) + { + printf("[AST.StructLiteralExp enter] %s\n", sle.toChars()); + scope(exit) printf("[AST.StructLiteralExp exit] %s\n", sle.toChars()); + } + buf.writestring(sle.sd.ident.toChars()); + buf.writeByte('('); + foreach(i, e; *sle.elements) + { + if (i) + buf.writestring(", "); + e.accept(this); + } + buf.writeByte(')'); + } +} diff --git a/src/dmd/globals.d b/src/dmd/globals.d index e8eb7eb44e99..d87fd6edd905 100644 --- a/src/dmd/globals.d +++ b/src/dmd/globals.d @@ -239,6 +239,10 @@ extern (C++) struct Param const(char)[] hdrname; // write 'header' file to docname bool hdrStripPlainFunctions = true; // strip the bodies of plain (non-template) functions + bool doCxxHdrGeneration; // write 'Cxx header' file + const(char)[] cxxhdrdir; // write 'header' file to docdir directory + const(char)[] cxxhdrname; // write 'header' file to docname + bool doJsonGeneration; // write JSON file const(char)[] jsonfilename; // write JSON file to jsonfilename JsonFieldFlags jsonFieldFlags; // JSON field flags to include @@ -300,6 +304,7 @@ extern (C++) struct Global string doc_ext = "html"; // for Ddoc generated files string ddoc_ext = "ddoc"; // for Ddoc macro include files string hdr_ext = "di"; // for D 'header' import files + string cxxhdr_ext = "h"; // for C/C++ 'header' files string json_ext = "json"; // for JSON files string map_ext = "map"; // for .map files bool run_noext; // allow -run sources without extensions. diff --git a/src/dmd/globals.h b/src/dmd/globals.h index d196686829b9..ecf014475ed0 100644 --- a/src/dmd/globals.h +++ b/src/dmd/globals.h @@ -205,6 +205,10 @@ struct Param DString hdrname; // write 'header' file to docname bool hdrStripPlainFunctions; // strip the bodies of plain (non-template) functions + bool doCxxHdrGeneration; // write 'Cxx header' file + DString cxxhdrdir; // write 'header' file to docdir directory + DString cxxhdrname; // write 'header' file to docname + bool doJsonGeneration; // write JSON file DString jsonfilename; // write JSON file to jsonfilename unsigned jsonFieldFlags; // JSON field flags to include @@ -265,6 +269,7 @@ struct Global const DString doc_ext; // for Ddoc generated files const DString ddoc_ext; // for Ddoc macro include files const DString hdr_ext; // for D 'header' import files + const DString cxxhdr_ext; // for C/C++ 'header' files const DString json_ext; // for JSON files const DString map_ext; // for .map files bool run_noext; // allow -run sources without extensions. diff --git a/src/dmd/mars.d b/src/dmd/mars.d index d7e0a047bb35..cb3cb2445015 100644 --- a/src/dmd/mars.d +++ b/src/dmd/mars.d @@ -36,6 +36,7 @@ import dmd.dmodule; import dmd.doc; import dmd.dsymbol; import dmd.dsymbolsem; +import dmd.dtoh; import dmd.errors; import dmd.expression; import dmd.globals; @@ -684,6 +685,13 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) File.write(cgFilename.ptr, buf[]); } } + + if (global.params.doCxxHdrGeneration) + genCppHdrFiles(modules); + + if (global.errors) + fatal(); + if (!params.obj) { } @@ -2138,6 +2146,27 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param goto Lerror; } } + else if (p[1] == 'H' && p[2] == 'C') // https://dlang.org/dmd.html#switch-HC + { + params.doCxxHdrGeneration = true; + switch (p[3]) + { + case 'd': // https://dlang.org/dmd.html#switch-HCd + if (!p[4]) + goto Lnoarg; + params.cxxhdrdir = (p + 4 + (p[4] == '=')).toDString; + break; + case 'f': // https://dlang.org/dmd.html#switch-HCf + if (!p[4]) + goto Lnoarg; + params.cxxhdrname = (p + 4 + (p[4] == '=')).toDString; + break; + case 0: + break; + default: + goto Lerror; + } + } else if (p[1] == 'H') // https://dlang.org/dmd.html#switch-H { params.doHdrGeneration = true; diff --git a/test/compilable/dtoh_AliasDeclaration.d b/test/compilable/dtoh_AliasDeclaration.d new file mode 100644 index 000000000000..1314e2b6b1db --- /dev/null +++ b/test/compilable/dtoh_AliasDeclaration.d @@ -0,0 +1,104 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_AliasDeclaration +struct S; +struct S2; +class C; +class C2; +typedef _d_int T; + +extern "C" _d_int x; + +// ignored variable dtoh_AliasDeclaration.x +extern "C" _d_int foo(_d_int x); + +// ignored function dtoh_AliasDeclaration.foo +extern _d_int foo2(_d_int x); + +// ignored function dtoh_AliasDeclaration.foo2 +struct S; + +typedef S aliasS; + +struct S2; + +typedef S2 aliasS2; + +// ignoring non-cpp class C +typedef C* aliasC; + +class C2; + +typedef C2* aliasC2; + +--- +*/ + +alias T = int; + +extern (C) int x; + +alias u = x; + +extern (C) int foo(int x) +{ + return x * 42; +} + +alias fun = foo; + +extern (C++) int foo2(int x) +{ + return x * 42; +} + +alias fun2 = foo2; + +extern (C) struct S; + +alias aliasS = S; + +extern (C++) struct S2; + +alias aliasS2 = S2; + +extern (C) class C; + +alias aliasC = C; + +extern (C++) class C2; + +alias aliasC2 = C2; diff --git a/test/compilable/dtoh_AnonDeclaration.d b/test/compilable/dtoh_AnonDeclaration.d new file mode 100644 index 000000000000..c55b07638037 --- /dev/null +++ b/test/compilable/dtoh_AnonDeclaration.d @@ -0,0 +1,71 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_AnonDeclaration +struct S +{ + union + { + _d_int x; + _d_char c[4$?:32=u|64=LLU$]; + }; + struct + { + _d_int y; + _d_double z; + extern "C" _d_void foo(); + _d_void bar(); + }; + S() {} +}; +--- +*/ + +extern (C++) struct S +{ + union + { + int x; + char[4] c; + } + + struct + { + int y; + double z; + extern(C) void foo() {} + extern(C++) void bar() {} + } +} diff --git a/test/compilable/dtoh_ClassDeclaration.d b/test/compilable/dtoh_ClassDeclaration.d new file mode 100644 index 000000000000..2e2a65eb9de2 --- /dev/null +++ b/test/compilable/dtoh_ClassDeclaration.d @@ -0,0 +1,190 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_ClassDeclaration +class C; +class A; +struct Inner; +class C +{ +public: + _d_byte a; + _d_int b; + _d_long c; +}; + +class C2 +{ +public: + _d_int a; + _d_int b; + _d_long c; + C2(_d_int a); +}; + +// ignoring non-cpp class C3 +class Aligned +{ +public: + _d_byte a; + _d_int b; + _d_long c; + Aligned(_d_int a); +}; + +class A +{ +public: + _d_int a; + C* c; + virtual _d_void foo(); + extern "C" virtual _d_void bar(); + virtual _d_void baz(_d_int x = 42); + struct + { + _d_int x; + _d_int y; + }; + union + { + _d_int u1; + _d_char u2[4$?:32=u|64=LLU$]; + }; +struct Inner +{ + _d_int x; + Inner() : x() {} +}; + +class InnerC +{ +public: + _d_int x; +}; + +class NonStaticInnerC +{ +public: + _d_int x; + A* this; +}; + +typedef Inner I; +class CC; + +}; +--- +*/ + +/* +ClassDeclaration has the following issues: + * align(n) does nothing. You can use align on classes in C++, though It is generally regarded as bad practice and should be avoided +*/ + +extern (C++) class C +{ + byte a; + int b; + long c; +} + +extern (C++) class C2 +{ + int a = 42; + int b; + long c; + + this(int a) {} +} + +extern (C) class C3 +{ + int a = 42; + int b; + long c; + + this(int a) {} +} + +extern (C++) align(1) class Aligned +{ + byte a; + int b; + long c; + + this(int a) {} +} + +extern (C++) class A +{ + int a; + C c; + + void foo(); + extern (C) void bar() {} + extern (C++) void baz(int x = 42) {} + + struct + { + int x; + int y; + } + + union + { + int u1; + char[4] u2; + } + + struct Inner + { + int x; + } + + static extern(C++) class InnerC + { + int x; + } + + class NonStaticInnerC + { + int x; + } + + alias I = Inner; + + extern(C++) class CC; + +} diff --git a/test/compilable/dtoh_StructDeclaration.d b/test/compilable/dtoh_StructDeclaration.d new file mode 100644 index 000000000000..ec31b974f003 --- /dev/null +++ b/test/compilable/dtoh_StructDeclaration.d @@ -0,0 +1,192 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_StructDeclaration +struct S; +struct Inner; +struct S +{ + _d_byte a; + _d_int b; + _d_long c; + S() : a(), b(), c() {} +}; + +struct S2 +{ + _d_int a; + _d_int b; + _d_long c; + S2(_d_int a); + S2() : a(42), b(), c() {} +}; + +struct S3 +{ + _d_int a; + _d_int b; + _d_long c; + extern "C" S3(_d_int a); + S3() : a(42), b(), c() {} +}; + +struct +#if defined(__GNUC__) || defined(__clang__) + __attribute__((packed, aligned(1))) +#elif defined(_MSC_VER) + __declspec(align(1)) +#elif defined(__DMC__) + #pragma pack(push, 1) +#endif +Aligned +{ + _d_byte a; + _d_int b; + _d_long c; + Aligned(_d_int a); + Aligned() : a(), b(), c() {} +}; +#if defined(__DMC__) + #pragma pack(pop) +#endif + +struct A +{ + _d_int a; + S s; + // ignoring extern () block because of linkage + extern "C" _d_void bar(); + _d_void baz(_d_int x = 42); + struct + { + _d_int x; + _d_int y; + }; + union + { + _d_int u1; + _d_char u2[4$?:32=u|64=LLU$]; + }; +struct Inner +{ + _d_int x; + Inner() : x() {} +}; + +typedef Inner I; +class C; + + A() : a(), s() {} +}; +--- +*/ + +/* +StructDeclaration has the following issues: + * align different than 1 does nothing; we should support align(n), where `n` in [1, 2, 4, 8, 16] + * align(n): inside struct definition doesn’t add alignment, but breaks generation of default ctors + * default ctors should be generated only if struct has no ctors + * if a struct has ctors defined, only default ctor (S() { … }) should be generated to init members to default values, and the defined ctors must be declared + * if a struct has ctors defined, the declared ctors must have the name of the struct, not __ctor, as `__ctor` might not be portable + * if a struct has a `member = void`, dtoh code segfaults + * a struct should only define ctors if it’s extern (C++) +*/ + +extern (C++) struct S +{ + byte a; + int b; + long c; +} + +extern (C++) struct S2 +{ + int a = 42; + int b; + long c; + + this(int a) {} +} + +extern (C) struct S3 +{ + int a = 42; + int b; + long c; + + this(int a) {} +} + +extern (C++) align(1) struct Aligned +{ + //align(1): + byte a; + int b; + long c; + + this(int a) {} +} + +extern (C++) struct A +{ + int a; + S s; + + extern (D) void foo(); + extern (C) void bar() {} + extern (C++) void baz(int x = 42) {} + + struct + { + int x; + int y; + } + + union + { + int u1; + char[4] u2; + } + + struct Inner + { + int x; + } + + alias I = Inner; + + extern(C++) class C; + +} diff --git a/test/compilable/dtoh_TemplateDeclaration.d b/test/compilable/dtoh_TemplateDeclaration.d new file mode 100644 index 000000000000..857d1a214099 --- /dev/null +++ b/test/compilable/dtoh_TemplateDeclaration.d @@ -0,0 +1,64 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_TemplateDeclaration +template +struct A +{ + // Ignoring var x alignment 0 + T x; + // ignoring function foo because semantic hasn't been run +}; + +struct B +{ + A<_d_int> x; + B() : x() {} +}; +--- +*/ + +extern (C++) struct A(T) +{ + T x; + // enum Num = 42; // dtoh segfaults at enum + + void foo() {} +} + +extern (C++) struct B +{ + A!int x; +} diff --git a/test/compilable/dtoh_VarDeclaration.d b/test/compilable/dtoh_VarDeclaration.d new file mode 100644 index 000000000000..545564833d19 --- /dev/null +++ b/test/compilable/dtoh_VarDeclaration.d @@ -0,0 +1,74 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_VarDeclaration +// ignoring variable dtoh_VarDeclaration.x because of linkage +// ignoring variable dtoh_VarDeclaration.y because of linkage +extern "C" _d_int z; + +extern _d_int t; + +struct S; + +struct S2; + +// ignoring non-cpp class C +class C2; + +union U; + +union U2; +--- +*/ + +int x = 42; + +extern int y; + +extern (C) int z; + +extern (C++) __gshared int t; + +extern (C) struct S; + +extern (C++) struct S2; + +extern (C) class C; + +extern (C++) class C2; + +extern (C) union U; + +extern (C++) union U2; diff --git a/test/compilable/dtoh_enum.d b/test/compilable/dtoh_enum.d new file mode 100644 index 000000000000..0e3381fce2c5 --- /dev/null +++ b/test/compilable/dtoh_enum.d @@ -0,0 +1,49 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_enum +enum Dummy +{ + DUMMYOne = 0, + DUMMYTwo = 1 +}; +--- +*/ + +enum Dummy +{ + One, + Two +} diff --git a/test/compilable/dtoh_functions.d b/test/compilable/dtoh_functions.d new file mode 100644 index 000000000000..5071bd7206eb --- /dev/null +++ b/test/compilable/dtoh_functions.d @@ -0,0 +1,100 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_functions +// ignoring function dtoh_functions.foo because of linkage +// ignoring function dtoh_functions.fun because it's extern +// ignoring function dtoh_functions.fun2 because it's extern +extern "C" _d_int bar(_d_int x); + +extern "C" _d_int bar2(_d_int x); + +extern "C" _d_int bar4(_d_int x = 42); + +extern _d_int baz(_d_int x); + +extern _d_int baz2(_d_int x); + +extern _d_int baz3(_d_int x = 42); +--- +*/ + +int foo(int x) +{ + return x * 42; +} + +extern (C) int fun(); +extern (C++) int fun2(); + +extern (C) int bar(int x) +{ + return x * 42; +} + +extern (C) static int bar2(int x) +{ + return x * 42; +} + +extern (C) private int bar3(int x) +{ + return x * 42; +} + +extern (C) int bar4(int x = 42) +{ + return x * 42; +} + +extern (C++) int baz(int x) +{ + return x * 42; +} + +extern (C++) static int baz2(int x) +{ + return x * 42; +} + +extern (C++) private int baz3(int x) +{ + return x * 42; +} + +extern (C++) int baz3(int x = 42) +{ + return x * 42; +} diff --git a/test/compilable/dtoh_unittest_block.d b/test/compilable/dtoh_unittest_block.d new file mode 100644 index 000000000000..965c66ae34ba --- /dev/null +++ b/test/compilable/dtoh_unittest_block.d @@ -0,0 +1,46 @@ +/* +REQUIRED_ARGS: -HC -c -o- +PERMUTE_ARGS: +TEST_OUTPUT: +--- +#pragma once + +// Automatically generated by dmd -HC + +#include +#include +#include +#include + +#define _d_void void +#define _d_bool bool +#define _d_byte signed char +#define _d_ubyte unsigned char +#define _d_short short +#define _d_ushort unsigned short +#define _d_int int +#define _d_uint unsigned +#define _d_long $?:32=long long|64=long$ +#define _d_ulong unsigned $?:32=long long|64=long$ +#define _d_float float +#define _d_double double +#define _d_real long double +#define _d_char char +#define _d_wchar wchar_t +#define _d_dchar unsigned +typedef _d_long d_int64; + +#define _d_null NULL + + +// Parsing module dtoh_unittest_block +--- +*/ + +unittest +{ + extern (C++) int foo(int x) + { + return x * 42; + } +} diff --git a/test/dshell/cpp_header_gen.d b/test/dshell/cpp_header_gen.d new file mode 100644 index 000000000000..235b6ba275b3 --- /dev/null +++ b/test/dshell/cpp_header_gen.d @@ -0,0 +1,25 @@ +module test.dshell.cpp_header_gen; + +import dshell; + +void main() +{ + if (OS != "linux") + { + writeln("CPP header generation test was skipped on non-linux platform."); + return; + } + + // FIXME: Should be a default variable + Vars.set("CC", "g++"); + + Vars.set("SOURCE_DIR", "$EXTRA_FILES/cpp_header_gen"); + Vars.set("LIB", "$OUTPUT_BASE/lib$LIBEXT"); + Vars.set("CPP_OBJ", "$OUTPUT_BASE/cpp.o"); + Vars.set("HEADER_EXE", "$OUTPUT_BASE/test"); + + run("$DMD -m$MODEL -c -lib -of=$LIB -HCf=$OUTPUT_BASE/library.h $SOURCE_DIR/library.d"); + run("$CC -m$MODEL -c -o $CPP_OBJ -I$OUTPUT_BASE -I$EXTRA_FILES/../../../src/dmd/root $SOURCE_DIR/app.cpp"); + run("$DMD -m$MODEL -conf= -defaultlib= -of=$HEADER_EXE $LIB $CPP_OBJ"); + run("$HEADER_EXE"); +} diff --git a/test/dshell/extra-files/cpp_header_gen/app.cpp b/test/dshell/extra-files/cpp_header_gen/app.cpp new file mode 100644 index 000000000000..de198810e236 --- /dev/null +++ b/test/dshell/extra-files/cpp_header_gen/app.cpp @@ -0,0 +1,45 @@ +// This should be solved by the header generator +#include "dcompat.h" +#include "library.h" + +#include + +int main() +{ + char name[] = "Header"; + const int length = sizeof(name) - 1; + + C* c = C::create(name, length); + assert(c); + assert(c->s.i == length); + assert(!c->s.b); + assert(c->name.ptr == name); + assert(c->name.length == length); + c->verify(); + + assert(foo(c->s) == bar(c)); + + c->s.multiply(c->s); + assert(c->s.i == length * length); + assert(c->s.b); + + U u; + u.b = false; + toggle(u); + assert(u.b); + + assert(3 <= PI && PI <= 4); + assert(counter = 42); + + // FIXME: Maybe improve naming convention or use enum class (C++11) + // assert(Weather::Sun != Weather::Rain); + // assert(Weather::Rain != Weather::Storm); + + assert(WEATHERSun != WEATHERRain); + assert(WEATHERRain != WEATHERStorm); + + S2 s2; + s2.s = c->s; + + return 0; +} diff --git a/test/dshell/extra-files/cpp_header_gen/library.d b/test/dshell/extra-files/cpp_header_gen/library.d new file mode 100644 index 000000000000..0500295988f5 --- /dev/null +++ b/test/dshell/extra-files/cpp_header_gen/library.d @@ -0,0 +1,91 @@ +module library; + +extern (C++): + +int foo(ref const S s) +{ + return s.i; +} + +int bar(const C c) +{ + return c.s.i; +} + +/* +// TODO: Seems not implemented yet +interface I +{ + void verify(); +} +*/ + +class C // : I +{ + S s; + char[] name; + + extern(D) this(ref S s, char[] name) + { + this.s = s; + this.name = name; + } + + static C create(char* name, const int length) + { + auto s = S(length, length & 1); + return new C(s, name[0 .. length]); + } + + void verify() + { + assert(s.i == 6); + assert(!s.b); + assert(name == "Header"); + } +} + +struct S +{ + int i; + bool b = true; + + void multiply(ref const S other) + { + i *= other.i; + b = b || !other.b; + } +} + +union U +{ + // int a; // FIXME: Generates struct constructor + // U() : a(), b() {} + bool b; +} + +void toggle(ref U u) +{ + u.b = !u.b; +} + +// FIXME: Generates non-existant global +// enum PI = 3.141; // extern _d_double PI; + +__gshared immutable PI = 3.141; +__gshared int counter = 42; + +enum Weather +{ + Sun, + Rain, + Storm +} + +static if (true) +{ + struct S2 + { + S s; + } +}