From 880671abc0476e2223447a83809dddc3100ed3a9 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Wed, 1 Mar 2023 17:57:09 +0100 Subject: [PATCH 001/301] dcompat.h: Add typedef for d_bool, use it in all C++ headers for fields --- dmd/aggregate.h | 10 ++-- dmd/attrib.h | 12 ++--- dmd/common/outbuffer.h | 6 +-- dmd/cond.h | 2 +- dmd/declaration.h | 12 ++--- dmd/dsymbol.h | 4 +- dmd/expression.h | 50 +++++++++--------- dmd/globals.h | 112 ++++++++++++++++++++--------------------- dmd/identifier.h | 2 +- dmd/init.h | 8 +-- dmd/module.h | 8 +-- dmd/mtype.h | 4 +- dmd/objc.h | 6 +-- dmd/root/dcompat.h | 10 +++- dmd/root/optional.h | 4 +- dmd/scope.h | 4 +- dmd/statement.h | 24 ++++----- dmd/target.h | 20 ++++---- dmd/template.h | 14 +++--- dmd/visitor.h | 3 +- 20 files changed, 163 insertions(+), 152 deletions(-) diff --git a/dmd/aggregate.h b/dmd/aggregate.h index 04e5eb2f0d9..03fe478685c 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -108,8 +108,8 @@ class AggregateDeclaration : public ScopeDsymbol Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this) Visibility visibility; - bool noDefaultCtor; // no default construction - bool disableNew; // disallow allocations using `new` + d_bool noDefaultCtor; // no default construction + d_bool disableNew; // disallow allocations using `new` Sizeok sizeok; // set when structsize contains valid data virtual Scope *newScope(Scope *sc); @@ -269,10 +269,10 @@ class ClassDeclaration : public AggregateDeclaration // their own vtbl[] TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration - bool com; // true if this is a COM class (meaning it derives from IUnknown) - bool stack; // true if this is a scope class + d_bool com; // true if this is a COM class (meaning it derives from IUnknown) + d_bool stack; // true if this is a scope class int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)] - bool inuse; // to prevent recursive attempts + d_bool inuse; // to prevent recursive attempts ThreeState isabstract; // if abstract class Baseok baseok; // set the progress of base classes resolving diff --git a/dmd/attrib.h b/dmd/attrib.h index 44ceb12e0d0..113653e9d7c 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -132,7 +132,7 @@ class AlignDeclaration final : public AttribDeclaration class AnonDeclaration final : public AttribDeclaration { public: - bool isunion; + d_bool isunion; int sem; // 1 if successful semantic() unsigned anonoffset; // offset of anonymous struct unsigned anonstructsize; // size of anonymous struct @@ -175,8 +175,8 @@ class StaticIfDeclaration final : public ConditionalDeclaration { public: ScopeDsymbol *scopesym; - bool addisdone; - bool onStack; + d_bool addisdone; + d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; Dsymbols *include(Scope *sc) override; @@ -193,8 +193,8 @@ class StaticForeachDeclaration final : public AttribDeclaration public: StaticForeach *sfe; ScopeDsymbol *scopesym; - bool onStack; - bool cached; + d_bool onStack; + d_bool cached; Dsymbols *cache; StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; @@ -227,7 +227,7 @@ class CompileDeclaration final : public AttribDeclaration Expressions *exps; ScopeDsymbol *scopesym; - bool compiled; + d_bool compiled; CompileDeclaration *syntaxCopy(Dsymbol *s) override; void addMember(Scope *sc, ScopeDsymbol *sds) override; diff --git a/dmd/common/outbuffer.h b/dmd/common/outbuffer.h index b672842e74d..4c1dceea3f6 100644 --- a/dmd/common/outbuffer.h +++ b/dmd/common/outbuffer.h @@ -21,11 +21,11 @@ struct OutBuffer private: DArray data; d_size_t offset; - bool notlinehead; + d_bool notlinehead; void *fileMapping; // pointer to a file mapping object not used on the C++ side public: - bool doindent; - bool spaces; + d_bool doindent; + d_bool spaces; int level; OutBuffer() diff --git a/dmd/cond.h b/dmd/cond.h index 422a715bdba..45094d14991 100644 --- a/dmd/cond.h +++ b/dmd/cond.h @@ -52,7 +52,7 @@ class StaticForeach final : public RootObject ForeachStatement *aggrfe; ForeachRangeStatement *rangefe; - bool needExpansion; + d_bool needExpansion; StaticForeach *syntaxCopy(); }; diff --git a/dmd/declaration.h b/dmd/declaration.h index 20accc82ec6..c12917f4404 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -167,8 +167,8 @@ class TupleDeclaration final : public Declaration public: Objects *objects; TypeTuple *tupletype; // !=NULL if this is a type tuple - bool isexp; // true: expression tuple - bool building; // it's growing in AliasAssign semantic + d_bool isexp; // true: expression tuple + d_bool building; // it's growing in AliasAssign semantic TupleDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; @@ -607,7 +607,7 @@ class FuncDeclaration : public Declaration // set if someone took the address of this function int tookAddressOf; - bool requiresClosure; // this function needs a closure + d_bool requiresClosure; // this function needs a closure // local variables in this function which are referenced by nested functions VarDeclarations closureVars; @@ -742,7 +742,7 @@ class FuncAliasDeclaration final : public FuncDeclaration { public: FuncDeclaration *funcalias; - bool hasOverloads; + d_bool hasOverloads; FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; } const char *kind() const override; @@ -758,7 +758,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration Type *treq; // target of return type inference // backend - bool deferToObj; + d_bool deferToObj; FuncLiteralDeclaration *syntaxCopy(Dsymbol *) override; bool isNested() const override; @@ -778,7 +778,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration class CtorDeclaration final : public FuncDeclaration { public: - bool isCpCtor; + d_bool isCpCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; const char *toChars() const override; diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index 1cee456aa10..039a28871b3 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -172,7 +172,7 @@ struct FieldState unsigned fieldAlign; unsigned bitOffset; - bool inFlight; + d_bool inFlight; }; struct DsymbolAttributes; @@ -189,7 +189,7 @@ class Dsymbol : public ASTNode private: DsymbolAttributes* atts; public: - bool errors; // this symbol failed to pass semantic() + d_bool errors; // this symbol failed to pass semantic() PASS semanticRun; unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab static Dsymbol *create(Identifier *); diff --git a/dmd/expression.h b/dmd/expression.h index c7f789dbc08..512e1c2b1cd 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -81,7 +81,7 @@ class Expression : public ASTNode public: EXP op; // to minimize use of dynamic_cast unsigned char size; // # of bytes in Expression so we can copy() it - bool parens; // if this is a parenthesized expression + d_bool parens; // if this is a parenthesized expression Type *type; // !=NULL means that semantic() has been run Loc loc; // file location @@ -331,7 +331,7 @@ class DsymbolExp final : public Expression { public: Dsymbol *s; - bool hasOverloads; + d_bool hasOverloads; DsymbolExp *syntaxCopy() override; bool isLvalue() override; @@ -422,7 +422,7 @@ class ArrayLiteralExp final : public Expression Expression *basis; Expressions *elements; OwnedBy ownedByCtfe; - bool onstack; + d_bool onstack; static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements); @@ -476,8 +476,8 @@ class StructLiteralExp final : public Expression */ int stageflags; - bool useStaticInit; // if this is true, use the StructDeclaration's init symbol - bool isOriginal; // used when moving instances to indicate `this is this.origin` + d_bool useStaticInit; // if this is true, use the StructDeclaration's init symbol + d_bool isOriginal; // used when moving instances to indicate `this is this.origin` OwnedBy ownedByCtfe; static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL); @@ -536,8 +536,8 @@ class NewExp final : public Expression Expression *argprefix; // expression to be evaluated just before arguments[] CtorDeclaration *member; // constructor function - bool onstack; // allocate on stack - bool thrownew; // this NewExp is the expression of a ThrowStatement + d_bool onstack; // allocate on stack + d_bool thrownew; // this NewExp is the expression of a ThrowStatement Expression *lowering; // lowered druntime hook: `_d_newclass` @@ -565,7 +565,7 @@ class SymbolExp : public Expression public: Declaration *var; Dsymbol *originalScope; - bool hasOverloads; + d_bool hasOverloads; void accept(Visitor *v) override { v->visit(this); } }; @@ -587,7 +587,7 @@ class SymOffExp final : public SymbolExp class VarExp final : public SymbolExp { public: - bool delegateWasExtracted; + d_bool delegateWasExtracted; static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; @@ -763,9 +763,9 @@ class DotIdExp final : public UnaExp { public: Identifier *ident; - bool noderef; // true if the result of the expression will never be dereferenced - bool wantsym; // do not replace Symbol with its initializer during semantic() - bool arrow; // ImportC: if -> instead of . + d_bool noderef; // true if the result of the expression will never be dereferenced + d_bool wantsym; // do not replace Symbol with its initializer during semantic() + d_bool arrow; // ImportC: if -> instead of . static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident); void accept(Visitor *v) override { v->visit(this); } @@ -785,7 +785,7 @@ class DotVarExp final : public UnaExp { public: Declaration *var; - bool hasOverloads; + d_bool hasOverloads; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; @@ -809,7 +809,7 @@ class DelegateExp final : public UnaExp { public: FuncDeclaration *func; - bool hasOverloads; + d_bool hasOverloads; VarDeclaration *vthis2; // container for multi-context @@ -830,9 +830,9 @@ class CallExp final : public UnaExp Expressions *arguments; // function arguments Identifiers *names; FuncDeclaration *f; // symbol to call - bool directcall; // true if a virtual call is devirtualized - bool inDebugStatement; // true if this was in a debug statement - bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) + d_bool directcall; // true if a virtual call is devirtualized + d_bool inDebugStatement; // true if this was in a debug statement + d_bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) VarDeclaration *vthis2; // container for multi-context static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); @@ -891,7 +891,7 @@ class NotExp final : public UnaExp class DeleteExp final : public UnaExp { public: - bool isRAII; + d_bool isRAII; void accept(Visitor *v) override { v->visit(this); } }; @@ -936,9 +936,9 @@ class SliceExp final : public UnaExp Expression *upr; // NULL if implicit 0 Expression *lwr; // NULL if implicit [length - 1] VarDeclaration *lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + d_bool upperIsInBounds; // true if upr <= e1.length + d_bool lowerIsLessThanUpper; // true if lwr <= upr + d_bool arrayop; // an array operation, rather than a slice SliceExp *syntaxCopy() override; bool isLvalue() override; @@ -1010,8 +1010,8 @@ class DotExp final : public BinExp class CommaExp final : public BinExp { public: - bool isGenerated; - bool allowCommaExp; + d_bool isGenerated; + d_bool allowCommaExp; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; Expression *modifiableLvalue(Scope *sc, Expression *e) override; @@ -1024,8 +1024,8 @@ class IndexExp final : public BinExp { public: VarDeclaration *lengthVar; - bool modifiable; - bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 + d_bool modifiable; + d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 IndexExp *syntaxCopy() override; bool isLvalue() override; diff --git a/dmd/globals.h b/dmd/globals.h index ec8fc32ed0f..84fbec6977d 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -85,8 +85,8 @@ enum class FeatureState : signed char struct Output { /// Configuration for the compiler generator - bool doOutput; // Output is enabled - bool fullOutput; // Generate comments for hidden declarations (for -HC), + d_bool doOutput; // Output is enabled + d_bool fullOutput; // Generate comments for hidden declarations (for -HC), // and don't strip the bodies of plain (non-template) functions (for -H) DString dir; // write to directory 'dir' DString name; // write to file 'name' @@ -99,71 +99,71 @@ struct Output // Put command line switches in here struct Param { - bool obj; // write object file - bool multiobj; // break one object file into multiple ones - bool trace; // insert profiling hooks - bool tracegc; // instrument calls to 'new' - bool verbose; // verbose compile - bool vcg_ast; // write-out codegen-ast - bool showColumns; // print character (column) numbers in diagnostics - bool vtls; // identify thread local variables - bool vtemplates; // collect and list statistics on template instantiations - bool vtemplatesListInstances; // collect and list statistics on template instantiations origins - bool vgc; // identify gc usage - bool vfield; // identify non-mutable field variables - bool vcomplex; // identify complex/imaginary type usage - bool vin; // identify 'in' parameters + d_bool obj; // write object file + d_bool multiobj; // break one object file into multiple ones + d_bool trace; // insert profiling hooks + d_bool tracegc; // instrument calls to 'new' + d_bool verbose; // verbose compile + d_bool vcg_ast; // write-out codegen-ast + d_bool showColumns; // print character (column) numbers in diagnostics + d_bool vtls; // identify thread local variables + d_bool vtemplates; // collect and list statistics on template instantiations + d_bool vtemplatesListInstances; // collect and list statistics on template instantiations origins + d_bool vgc; // identify gc usage + d_bool vfield; // identify non-mutable field variables + d_bool vcomplex; // identify complex/imaginary type usage + d_bool vin; // identify 'in' parameters Diagnostic useDeprecated; - bool useUnitTests; // generate unittest code - bool useInline; // inline expand functions - bool release; // build release version - bool preservePaths; // true means don't strip path from source file + d_bool useUnitTests; // generate unittest code + d_bool useInline; // inline expand functions + d_bool release; // build release version + d_bool preservePaths; // true means don't strip path from source file Diagnostic warnings; - bool color; // use ANSI colors in console output - bool cov; // generate code coverage data + d_bool color; // use ANSI colors in console output + d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required - bool ctfe_cov; // generate coverage data for ctfe - bool ignoreUnsupportedPragmas; // rather than error on them - bool useModuleInfo; // generate runtime module information - bool useTypeInfo; // generate runtime type information - bool useExceptions; // support exception handling - bool betterC; // be a "better C" compiler; no dependency on D runtime - bool addMain; // add a default main() function - bool allInst; // generate code for all template instantiations - bool bitfields; // support C style bit fields + d_bool ctfe_cov; // generate coverage data for ctfe + d_bool ignoreUnsupportedPragmas; // rather than error on them + d_bool useModuleInfo; // generate runtime module information + d_bool useTypeInfo; // generate runtime type information + d_bool useExceptions; // support exception handling + d_bool betterC; // be a "better C" compiler; no dependency on D runtime + d_bool addMain; // add a default main() function + d_bool allInst; // generate code for all template instantiations + d_bool bitfields; // support C style bit fields CppStdRevision cplusplus; // version of C++ name mangling to support - bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) - bool manual; // open browser on compiler manual - bool usage; // print usage and exit - bool mcpuUsage; // print help on -mcpu switch - bool transitionUsage; // print help on -transition switch - bool checkUsage; // print help on -check switch - bool checkActionUsage; // print help on -checkaction switch - bool revertUsage; // print help on -revert switch - bool previewUsage; // print help on -preview switch - bool externStdUsage; // print help on -extern-std switch - bool hcUsage; // print help on -HC switch - bool logo; // print logo; + d_bool showGaggedErrors; // print gagged errors anyway + d_bool printErrorContext; // print errors with the error context (the error line in the source file) + d_bool manual; // open browser on compiler manual + d_bool usage; // print usage and exit + d_bool mcpuUsage; // print help on -mcpu switch + d_bool transitionUsage; // print help on -transition switch + d_bool checkUsage; // print help on -check switch + d_bool checkActionUsage; // print help on -checkaction switch + d_bool revertUsage; // print help on -revert switch + d_bool previewUsage; // print help on -preview switch + d_bool externStdUsage; // print help on -extern-std switch + d_bool hcUsage; // print help on -HC switch + d_bool logo; // print logo; // Options for `-preview=/-revert=` FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params - bool ehnogc; // use @nogc exception handling - bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() - bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes + d_bool ehnogc; // use @nogc exception handling + d_bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md + d_bool fieldwise; // do struct equality testing field-wise rather than by memcmp() + d_bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 FeatureState noSharedAccess; // read/write access to shared memory objects - bool previewIn; // `in` means `[ref] scope const`, accepts rvalues - bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract - bool shortenedMethods; // allow => in normal function declarations - bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 - bool fix16997; // fix integral promotions for unary + - ~ operators + d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues + d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract + d_bool shortenedMethods; // allow => in normal function declarations + d_bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 + d_bool fix16997; // fix integral promotions for unary + - ~ operators // https://issues.dlang.org/show_bug.cgi?id=16997 FeatureState dtorFields; // destruct fields of partially constructed objects // https://issues.dlang.org/show_bug.cgi?id=14246 @@ -208,7 +208,7 @@ struct Param MessageStyle messageStyle; // style of file/line annotations on messages - bool run; // run resulting executable + d_bool run; // run resulting executable Strings runargs; // arguments for executable Array cppswitches; // preprocessor switches @@ -228,7 +228,7 @@ struct Param struct structalign_t { unsigned short value; - bool pack; + d_bool pack; bool isDefault() const; void setDefault(); @@ -275,7 +275,7 @@ struct Global Array* versionids; // command line versions and predefined versions Array* debugids; // command line debug versions and predefined versions - bool hasMainFunction; + d_bool hasMainFunction; unsigned varSequenceNumber; FileManager* fileManager; diff --git a/dmd/identifier.h b/dmd/identifier.h index c12c3554c1b..e7b3ba60b0f 100644 --- a/dmd/identifier.h +++ b/dmd/identifier.h @@ -17,7 +17,7 @@ class Identifier final : public RootObject { private: int value; - bool isAnonymous_; + d_bool isAnonymous_; DString string; public: diff --git a/dmd/init.h b/dmd/init.h index 66b874c91b5..9a6a56b68bb 100644 --- a/dmd/init.h +++ b/dmd/init.h @@ -77,8 +77,8 @@ class ArrayInitializer final : public Initializer Initializers value; // of Initializer *'s unsigned dim; // length of array being initialized Type *type; // type that array will be used to initialize - bool sem; // true if semantic() is run - bool isCarray; // C array semantics + d_bool sem; // true if semantic() is run + d_bool isCarray; // C array semantics bool isAssociativeArray() const; Expression *toAssocArrayLiteral(); @@ -89,7 +89,7 @@ class ArrayInitializer final : public Initializer class ExpInitializer final : public Initializer { public: - bool expandTuples; + d_bool expandTuples; Expression *exp; void accept(Visitor *v) override { v->visit(this); } @@ -112,7 +112,7 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - bool sem; // true if semantic() is run + d_bool sem; // true if semantic() is run void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/module.h b/dmd/module.h index 002bb1a875b..8b481108f8c 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -74,8 +74,8 @@ class Module final : public Package unsigned errors; // if any errors in file unsigned numlines; // number of lines in source file FileType filetype; // source file type - bool hasAlwaysInlines; // contains references to functions that must be inlined - bool isPackageFile; // if it is a package.d + d_bool hasAlwaysInlines; // contains references to functions that must be inlined + d_bool isPackageFile; // if it is a package.d Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -90,7 +90,7 @@ class Module final : public Package Identifier *searchCacheIdent; Dsymbol *searchCacheSymbol; // cached value of search int searchCacheFlags; // cached flags - bool insearch; + d_bool insearch; // module from command line we're imported from, // i.e. a module that will be taken all the @@ -165,7 +165,7 @@ struct ModuleDeclaration Loc loc; Identifier *id; DArray packages; // array of Identifier's representing packages - bool isdeprecated; // if it is a deprecated module + d_bool isdeprecated; // if it is a deprecated module Expression *msg; const char *toChars() const; diff --git a/dmd/mtype.h b/dmd/mtype.h index d0775f2f5fb..fbfd766fa94 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -589,7 +589,7 @@ struct ParameterList Parameters* parameters; StorageClass stc; VarArg varargs; - bool hasIdentifierList; // true if C identifier-list style + d_bool hasIdentifierList; // true if C identifier-list style size_t length(); Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); } @@ -779,7 +779,7 @@ class TypeStruct final : public Type public: StructDeclaration *sym; AliasThisRec att; - bool inuse; + d_bool inuse; static TypeStruct *create(StructDeclaration *sym); const char *kind() override; diff --git a/dmd/objc.h b/dmd/objc.h index 305ce812487..a5cc6f1b089 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -37,8 +37,8 @@ struct ObjcSelector struct ObjcClassDeclaration { - bool isMeta; - bool isExtern; + d_bool isMeta; + d_bool isExtern; Identifier* identifier; ClassDeclaration* classDeclaration; @@ -52,7 +52,7 @@ struct ObjcFuncDeclaration { ObjcSelector* selector; VarDeclaration* selectorParameter; - bool isOptional; + d_bool isOptional; }; class Objc diff --git a/dmd/root/dcompat.h b/dmd/root/dcompat.h index 0bc23b7a8b3..1a496880100 100644 --- a/dmd/root/dcompat.h +++ b/dmd/root/dcompat.h @@ -36,7 +36,7 @@ struct DString : public DArray }; /// Corresponding C++ type that maps to D size_t -#if __APPLE__ && __i386__ +#if __APPLE__ && (__i386__ || __ppc__) // size_t is 'unsigned long', which makes it mangle differently than D's 'uint' typedef unsigned d_size_t; #elif MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \ @@ -49,3 +49,11 @@ typedef unsigned d_size_t; #else typedef size_t d_size_t; #endif + +/// Corresponding C++ type that maps to D bool +#if __APPLE__ && __ppc__ +// bool is defined as an 'int', which does not match same size as D +typedef uint8_t d_bool; +#else +typedef bool d_bool; +#endif diff --git a/dmd/root/optional.h b/dmd/root/optional.h index cc2ee79edeb..353332c2199 100644 --- a/dmd/root/optional.h +++ b/dmd/root/optional.h @@ -11,6 +11,8 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h */ +#include "dcompat.h" // for d_bool + /// Optional type that is either `empty` or contains a value of type `T` template struct Optional final @@ -20,7 +22,7 @@ struct Optional final T value; /** whether `value` is set **/ - bool present; + d_bool present; public: /** Creates an `Optional` with the given value **/ diff --git a/dmd/scope.h b/dmd/scope.h index b25c26afff2..da114289850 100644 --- a/dmd/scope.h +++ b/dmd/scope.h @@ -81,8 +81,8 @@ struct Scope ForeachStatement *fes; // if nested function for ForeachStatement, this is it Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ Dsymbol *inunion; // !=null if processing members of a union - bool nofree; // true if shouldn't free it - bool inLoop; // true if inside a loop (where constructor calls aren't allowed) + d_bool nofree; // true if shouldn't free it + d_bool inLoop; // true if inside a loop (where constructor calls aren't allowed) int intypeof; // in typeof(exp) VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init diff --git a/dmd/statement.h b/dmd/statement.h index 46cc4dadf64..6d1f85b38d9 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -433,7 +433,7 @@ class SwitchStatement final : public Statement public: Expression *condition; Statement *_body; - bool isFinal; + d_bool isFinal; DefaultStatement *sdefault; Statement *tryBody; // set to TryCatchStatement or TryFinallyStatement if in _body portion @@ -600,11 +600,11 @@ class Catch final : public RootObject VarDeclaration *var; // set if semantic processing errors - bool errors; + d_bool errors; // was generated by the compiler, // wasn't present in source code - bool internalCatch; + d_bool internalCatch; Catch *syntaxCopy(); }; @@ -616,7 +616,7 @@ class TryFinallyStatement final : public Statement Statement *finalbody; Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion - bool bodyFallsThru; // true if _body falls through to finally + d_bool bodyFallsThru; // true if _body falls through to finally static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); TryFinallyStatement *syntaxCopy() override; @@ -643,7 +643,7 @@ class ThrowStatement final : public Statement Expression *exp; // was generated by the compiler, // wasn't present in source code - bool internalThrow; + d_bool internalThrow; ThrowStatement *syntaxCopy() override; @@ -668,7 +668,7 @@ class GotoStatement final : public Statement TryFinallyStatement *tf; ScopeGuardStatement *os; VarDeclaration *lastVar; - bool inCtfeBlock; + d_bool inCtfeBlock; GotoStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -685,8 +685,8 @@ class LabelStatement final : public Statement VarDeclaration *lastVar; Statement *gotoTarget; // interpret void* extra; // used by Statement_toIR() - bool breaks; // someone did a 'break ident' - bool inCtfeBlock; + d_bool breaks; // someone did a 'break ident' + d_bool inCtfeBlock; LabelStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -697,8 +697,8 @@ class LabelDsymbol final : public Dsymbol public: LabelStatement *statement; - bool deleted; // set if rewritten to return in foreach delegate - bool iasm; // set if used by inline assembler + d_bool deleted; // set if rewritten to return in foreach delegate + d_bool iasm; // set if used by inline assembler static LabelDsymbol *create(Identifier *ident); LabelDsymbol *isLabel() override; @@ -722,8 +722,8 @@ class InlineAsmStatement final : public AsmStatement code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) - bool refparam; // true if function parameter is referenced - bool naked; // true if function is to be naked + d_bool refparam; // true if function parameter is referenced + d_bool naked; // true if function is to be naked InlineAsmStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } diff --git a/dmd/target.h b/dmd/target.h index 5bd5162d8d4..8e1c9699f5c 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -92,11 +92,11 @@ struct TargetCPP Microsoft, Sun }; - bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order - bool exceptions; // set if catching C++ exceptions is supported - bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable - bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases - bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime + d_bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order + d_bool exceptions; // set if catching C++ exceptions is supported + d_bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable + d_bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases + d_bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime Runtime runtime; const char *toMangle(Dsymbol *s); @@ -110,7 +110,7 @@ struct TargetCPP struct TargetObjC { - bool supported; // set if compiler can interface with Objective-C + d_bool supported; // set if compiler can interface with Objective-C }; struct Target @@ -156,15 +156,15 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target - bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd - bool isLP64; // pointers are 64 bits + d_bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd + d_bool isLP64; // pointers are 64 bits // Environmental DString obj_ext; /// extension for object files DString lib_ext; /// extension for static library files DString dll_ext; /// extension for dynamic library files - bool run_noext; /// allow -run sources without extensions - bool omfobj; /// for Win32: write OMF object files instead of COFF + d_bool run_noext; /// allow -run sources without extensions + d_bool omfobj; /// for Win32: write OMF object files instead of COFF template struct FPTypeProperties diff --git a/dmd/template.h b/dmd/template.h index 12b21207b8e..8622b5c6483 100644 --- a/dmd/template.h +++ b/dmd/template.h @@ -78,12 +78,12 @@ class TemplateDeclaration final : public ScopeDsymbol Dsymbol *onemember; // if !=NULL then one member of this template - bool literal; // this template declaration is a literal - bool ismixin; // template declaration is only to be used as a mixin - bool isstatic; // this is static template declaration - bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } - bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` - bool deprecated_; // this template declaration is deprecated + d_bool literal; // this template declaration is a literal + d_bool ismixin; // template declaration is only to be used as a mixin + d_bool isstatic; // this is static template declaration + d_bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } + d_bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` + d_bool deprecated_; // this template declaration is deprecated Visibility visibility; TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack @@ -133,7 +133,7 @@ class TemplateParameter : public ASTNode * A dependent template parameter should return MATCHexact in matchArg() * to respect the match level of the corresponding precedent parameter. */ - bool dependent; + d_bool dependent; virtual TemplateTypeParameter *isTemplateTypeParameter(); virtual TemplateValueParameter *isTemplateValueParameter(); diff --git a/dmd/visitor.h b/dmd/visitor.h index f8cbdb48c92..ed9f9ce1f6f 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -10,6 +10,7 @@ #pragma once #include "root/dsystem.h" +#include "root/dcompat.h" // for d_bool class Statement; class ErrorStatement; @@ -663,6 +664,6 @@ class Visitor : public ParseTimeVisitor class StoppableVisitor : public Visitor { public: - bool stop; + d_bool stop; StoppableVisitor() : stop(false) {} }; From 0f30ec15b5bd07c4143a1e9d1701fd4f176a4172 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 2 Mar 2023 11:04:19 +0000 Subject: [PATCH 002/301] Rename CompileDeclaration to MixinDeclaration (dlang/dmd!14935) * Rename CompileDeclaration to MixinDeclaration * Rename isCompileDeclaration to isMixinDeclaration * Regen frontend.h --- dmd/attrib.d | 15 ++++++++------- dmd/attrib.h | 4 ++-- dmd/dsymbol.d | 2 +- dmd/dsymbol.h | 2 +- dmd/dsymbolsem.d | 8 ++++---- dmd/frontend.h | 16 ++++++++-------- dmd/hdrgen.d | 2 +- dmd/parse.d | 2 +- dmd/parsetimevisitor.d | 2 +- dmd/strictvisitor.d | 2 +- dmd/transitivevisitor.d | 2 +- dmd/typesem.d | 2 +- dmd/visitor.h | 4 ++-- 13 files changed, 32 insertions(+), 31 deletions(-) diff --git a/dmd/attrib.d b/dmd/attrib.d index dbe78ef74bc..8942e50a299 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -1279,7 +1279,8 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration * mixin("int x"); * https://dlang.org/spec/module.html#mixin-declaration */ -extern (C++) final class CompileDeclaration : AttribDeclaration +// Note: was CompileDeclaration +extern (C++) final class MixinDeclaration : AttribDeclaration { Expressions* exps; ScopeDsymbol scopesym; @@ -1288,19 +1289,19 @@ extern (C++) final class CompileDeclaration : AttribDeclaration extern (D) this(const ref Loc loc, Expressions* exps) { super(loc, null, null); - //printf("CompileDeclaration(loc = %d)\n", loc.linnum); + //printf("MixinDeclaration(loc = %d)\n", loc.linnum); this.exps = exps; } - override CompileDeclaration syntaxCopy(Dsymbol s) + override MixinDeclaration syntaxCopy(Dsymbol s) { - //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); - return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps)); + //printf("MixinDeclaration::syntaxCopy('%s')\n", toChars()); + return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps)); } override void addMember(Scope* sc, ScopeDsymbol sds) { - //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); + //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); this.scopesym = sds; } @@ -1314,7 +1315,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration return "mixin"; } - override inout(CompileDeclaration) isCompileDeclaration() inout + override inout(MixinDeclaration) isMixinDeclaration() inout { return this; } diff --git a/dmd/attrib.h b/dmd/attrib.h index 113653e9d7c..1e75598c72a 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -221,7 +221,7 @@ class ForwardingAttribDeclaration final : public AttribDeclaration // Mixin declarations -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Expressions *exps; @@ -229,7 +229,7 @@ class CompileDeclaration final : public AttribDeclaration ScopeDsymbol *scopesym; d_bool compiled; - CompileDeclaration *syntaxCopy(Dsymbol *s) override; + MixinDeclaration *syntaxCopy(Dsymbol *s) override; void addMember(Scope *sc, ScopeDsymbol *sds) override; void setScope(Scope *sc) override; const char *kind() const override; diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index aa478f2fea2..c21b20e6881 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -1407,7 +1407,7 @@ extern (C++) class Dsymbol : ASTNode inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } inout(OverloadSet) isOverloadSet() inout { return null; } - inout(CompileDeclaration) isCompileDeclaration() inout { return null; } + inout(MixinDeclaration) isMixinDeclaration() inout { return null; } inout(StaticAssert) isStaticAssert() inout { return null; } inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } } diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index 039a28871b3..96fa8fdd640 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -321,7 +321,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; } virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; } virtual OverloadSet *isOverloadSet() { return NULL; } - virtual CompileDeclaration *isCompileDeclaration() { return NULL; } + virtual MixinDeclaration *isMixinDeclaration() { return NULL; } virtual StaticAssert *isStaticAssert() { return NULL; } virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } void accept(Visitor *v) override { v->visit(this); } diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 6697ad6d4d6..61811fe5449 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1922,9 +1922,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor attribSemantic(sfd); } - private Dsymbols* compileIt(CompileDeclaration cd) + private Dsymbols* compileIt(MixinDeclaration cd) { - //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); + //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); OutBuffer buf; if (expressionsToString(buf, sc, cd.exps)) return null; @@ -1951,9 +1951,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor /*********************************************************** * https://dlang.org/spec/module.html#mixin-declaration */ - override void visit(CompileDeclaration cd) + override void visit(MixinDeclaration cd) { - //printf("CompileDeclaration::semantic()\n"); + //printf("MixinDeclaration::semantic()\n"); if (!cd.compiled) { cd.decl = compileIt(cd); diff --git a/dmd/frontend.h b/dmd/frontend.h index 5da2e4bade8..fceb567e978 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -100,7 +100,7 @@ class AttribDeclaration; class AnonDeclaration; class VisibilityDeclaration; class OverloadSet; -class CompileDeclaration; +class MixinDeclaration; class StaticAssert; class StaticIfDeclaration; class DsymbolTable; @@ -570,7 +570,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration* isCPPNamespaceDeclaration(); virtual VisibilityDeclaration* isVisibilityDeclaration(); virtual OverloadSet* isOverloadSet(); - virtual CompileDeclaration* isCompileDeclaration(); + virtual MixinDeclaration* isMixinDeclaration(); virtual StaticAssert* isStaticAssert(); virtual StaticIfDeclaration* isStaticIfDeclaration(); }; @@ -1889,7 +1889,7 @@ class ParseTimeVisitor virtual void visit(typename AST::StaticDtorDeclaration s); virtual void visit(typename AST::SharedStaticCtorDeclaration s); virtual void visit(typename AST::SharedStaticDtorDeclaration s); - virtual void visit(typename AST::CompileDeclaration s); + virtual void visit(typename AST::MixinDeclaration s); virtual void visit(typename AST::UserAttributeDeclaration s); virtual void visit(typename AST::LinkDeclaration s); virtual void visit(typename AST::AnonDeclaration s); @@ -4780,11 +4780,11 @@ struct ASTCodegen final using AttribDeclaration = ::AttribDeclaration; using CPPMangleDeclaration = ::CPPMangleDeclaration; using CPPNamespaceDeclaration = ::CPPNamespaceDeclaration; - using CompileDeclaration = ::CompileDeclaration; using ConditionalDeclaration = ::ConditionalDeclaration; using DeprecatedDeclaration = ::DeprecatedDeclaration; using ForwardingAttribDeclaration = ::ForwardingAttribDeclaration; using LinkDeclaration = ::LinkDeclaration; + using MixinDeclaration = ::MixinDeclaration; using PragmaDeclaration = ::PragmaDeclaration; using StaticForeachDeclaration = ::StaticForeachDeclaration; using StaticIfDeclaration = ::StaticIfDeclaration; @@ -5591,17 +5591,17 @@ class ForwardingAttribDeclaration final : public AttribDeclaration void accept(Visitor* v) override; }; -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Array* exps; ScopeDsymbol* scopesym; bool compiled; - CompileDeclaration* syntaxCopy(Dsymbol* s) override; + MixinDeclaration* syntaxCopy(Dsymbol* s) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; void setScope(Scope* sc) override; const char* kind() const override; - CompileDeclaration* isCompileDeclaration() override; + MixinDeclaration* isMixinDeclaration() override; void accept(Visitor* v) override; }; @@ -8271,7 +8271,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(AnonDeclaration* d) override; void visit(PragmaDeclaration* d) override; void visit(ConditionalDeclaration* d) override; - void visit(CompileDeclaration* d) override; + void visit(MixinDeclaration* d) override; void visit(UserAttributeDeclaration* d) override; virtual void visitFuncBody(FuncDeclaration* f); virtual void visitBaseClasses(ClassDeclaration* d); diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index c7e5690bc0a..08564972c5a 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -1160,7 +1160,7 @@ public: } - override void visit(CompileDeclaration d) + override void visit(MixinDeclaration d) { buf.writestring("mixin("); argsToBuffer(d.exps, buf, hgs, null); diff --git a/dmd/parse.d b/dmd/parse.d index 36a76f50da2..bc08fd5a63c 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -381,7 +381,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); auto exps = parseArguments(); check(TOK.semicolon); - s = new AST.CompileDeclaration(loc, exps); + s = new AST.MixinDeclaration(loc, exps); break; } case TOK.template_: diff --git a/dmd/parsetimevisitor.d b/dmd/parsetimevisitor.d index 387b28c1532..cc33332cbbe 100644 --- a/dmd/parsetimevisitor.d +++ b/dmd/parsetimevisitor.d @@ -66,7 +66,7 @@ public: void visit(AST.SharedStaticDtorDeclaration s) { visit(cast(AST.StaticDtorDeclaration)s); } // AttribDeclarations - void visit(AST.CompileDeclaration s) { visit(cast(AST.AttribDeclaration)s); } + void visit(AST.MixinDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.UserAttributeDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.LinkDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.AnonDeclaration s) { visit(cast(AST.AttribDeclaration)s); } diff --git a/dmd/strictvisitor.d b/dmd/strictvisitor.d index 7c2f6ae89e4..12bdaf9ab7a 100644 --- a/dmd/strictvisitor.d +++ b/dmd/strictvisitor.d @@ -45,7 +45,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.TemplateDeclaration) { assert(0); } override void visit(AST.TemplateInstance) { assert(0); } override void visit(AST.Nspace) { assert(0); } - override void visit(AST.CompileDeclaration) { assert(0); } + override void visit(AST.MixinDeclaration) { assert(0); } override void visit(AST.UserAttributeDeclaration) { assert(0); } override void visit(AST.LinkDeclaration) { assert(0); } override void visit(AST.AnonDeclaration) { assert(0); } diff --git a/dmd/transitivevisitor.d b/dmd/transitivevisitor.d index 5844911bc6a..05af17e55b5 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/transitivevisitor.d @@ -579,7 +579,7 @@ package mixin template ParseVisitMethods(AST) de.accept(this); } - override void visit(AST.CompileDeclaration d) + override void visit(AST.MixinDeclaration d) { //printf("Visiting compileDeclaration\n"); visitArgs(d.exps.peekSlice()); diff --git a/dmd/typesem.d b/dmd/typesem.d index 84561ac467a..965acef8d22 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -2697,7 +2697,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type { void semanticOnMixin(Dsymbol member) { - if (auto compileDecl = member.isCompileDeclaration()) + if (auto compileDecl = member.isMixinDeclaration()) compileDecl.dsymbolSemantic(sc); else if (auto mixinTempl = member.isTemplateMixin()) mixinTempl.dsymbolSemantic(sc); diff --git a/dmd/visitor.h b/dmd/visitor.h index ed9f9ce1f6f..88b376c3762 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -110,7 +110,7 @@ class AnonDeclaration; class PragmaDeclaration; class ConditionalDeclaration; class StaticIfDeclaration; -class CompileDeclaration; +class MixinDeclaration; class StaticForeachDeclaration; class UserAttributeDeclaration; class ForwardingAttribDeclaration; @@ -365,7 +365,7 @@ class ParseTimeVisitor virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); } // AttribDeclarations - virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); } + virtual void visit(MixinDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); } From 28d8356df8d659e9d5762d9659973b3c472ef7de Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 2 Mar 2023 11:04:41 +0000 Subject: [PATCH 003/301] Rename CompileStatement to MixinStatement (dlang/dmd!14933) * Rename CompileStatement to MixinStatement * Rename isCompileStatement to isMixinStatement * rename STMT.Compile to STMT.Mixin * Regen frontend.h --- dmd/astenums.d | 2 +- dmd/blockexit.d | 2 +- dmd/clone.d | 2 +- dmd/foreachvar.d | 2 +- dmd/frontend.h | 28 ++++++++++++++-------------- dmd/hdrgen.d | 2 +- dmd/ob.d | 2 +- dmd/parse.d | 2 +- dmd/parsetimevisitor.d | 2 +- dmd/statement.d | 11 ++++++----- dmd/statement.h | 8 ++++---- dmd/statementsem.d | 8 ++++---- dmd/strictvisitor.d | 2 +- dmd/transitivevisitor.d | 4 ++-- dmd/visitor.h | 4 ++-- 15 files changed, 41 insertions(+), 40 deletions(-) diff --git a/dmd/astenums.d b/dmd/astenums.d index 6e882082bed..60ca4d015cc 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -339,7 +339,7 @@ enum STMT : ubyte Error, Peel, Exp, DtorExp, - Compile, + Mixin, Compound, CompoundDeclaration, CompoundAsm, UnrolledLoop, Scope, diff --git a/dmd/blockexit.d b/dmd/blockexit.d index bd5b78e92dc..eccc15d9e6d 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -115,7 +115,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(CompileStatement s) + override void visit(MixinStatement s) { assert(global.errors); result = BE.fallthru; diff --git a/dmd/clone.d b/dmd/clone.d index 19bf83e4ec3..fb3929fe913 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -840,7 +840,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) " else " ~ " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ "return h;"; - fop.fbody = new CompileStatement(loc, new StringExp(loc, code)); + fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; diff --git a/dmd/foreachvar.d b/dmd/foreachvar.d index ba2825a3098..7a964695f45 100644 --- a/dmd/foreachvar.d +++ b/dmd/foreachvar.d @@ -299,7 +299,7 @@ void foreachExpAndVar(Statement s, case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: assert(0); // should have been rewritten diff --git a/dmd/frontend.h b/dmd/frontend.h index fceb567e978..5bf43bbef70 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -315,7 +315,7 @@ class StaticForeachStatement; class GotoDefaultStatement; class BreakStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class ForwardingStatement; class ContinueStatement; class ThrowStatement; @@ -1916,7 +1916,7 @@ class ParseTimeVisitor virtual void visit(typename AST::ReturnStatement s); virtual void visit(typename AST::LabelStatement s); virtual void visit(typename AST::StaticAssertStatement s); - virtual void visit(typename AST::CompileStatement s); + virtual void visit(typename AST::MixinStatement s); virtual void visit(typename AST::WhileStatement s); virtual void visit(typename AST::ForStatement s); virtual void visit(typename AST::DoStatement s); @@ -4053,7 +4053,7 @@ enum class STMT : uint8_t Peel = 1u, Exp = 2u, DtorExp = 3u, - Compile = 4u, + Mixin = 4u, Compound = 5u, CompoundDeclaration = 6u, CompoundAsm = 7u, @@ -4132,7 +4132,7 @@ class Statement : public ASTNode GotoCaseStatement* isGotoCaseStatement(); BreakStatement* isBreakStatement(); DtorExpStatement* isDtorExpStatement(); - CompileStatement* isCompileStatement(); + MixinStatement* isMixinStatement(); ForwardingStatement* isForwardingStatement(); DoStatement* isDoStatement(); WhileStatement* isWhileStatement(); @@ -4203,14 +4203,6 @@ class Catch final : public RootObject Catch* syntaxCopy(); }; -class CompileStatement final : public Statement -{ -public: - Array* exps; - CompileStatement* syntaxCopy() override; - void accept(Visitor* v) override; -}; - class CompoundStatement : public Statement { public: @@ -4477,6 +4469,14 @@ class LabelStatement final : public Statement void accept(Visitor* v) override; }; +class MixinStatement final : public Statement +{ +public: + Array* exps; + MixinStatement* syntaxCopy() override; + void accept(Visitor* v) override; +}; + class PeelStatement final : public Statement { public: @@ -5060,7 +5060,6 @@ struct ASTCodegen final using CaseRangeStatement = ::CaseRangeStatement; using CaseStatement = ::CaseStatement; using Catch = ::Catch; - using CompileStatement = ::CompileStatement; using CompoundAsmStatement = ::CompoundAsmStatement; using CompoundDeclarationStatement = ::CompoundDeclarationStatement; using CompoundStatement = ::CompoundStatement; @@ -5085,6 +5084,7 @@ struct ASTCodegen final using InlineAsmStatement = ::InlineAsmStatement; using LabelDsymbol = ::LabelDsymbol; using LabelStatement = ::LabelStatement; + using MixinStatement = ::MixinStatement; using PeelStatement = ::PeelStatement; using PragmaStatement = ::PragmaStatement; using ReturnStatement = ::ReturnStatement; @@ -8210,7 +8210,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor public: using SemanticTimePermissiveVisitor::visit; void visit(ExpStatement* s) override; - void visit(CompileStatement* s) override; + void visit(MixinStatement* s) override; void visit(CompoundStatement* s) override; virtual void visitVarDecl(VarDeclaration* v); void visit(CompoundDeclarationStatement* s) override; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 08564972c5a..67fdce91c4b 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -180,7 +180,7 @@ public: buf.writenl(); } - override void visit(CompileStatement s) + override void visit(MixinStatement s) { buf.writestring("mixin("); argsToBuffer(s.exps, buf, hgs, null); diff --git a/dmd/ob.d b/dmd/ob.d index 9cff76b84aa..89728b64486 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -844,7 +844,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: debug printf("s: %s\n", s.toChars()); diff --git a/dmd/parse.d b/dmd/parse.d index bc08fd5a63c..6f7bd594f5a 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -5959,7 +5959,7 @@ LagainStc: if (e.op == EXP.mixin_) { AST.MixinExp cpe = cast(AST.MixinExp)e; - s = new AST.CompileStatement(loc, cpe.exps); + s = new AST.MixinStatement(loc, cpe.exps); } else { diff --git a/dmd/parsetimevisitor.d b/dmd/parsetimevisitor.d index cc33332cbbe..a4a9434334e 100644 --- a/dmd/parsetimevisitor.d +++ b/dmd/parsetimevisitor.d @@ -99,7 +99,7 @@ public: void visit(AST.ReturnStatement s) { visit(cast(AST.Statement)s); } void visit(AST.LabelStatement s) { visit(cast(AST.Statement)s); } void visit(AST.StaticAssertStatement s) { visit(cast(AST.Statement)s); } - void visit(AST.CompileStatement s) { visit(cast(AST.Statement)s); } + void visit(AST.MixinStatement s) { visit(cast(AST.Statement)s); } void visit(AST.WhileStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ForStatement s) { visit(cast(AST.Statement)s); } void visit(AST.DoStatement s) { visit(cast(AST.Statement)s); } diff --git a/dmd/statement.d b/dmd/statement.d index 90728fb6fec..30f9ad44d35 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -388,7 +388,7 @@ extern (C++) abstract class Statement : ASTNode inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } - inout(CompileStatement) isCompileStatement() { return stmt == STMT.Compile ? cast(typeof(return))this : null; } + inout(MixinStatement) isMixinStatement() { return stmt == STMT.Mixin ? cast(typeof(return))this : null; } inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } @@ -518,7 +518,8 @@ extern (C++) final class DtorExpStatement : ExpStatement /*********************************************************** * https://dlang.org/spec/statement.html#mixin-statement */ -extern (C++) final class CompileStatement : Statement +// Note: was called CompileStatement +extern (C++) final class MixinStatement : Statement { Expressions* exps; @@ -531,13 +532,13 @@ extern (C++) final class CompileStatement : Statement extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, STMT.Compile); + super(loc, STMT.Mixin); this.exps = exps; } - override CompileStatement syntaxCopy() + override MixinStatement syntaxCopy() { - return new CompileStatement(loc, Expression.arraySyntaxCopy(exps)); + return new MixinStatement(loc, Expression.arraySyntaxCopy(exps)); } override void accept(Visitor v) diff --git a/dmd/statement.h b/dmd/statement.h index 6d1f85b38d9..b7403b5d8ad 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -65,7 +65,7 @@ enum STMTerror, STMTpeel, STMTexp, STMTdtorExp, - STMTcompile, + STMTmixin, STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm, STMTunrolledLoop, STMTscope, @@ -143,7 +143,7 @@ class Statement : public ASTNode GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } - CompileStatement *isCompileStatement() { return stmt == STMTcompile ? (CompileStatement*)this : NULL; } + MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : NULL; } ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } @@ -206,12 +206,12 @@ class DtorExpStatement final : public ExpStatement void accept(Visitor *v) override { v->visit(this); } }; -class CompileStatement final : public Statement +class MixinStatement final : public Statement { public: Expressions *exps; - CompileStatement *syntaxCopy() override; + MixinStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index bbaee8e6152..f713456e743 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -226,12 +226,12 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(CompileStatement cs) + override void visit(MixinStatement cs) { /* https://dlang.org/spec/statement.html#mixin-statement */ - //printf("CompileStatement::semantic() %s\n", exp.toChars()); + //printf("MixinStatement::semantic() %s\n", exp.toChars()); Statements* a = cs.flatten(sc); if (!a) return; @@ -4733,8 +4733,8 @@ private Statements* flatten(Statement statement, Scope* sc) (*a)[0] = ls; return a; - case STMT.Compile: - auto cs = statement.isCompileStatement(); + case STMT.Mixin: + auto cs = statement.isMixinStatement(); OutBuffer buf; diff --git a/dmd/strictvisitor.d b/dmd/strictvisitor.d index 12bdaf9ab7a..82e91c6aa76 100644 --- a/dmd/strictvisitor.d +++ b/dmd/strictvisitor.d @@ -71,7 +71,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.ReturnStatement) { assert(0); } override void visit(AST.LabelStatement) { assert(0); } override void visit(AST.StaticAssertStatement) { assert(0); } - override void visit(AST.CompileStatement) { assert(0); } + override void visit(AST.MixinStatement) { assert(0); } override void visit(AST.WhileStatement) { assert(0); } override void visit(AST.ForStatement) { assert(0); } override void visit(AST.DoStatement) { assert(0); } diff --git a/dmd/transitivevisitor.d b/dmd/transitivevisitor.d index 05af17e55b5..c58827063d2 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/transitivevisitor.d @@ -44,9 +44,9 @@ package mixin template ParseVisitMethods(AST) } } - override void visit(AST.CompileStatement s) + override void visit(AST.MixinStatement s) { - //printf("Visiting CompileStatement\n"); + //printf("Visiting MixinStatement\n"); visitArgs(s.exps.peekSlice()); } diff --git a/dmd/visitor.h b/dmd/visitor.h index 88b376c3762..4cf798ad5ca 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -17,7 +17,7 @@ class ErrorStatement; class PeelStatement; class ExpStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class CompoundStatement; class CompoundDeclarationStatement; class UnrolledLoopStatement; @@ -396,7 +396,7 @@ class ParseTimeVisitor virtual void visit(ReturnStatement *s) { visit((Statement *)s); } virtual void visit(LabelStatement *s) { visit((Statement *)s); } virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); } - virtual void visit(CompileStatement *s) { visit((Statement *)s); } + virtual void visit(MixinStatement *s) { visit((Statement *)s); } virtual void visit(WhileStatement *s) { visit((Statement *)s); } virtual void visit(ForStatement *s) { visit((Statement *)s); } virtual void visit(DoStatement *s) { visit((Statement *)s); } From 9bc2953910deae8392697d0827bda13ccb4349df Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Fri, 3 Mar 2023 09:25:17 +0100 Subject: [PATCH 004/301] merge stable (dlang/dmd!14947) * Fix Issue 22039 - ICE on infinite recursion in default parameter (dlang/dmd!14934) * remove IRState.falseBlock - stable version --------- Co-authored-by: Razvan Nitu Co-authored-by: Walter Bright --- dmd/expressionsem.d | 3 ++- tests/dmd/fail_compilation/fail22039.d | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/fail22039.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 9b9fbb8ad77..d186abc0552 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -1728,7 +1728,6 @@ private bool functionParameters(const ref Loc loc, Scope* sc, const size_t nparams = tf.parameterList.length; const olderrors = global.errors; bool err = false; - *prettype = Type.terror; Expression eprefix = null; *peprefix = null; @@ -1817,6 +1816,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, return errorArgs(); } arg = p.defaultArg; + if (!arg.type) + arg = arg.expressionSemantic(sc); arg = inlineCopy(arg, sc); // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ arg = arg.resolveLoc(loc, sc); diff --git a/tests/dmd/fail_compilation/fail22039.d b/tests/dmd/fail_compilation/fail22039.d new file mode 100644 index 00000000000..3df834f7ad0 --- /dev/null +++ b/tests/dmd/fail_compilation/fail22039.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=22039 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail22039.d(11): Error: recursive evaluation of `func()` +fail_compilation/fail22039.d(14): Error: recursive evaluation of `gun(func2())` +--- +*/ + +int func(int x = func()) { return x; } + +int gun() { return 2; } +int func2(int x = gun(func2())) { return x; } From 74841e528a94f407f2097b3d39640c0df1339086 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 3 Mar 2023 01:20:33 -0800 Subject: [PATCH 005/301] colorHighlightCode: use ErrorSinkNull instead of gagging (dlang/dmd!14920) --- dmd/errors.d | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dmd/errors.d b/dmd/errors.d index 05b884c280e..b89377237e2 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -819,8 +819,11 @@ private void colorHighlightCode(ref OutBuffer buf) } ++nested; - auto gaggedErrorsSave = global.startGagging(); - scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, global.vendor, global.versionNumber()); + __gshared ErrorSinkNull errorSinkNull; + if (!errorSinkNull) + errorSinkNull = new ErrorSinkNull; + + scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, errorSinkNull, global.vendor, global.versionNumber()); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); @@ -871,7 +874,6 @@ private void colorHighlightCode(ref OutBuffer buf) //printf("res = '%.*s'\n", cast(int)buf.length, buf[].ptr); buf.setsize(0); buf.write(&res); - global.endGagging(gaggedErrorsSave); --nested; } From 4af6384fa276701dcfa44d42c49c561530470485 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Wed, 1 Mar 2023 12:40:02 +0100 Subject: [PATCH 006/301] core.demangle: Document why we're shifting in parseTypeFunction --- runtime/druntime/src/core/demangle.d | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 3fcb26657c8..b8a40fb7224 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -1357,6 +1357,7 @@ pure @safe: auto attributes = parseFuncAttr(); auto argbeg = len; + put(IsDelegate.yes == isdg ? "delegate" : "function"); put( '(' ); parseFuncArguments(); put( ')' ); @@ -1369,16 +1370,17 @@ pure @safe: put(str); } } - auto retbeg = len; - parseType(); - put( ' ' ); - // append delegate/function - if (IsDelegate.yes == isdg) - put( "delegate" ); - else - put( "function" ); - // move arguments and attributes behind name - shift( dst[argbeg .. retbeg] ); + + // A function / delegate return type is located at the end of its mangling + // Write it in order, then shift it back to 'code order' + // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe' + { + auto retbeg = len; + parseType(); + put(' '); + shift(dst[argbeg .. retbeg]); + } + return dst[beg..len]; } From 725f380f0b5e61d8b3dcb61634ca000c6ac3b16e Mon Sep 17 00:00:00 2001 From: Geod24 Date: Wed, 1 Mar 2023 16:09:19 +0100 Subject: [PATCH 007/301] demangler: Nest variable deeper to simplify control flow This makes it more obvious that attr is non-null only in a specific case. --- runtime/druntime/src/core/demangle.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index b8a40fb7224..e18566a3dcb 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -1894,7 +1894,6 @@ pure @safe: auto prevlen = len; auto prevbrp = brp; - char[] attr; try { if ( 'M' == front ) @@ -1910,6 +1909,7 @@ pure @safe: } if ( isCallConvention( front ) ) { + char[] attr; // we don't want calling convention and attributes in the qualified name parseCallConvention(); auto attributes = parseFuncAttr(); @@ -1925,6 +1925,7 @@ pure @safe: put( '(' ); parseFuncArguments(); put( ')' ); + return attr; } } catch ( ParseException ) @@ -1933,9 +1934,8 @@ pure @safe: pos = prevpos; len = prevlen; brp = prevbrp; - attr = null; } - return attr; + return null; } /* From 4c9e16ed329864621608fd71ff1edb5f7d6e2edc Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 12:06:59 +0100 Subject: [PATCH 008/301] core.demangle: Remove shift call from put The shift call was only used from one place, the associative array parseType. Removing it makes code much clearer. --- runtime/druntime/src/core/demangle.d | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index e18566a3dcb..03e52ea41cb 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -265,14 +265,7 @@ pure @safe: void put(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if (!val.length) return; - - if (!contains(dst[0 .. len], val)) - append(val); - else - shift(val); + append(val); } @@ -924,7 +917,7 @@ pure @safe: auto tx = parseType(); parseType(); put( '[' ); - put( tx ); + shift(tx); put( ']' ); return dst[beg .. len]; case 'P': // TypePointer (P Type) From d83c37b3d268276686a66efa64be0a7664a8a211 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 12:41:56 +0100 Subject: [PATCH 009/301] core.demangle: Make error functions non-templated They don't depend on template parameters and are static, so take them out of there. --- runtime/druntime/src/core/demangle.d | 96 +++++++++++++++------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 03e52ea41cb..8ef1779b9c9 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -85,46 +85,6 @@ pure @safe: bool mute = false; Hooks hooks; - static class ParseException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static class OverflowException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new ParseException( msg ); - debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); - throw __ctfe ? new ParseException(msg) - : cast(ParseException) __traits(initSymbol, ParseException).ptr; - - } - - - static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new OverflowException( msg ); - debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); - throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; - } - - ////////////////////////////////////////////////////////////////////////// // Type Testing and Conversion ////////////////////////////////////////////////////////////////////////// @@ -2207,7 +2167,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); size_t n = d.decodeBackref(); if (!n || n > refpos) - d.error("invalid back reference"); + error("invalid back reference"); auto savepos = d.pos; scope(exit) d.pos = savepos; @@ -2215,11 +2175,11 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe auto idlen = d.decodeNumber(); if (d.pos + idlen > d.buf.length) - d.error("invalid back reference"); + error("invalid back reference"); auto id = d.buf[d.pos .. d.pos + idlen]; auto pid = id in idpos; if (!pid) - d.error("invalid back reference"); + error("invalid back reference"); npos = positionInResult(*pid); } encodeBackref(reslen - npos); @@ -2230,7 +2190,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe { auto n = d.decodeNumber(); if (!n || n > d.buf.length || n > d.buf.length - d.pos) - d.error("LName too shot or too long"); + error("LName too shot or too long"); auto id = d.buf[d.pos .. d.pos + n]; d.pos += n; if (auto pid = id in idpos) @@ -2262,7 +2222,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); auto n = d.decodeBackref(); if (n == 0 || n > refPos) - d.error("invalid back reference"); + error("invalid back reference"); size_t npos = positionInResult(refPos - n); size_t reslen = result.length; @@ -2962,3 +2922,49 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d dst[] = buf[]; return dst; } + + +/** + * Error handling through Exceptions + * + * The following types / functions are only used in this module, + * hence why the functions are `@trusted`. + * To make things `@nogc`, default-initialized instances are thrown. + */ +private class ParseException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private class OverflowException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private noreturn error(string msg = "Invalid symbol") @trusted pure +{ + pragma(inline, false); // tame dmd inliner + + //throw new ParseException( msg ); + debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); + throw __ctfe ? new ParseException(msg) + : cast(ParseException) __traits(initSymbol, ParseException).ptr; +} + +/// Ditto +private noreturn overflow(string msg = "Buffer overflow") @trusted pure +{ + pragma(inline, false); // tame dmd inliner + + //throw new OverflowException( msg ); + debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); + throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; +} From 1d363c5c60a1c9ca750fe432b9d6852cee6b8118 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 12:35:50 +0100 Subject: [PATCH 010/301] core.demangle: Separate data output in its own struct --- runtime/druntime/src/core/demangle.d | 243 +++++++++++++++------------ 1 file changed, 132 insertions(+), 111 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 8ef1779b9c9..33023a62afb 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -69,17 +69,13 @@ pure @safe: { buf = buf_; addType = addType_; - dst = dst_; + dst.dst = dst_; } - - enum size_t minBufSize = 4000; - - const(char)[] buf = null; - char[] dst = null; + Buffer dst; + private @property size_t len () const @safe pure nothrow @nogc { return dst.len; } size_t pos = 0; - size_t len = 0; size_t brp = 0; // current back reference pos AddType addType = AddType.yes; bool mute = false; @@ -123,91 +119,18 @@ pure @safe: error(); } - - ////////////////////////////////////////////////////////////////////////// - // Data Output - ////////////////////////////////////////////////////////////////////////// - - - static bool contains( const(char)[] a, const(char)[] b ) @trusted + char[] shift(scope const(char)[] val) return scope { - if (a.length && b.length) - { - auto bend = b.ptr + b.length; - auto aend = a.ptr + a.length; - return a.ptr <= b.ptr && bend <= aend; - } - return false; + if (mute) + return null; + return dst.shift(val); } - - // move val to the end of the dst buffer - char[] shift( const(char)[] val ) + char[] append(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); - - if (len + val.length > dst.length) - overflow(); - size_t v = &val[0] - &dst[0]; - dst[len .. len + val.length] = val[]; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - - return dst[len - val.length .. len]; - } - return null; - } - - // remove val from dst buffer - void remove( const(char)[] val ) - { - pragma(inline, false); // tame dmd inliner - - if ( val.length ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); - size_t v = &val[0] - &dst[0]; - assert( len >= val.length && len <= dst.length ); - len -= val.length; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - } - } - - char[] append( const(char)[] val ) return scope - { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - if ( !dst.length ) - dst.length = minBufSize; - assert( !contains( dst[0 .. len], val ) ); - debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); - - if ( dst.length - len >= val.length && &dst[len] == &val[0] ) - { - // data is already in place - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - if ( dst.length - len >= val.length ) - { - dst[len .. len + val.length] = val[]; - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - overflow(); - } - return null; + if (mute) + return null; + return dst.append(val); } void putComma(size_t n) @@ -260,7 +183,7 @@ pure @safe: { debug(trace) printf( "silent+\n" ); debug(trace) scope(success) printf( "silent-\n" ); - auto n = len; dg(); len = n; + auto n = len; dg(); dst.len = n; } @@ -1667,7 +1590,7 @@ pure @safe: } catch ( ParseException e ) { - len = l; + dst.len = l; pos = p; brp = b; debug(trace) printf( "not a mangled name arg\n" ); @@ -1695,7 +1618,7 @@ pure @safe: } qlen /= 10; // retry with one digit less pos = --p; - len = l; + dst.len = l; brp = b; } } @@ -1826,7 +1749,7 @@ pure @safe: catch ( ParseException e ) { debug(trace) printf( "not a template instance name\n" ); - len = t; + dst.len = t; } } goto case; @@ -1885,7 +1808,7 @@ pure @safe: { // not part of a qualified name, so back up pos = prevpos; - len = prevlen; + dst.len = prevlen; brp = prevbrp; } return null; @@ -1938,7 +1861,7 @@ pure @safe: do { if ( attr ) - remove( attr ); // dump attributes of parent symbols + dst.remove(attr); // dump attributes of parent symbols if ( beg != len ) put( '.' ); parseSymbolName(); @@ -1971,7 +1894,7 @@ pure @safe: { // remove type assert( attr.length == 0 ); - len = lastlen; + dst.len = lastlen; } if ( pos >= buf.length || (n != 0 && pos >= end) ) return; @@ -1995,15 +1918,6 @@ pure @safe: parseMangledName( AddType.yes == addType ); } - char[] copyInput() return scope - { - if (dst.length < buf.length) - dst.length = buf.length; - char[] r = dst[0 .. buf.length]; - r[] = buf[]; - return r; - } - char[] doDemangle(alias FUNC)() return scope { while ( true ) @@ -2017,12 +1931,12 @@ pure @safe: catch ( OverflowException e ) { debug(trace) printf( "overflow... restarting\n" ); - auto a = minBufSize; - auto b = 2 * dst.length; + auto a = Buffer.minSize; + auto b = 2 * dst.dst.length; auto newsz = a < b ? b : a; debug(info) printf( "growing dst to %lu bytes\n", newsz ); - dst.length = newsz; - pos = len = brp = 0; + dst.dst.length = newsz; + pos = dst.len = brp = 0; continue; } catch ( ParseException e ) @@ -2032,7 +1946,7 @@ pure @safe: auto msg = e.toString(); printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); } - return copyInput(); + return dst.copyInput(buf); } catch ( Exception e ) { @@ -2074,7 +1988,7 @@ char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, // fast path (avoiding throwing & catching exception) for obvious // non-D mangled names if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D")) - return d.copyInput(); + return d.dst.copyInput(buf); return d.demangleName(); } @@ -2923,7 +2837,6 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d return dst; } - /** * Error handling through Exceptions * @@ -2968,3 +2881,111 @@ private noreturn overflow(string msg = "Buffer overflow") @trusted pure debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; } + +private struct Buffer +{ + enum size_t minSize = 4000; + + @safe pure: + + private char[] dst; + private size_t len; + + public inout(char)[] opSlice (size_t from, size_t to) + inout return scope @safe pure nothrow @nogc + { + assert(from <= to); + assert(to <= len); + return this.dst[from .. to]; + } + + static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted + { + if (a.length && b.length) + { + auto bend = b.ptr + b.length; + auto aend = a.ptr + a.length; + return a.ptr <= b.ptr && bend <= aend; + } + return false; + } + + char[] copyInput(scope const(char)[] buf) + return scope nothrow + { + if (dst.length < buf.length) + dst.length = buf.length; + char[] r = dst[0 .. buf.length]; + r[] = buf[]; + return r; + } + + // move val to the end of the dst buffer + char[] shift(scope const(char)[] val) return scope + { + pragma(inline, false); // tame dmd inliner + + if (val.length) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); + + if (len + val.length > dst.length) + overflow(); + size_t v = &val[0] - &dst[0]; + dst[len .. len + val.length] = val[]; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + + return dst[len - val.length .. len]; + } + return null; + } + + // remove val from dst buffer + void remove(scope const(char)[] val) scope + { + pragma(inline, false); // tame dmd inliner + + if ( val.length ) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); + size_t v = &val[0] - &dst[0]; + assert( len >= val.length && len <= dst.length ); + len -= val.length; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + } + } + + char[] append(scope const(char)[] val) return scope + { + pragma(inline, false); // tame dmd inliner + + if (val.length) + { + if ( !dst.length ) + dst.length = minSize; + assert( !contains( dst[0 .. len], val ) ); + debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); + + if ( dst.length - len >= val.length && &dst[len] == &val[0] ) + { + // data is already in place + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + if ( dst.length - len >= val.length ) + { + dst[len .. len + val.length] = val[]; + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + overflow(); + } + return null; + } +} From 863713381ea969912d24e1ad5dd3ee9d508ecab5 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 12:46:51 +0100 Subject: [PATCH 011/301] core.demangle: Fold append into put There was only one usage and it wasn't using the return type. --- runtime/druntime/src/core/demangle.d | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 33023a62afb..55e7a1dad30 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -126,13 +126,6 @@ pure @safe: return dst.shift(val); } - char[] append(scope const(char)[] val) return scope - { - if (mute) - return null; - return dst.append(val); - } - void putComma(size_t n) { pragma(inline, false); @@ -148,7 +141,9 @@ pure @safe: void put(scope const(char)[] val) return scope { - append(val); + if (mute) + return; + dst.append(val); } @@ -173,7 +168,7 @@ pure @safe: { if ( val.length ) { - append( " " ); + put(" "); put( val ); } } From 14b22a5231aa83681e78cbc4210d5f3b05aec5f2 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 12:58:33 +0100 Subject: [PATCH 012/301] core.demangle: Replace len with Buffer.length/opDollar --- runtime/druntime/src/core/demangle.d | 84 +++++++++++++++------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 55e7a1dad30..4366d07ce90 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -74,7 +74,6 @@ pure @safe: const(char)[] buf = null; Buffer dst; - private @property size_t len () const @safe pure nothrow @nogc { return dst.len; } size_t pos = 0; size_t brp = 0; // current back reference pos AddType addType = AddType.yes; @@ -178,7 +177,7 @@ pure @safe: { debug(trace) printf( "silent+\n" ); debug(trace) scope(success) printf( "silent-\n" ); - auto n = len; dg(); dst.len = n; + auto n = dst.length; dg(); dst.len = n; } @@ -706,7 +705,7 @@ pure @safe: debug(trace) printf( "parseType+\n" ); debug(trace) scope(success) printf( "parseType-\n" ); - auto beg = len; + auto beg = dst.length; auto t = front; char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe @@ -738,19 +737,19 @@ pure @safe: put( "shared(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'x': // Const (x Type) popFront(); put( "const(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'y': // Immutable (y Type) popFront(); put( "immutable(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'N': popFront(); switch ( front ) @@ -758,20 +757,20 @@ pure @safe: case 'n': // Noreturn popFront(); put("noreturn"); - return dst[beg .. len]; + return dst[beg .. $]; case 'g': // Wild (Ng Type) popFront(); // TODO: Anything needed here? put( "inout(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'h': // TypeVector (Nh Type) popFront(); put( "__vector(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); assert( 0 ); @@ -780,7 +779,7 @@ pure @safe: popFront(); parseType(); put( "[]" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'G': // TypeStaticArray (G Number Type) popFront(); auto num = sliceNumber(); @@ -788,7 +787,7 @@ pure @safe: put( '[' ); put( num ); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'H': // TypeAssocArray (H Type Type) popFront(); // skip t1 @@ -797,12 +796,12 @@ pure @safe: put( '[' ); shift(tx); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'P': // TypePointer (P Type) popFront(); parseType(); put( '*' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction return parseTypeFunction(); case 'C': // TypeClass (C LName) @@ -811,7 +810,7 @@ pure @safe: case 'T': // TypeTypedef (T LName) popFront(); parseQualifiedName(); - return dst[beg .. len]; + return dst[beg .. $]; case 'D': // TypeDelegate (D TypeFunction) popFront(); auto modifiers = parseModifier(); @@ -828,15 +827,15 @@ pure @safe: put(str); } } - return dst[beg .. len]; + return dst[beg .. $]; case 'n': // TypeNone (n) popFront(); // TODO: Anything needed here? - return dst[beg .. len]; + return dst[beg .. $]; case 'B': // TypeTuple (B Number Arguments) popFront(); // TODO: Handle this. - return dst[beg .. len]; + return dst[beg .. $]; case 'Z': // Internal symbol // This 'type' is used for untyped internal symbols, i.e.: // __array @@ -846,13 +845,13 @@ pure @safe: // __Interface // __ModuleInfo popFront(); - return dst[beg .. len]; + return dst[beg .. $]; default: if (t >= 'a' && t <= 'w') { popFront(); put( primitives[cast(size_t)(t - 'a')] ); - return dst[beg .. len]; + return dst[beg .. $]; } else if (t == 'z') { @@ -862,11 +861,11 @@ pure @safe: case 'i': popFront(); put( "cent" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'k': popFront(); put( "ucent" ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); assert( 0 ); @@ -1222,12 +1221,12 @@ pure @safe: { debug(trace) printf( "parseTypeFunction+\n" ); debug(trace) scope(success) printf( "parseTypeFunction-\n" ); - auto beg = len; + auto beg = dst.length; parseCallConvention(); auto attributes = parseFuncAttr(); - auto argbeg = len; + auto argbeg = dst.length; put(IsDelegate.yes == isdg ? "delegate" : "function"); put( '(' ); parseFuncArguments(); @@ -1246,13 +1245,13 @@ pure @safe: // Write it in order, then shift it back to 'code order' // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe' { - auto retbeg = len; + auto retbeg = dst.length; parseType(); put(' '); shift(dst[argbeg .. retbeg]); } - return dst[beg..len]; + return dst[beg .. $]; } static bool isCallConvention( char ch ) @@ -1574,7 +1573,7 @@ pure @safe: if ( mayBeMangledNameArg() ) { - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; try @@ -1597,7 +1596,7 @@ pure @safe: // try all possible pairs of numbers auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName pos--; - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; while ( qlen > 0 ) @@ -1733,7 +1732,7 @@ pure @safe: case '0': .. case '9': if ( mayBeTemplateInstanceName() ) { - auto t = len; + auto t = dst.length; try { @@ -1762,7 +1761,7 @@ pure @safe: { // try to demangle a function, in case we are pointing to some function local auto prevpos = pos; - auto prevlen = len; + auto prevlen = dst.length; auto prevbrp = brp; try @@ -1790,7 +1789,7 @@ pure @safe: put(str); put(' '); } - attr = dst[prevlen .. len]; + attr = dst[prevlen .. $]; } put( '(' ); @@ -1818,7 +1817,7 @@ pure @safe: { debug(trace) printf( "parseQualifiedName+\n" ); debug(trace) scope(success) printf( "parseQualifiedName-\n" ); - size_t beg = len; + size_t beg = dst.length; size_t n = 0; do @@ -1829,7 +1828,7 @@ pure @safe: parseFunctionTypeNoReturn(); } while ( isSymbolNameFront() ); - return dst[beg .. len]; + return dst[beg .. $]; } @@ -1850,17 +1849,17 @@ pure @safe: match( 'D' ); do { - size_t beg = len; - size_t nameEnd = len; + size_t beg = dst.length; + size_t nameEnd = dst.length; char[] attr; do { if ( attr ) dst.remove(attr); // dump attributes of parent symbols - if ( beg != len ) + if (beg != dst.length) put( '.' ); parseSymbolName(); - nameEnd = len; + nameEnd = dst.length; attr = parseFunctionTypeNoReturn( displayType ); } while ( isSymbolNameFront() ); @@ -1868,7 +1867,7 @@ pure @safe: if ( displayType ) { attr = shift( attr ); - nameEnd = len - attr.length; // name includes function arguments + nameEnd = dst.length - attr.length; // name includes function arguments } name = dst[beg .. nameEnd]; @@ -1876,7 +1875,7 @@ pure @safe: if ( 'M' == front ) popFront(); // has 'this' pointer - auto lastlen = len; + auto lastlen = dst.length; auto type = parseType(); if ( displayType ) { @@ -1921,7 +1920,7 @@ pure @safe: { debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr ); FUNC(); - return dst[0 .. len]; + return dst[0 .. $]; } catch ( OverflowException e ) { @@ -2886,6 +2885,13 @@ private struct Buffer private char[] dst; private size_t len; + public alias opDollar = len; + + public size_t length () const scope @safe pure nothrow @nogc + { + return this.len; + } + public inout(char)[] opSlice (size_t from, size_t to) inout return scope @safe pure nothrow @nogc { From d39e92909f8781c884ff47d3e1b4a4e246d24d4e Mon Sep 17 00:00:00 2001 From: Geod24 Date: Thu, 2 Mar 2023 13:00:34 +0100 Subject: [PATCH 013/301] core.demangler: Return superfluous assert(0) There is a call to a noreturn function just before --- runtime/druntime/src/core/demangle.d | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 4366d07ce90..e1b81dc29c3 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -773,7 +773,6 @@ pure @safe: return dst[beg .. $]; default: error(); - assert( 0 ); } case 'A': // TypeArray (A Type) popFront(); @@ -868,7 +867,6 @@ pure @safe: return dst[beg .. $]; default: error(); - assert( 0 ); } } error(); From 00ae887c83d48ae65b7f662b4a4d26d9fc1ef271 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 3 Mar 2023 07:01:41 -0800 Subject: [PATCH 014/301] remove template bloat for CTFE switch string default (dlang/dmd!14941) --- dmd/statementsem.d | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index f713456e743..d080e3f90b6 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -2285,6 +2285,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } + else if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + { + // something for the interpreter to deal with + s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0)); + } else if (global.params.useSwitchError == CHECKENABLE.on && global.params.checkAction != CHECKACTION.halt) { From 645cce40cdeab9a5a43aa1fff4376aef661355ff Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 3 Mar 2023 07:02:36 -0800 Subject: [PATCH 015/301] do not emit switch templates in CTFE (dlang/dmd!14942) --- dmd/statementsem.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index d080e3f90b6..45ebd589a8b 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -2339,7 +2339,8 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } - if (!ss.condition.type.isString()) + if (!ss.condition.type.isString() || + sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) { sc.pop(); result = ss; From 386dd8b5ff9ad94150ae3e9d54b8daa4f60f76a9 Mon Sep 17 00:00:00 2001 From: lucica28 <57060141+lucica28@users.noreply.github.com> Date: Fri, 3 Mar 2023 17:12:49 +0200 Subject: [PATCH 016/301] add location for storage class declaration (dlang/dmd!14720) --- dmd/attrib.d | 12 ++++++++++++ dmd/parse.d | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dmd/attrib.d b/dmd/attrib.d index 8942e50a299..c08382cbd04 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -62,6 +62,12 @@ extern (C++) abstract class AttribDeclaration : Dsymbol this.decl = decl; } + extern (D) this(const ref Loc loc, Dsymbols* decl) + { + super(loc, null); + this.decl = decl; + } + extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) { super(loc, ident); @@ -228,6 +234,12 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration this.stc = stc; } + extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl) + { + super(loc, decl); + this.stc = stc; + } + override StorageClassDeclaration syntaxCopy(Dsymbol s) { assert(!s); diff --git a/dmd/parse.d b/dmd/parse.d index 6f7bd594f5a..f396177b5c3 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -335,6 +335,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer linkage = linksave; Loc startloc; + Loc scdLoc; switch (token.value) { @@ -696,6 +697,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } Lstc: pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc); + scdLoc = token.loc; nextToken(); Lautodecl: @@ -748,7 +750,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto stc2 = getStorageClass!AST(pAttrs); if (stc2 != STC.undefined_) { - s = new AST.StorageClassDeclaration(stc2, a); + s = new AST.StorageClassDeclaration(scdLoc, stc2, a); } if (pAttrs.udas) { From 8d564c65728c5afe2aada102524332bb85e75919 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 3 Mar 2023 07:40:24 -0800 Subject: [PATCH 017/301] do not lower if using CTFE (dlang/dmd!14940) --- dmd/expressionsem.d | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index d186abc0552..3c22683b5d4 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3797,8 +3797,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = id.expressionSemantic(sc); return; } - else if (!exp.onstack && !exp.type.isscope()) + else if (!(sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) && // interpreter can handle these + !exp.onstack && !exp.type.isscope()) // these won't use the GC { + /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` + * or `_d_newclassTTrace` + */ auto hook = global.params.tracegc ? Id._d_newclassTTrace : Id._d_newclassT; if (!verifyHookExist(exp.loc, *sc, hook, "new class")) return setError(); From bef3e41cea7af25a1b381eedd0c752cd55fd46d9 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Fri, 3 Mar 2023 15:58:22 +0100 Subject: [PATCH 018/301] Remove uses of in on extern(C) functions in druntime It will error out if we try to compile druntime with `-preview=in`, because we can't use `in` on non `extern(D)` / `extern(C++)` functions. --- runtime/druntime/src/rt/aApply.d | 24 ++++++++++++------------ runtime/druntime/src/rt/aApplyR.d | 24 ++++++++++++------------ runtime/druntime/src/rt/lifetime.d | 6 +++--- runtime/druntime/src/rt/tracegc.d | 4 ++-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/runtime/druntime/src/rt/aApply.d b/runtime/druntime/src/rt/aApply.d index 5d5ddb34740..c59d9dc1234 100644 --- a/runtime/druntime/src/rt/aApply.d +++ b/runtime/druntime/src/rt/aApply.d @@ -71,7 +71,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplycd1(in char[] aa, dg_t dg) +extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -132,7 +132,7 @@ unittest } /// ditto -extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -193,7 +193,7 @@ unittest } /// ditto -extern (C) int _aApplycw1(in char[] aa, dg_t dg) +extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -267,7 +267,7 @@ unittest } /// ditto -extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -347,7 +347,7 @@ unittest } /// ditto -extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -423,7 +423,7 @@ unittest } /// ditto -extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -508,7 +508,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyXXX that include a loop index. */ -extern (C) int _aApplycd2(in char[] aa, dg2_t dg) +extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -576,7 +576,7 @@ unittest } /// ditto -extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -644,7 +644,7 @@ unittest } /// ditto -extern (C) int _aApplycw2(in char[] aa, dg2_t dg) +extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -723,7 +723,7 @@ unittest } /// ditto -extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -808,7 +808,7 @@ unittest } /// ditto -extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -888,7 +888,7 @@ unittest } /// ditto -extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplydw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/aApplyR.d b/runtime/druntime/src/rt/aApplyR.d index ce3bb9eaf70..560025c636d 100644 --- a/runtime/druntime/src/rt/aApplyR.d +++ b/runtime/druntime/src/rt/aApplyR.d @@ -34,7 +34,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplyRcd1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length); @@ -107,7 +107,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length); @@ -170,7 +170,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length); @@ -256,7 +256,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length); @@ -340,7 +340,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length); @@ -418,7 +418,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length); @@ -502,7 +502,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyRXXX that include a loop index. */ -extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg) { int result; size_t i; size_t len = aa.length; @@ -578,7 +578,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length); @@ -643,7 +643,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length); @@ -731,7 +731,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length); @@ -817,7 +817,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length); @@ -896,7 +896,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/lifetime.d b/runtime/druntime/src/rt/lifetime.d index f2515c3c677..c5ca8f46973 100644 --- a/runtime/druntime/src/rt/lifetime.d +++ b/runtime/druntime/src/rt/lifetime.d @@ -1197,7 +1197,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak } /// ditto -extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1206,7 +1206,7 @@ extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak } /// Same as above, for item with non-zero initializer. -extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemiT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1286,7 +1286,7 @@ extern (C) CollectHandler rt_getCollectHandler() /** * */ -extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow +extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow { if (attr & BlkAttr.STRUCTFINAL) { diff --git a/runtime/druntime/src/rt/tracegc.d b/runtime/druntime/src/rt/tracegc.d index 29b61468056..c89a358742c 100644 --- a/runtime/druntime/src/rt/tracegc.d +++ b/runtime/druntime/src/rt/tracegc.d @@ -22,8 +22,8 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length); extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length); extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims); extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims); -extern (C) void* _d_newitemT(in TypeInfo ti); -extern (C) void* _d_newitemiT(in TypeInfo ti); +extern (C) void* _d_newitemT(const TypeInfo ti); +extern (C) void* _d_newitemiT(const TypeInfo ti); extern (C) void _d_callfinalizer(void* p); extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); From e550f447f60161423683da6d16a721e47003698a Mon Sep 17 00:00:00 2001 From: Geod24 Date: Mon, 26 Sep 2022 11:29:45 +0200 Subject: [PATCH 019/301] Trivial: Correct Module.isCoreModule documentation --- dmd/dmodule.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dmd/dmodule.d b/dmd/dmodule.d index a5f7cd321da..28e2efff575 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -1229,8 +1229,7 @@ extern (C++) final class Module : Package return this.importedFrom == this; } - // true if the module source file is directly - // listed in command line. + /// Returns: Whether this module is in the `core` package and has name `ident` bool isCoreModule(Identifier ident) nothrow { return this.ident == ident && parent && parent.ident == Id.core && !parent.parent; From 857b845a7d468c9540771ee40f62470c82a3e8d5 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 5 Mar 2023 00:25:56 -0800 Subject: [PATCH 020/301] add CompileEnv to lexer --- dmd/cparse.d | 4 +- dmd/dmodule.d | 4 +- dmd/doc.d | 2 +- dmd/dsymbolsem.d | 2 +- dmd/dtoh.d | 4 +- dmd/errors.d | 2 +- dmd/expressionsem.d | 2 +- dmd/frontend.h | 32 +++++- dmd/globals.d | 47 ++++++-- dmd/globals.h | 11 +- dmd/iasmgcc.d | 6 +- dmd/json.d | 10 +- dmd/lexer.d | 119 ++++++++------------- dmd/parse.d | 11 +- dmd/statementsem.d | 2 +- dmd/typesem.d | 2 +- tests/dmd/unit/lexer/diagnostic_reporter.d | 2 +- tests/dmd/unit/lexer/lexer_dmdlib.d | 8 +- tests/dmd/unit/lexer/location_offset.d | 36 +++---- 19 files changed, 171 insertions(+), 135 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index a18f810372d..3b990988e88 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -71,9 +71,9 @@ final class CParser(AST) : Parser!AST extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, - const ref TARGET target, OutBuffer* defines) scope + const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope { - super(_module, input, doDocComment, errorSink); + super(_module, input, doDocComment, errorSink, compileEnv); //printf("CParser.this()\n"); mod = _module; diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 28e2efff575..c911ec4976b 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -767,7 +767,7 @@ extern (C++) final class Module : Package { filetype = FileType.c; - scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines); + scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv); p.nextToken(); checkCompiledImport(); members = p.parseModule(); @@ -776,7 +776,7 @@ extern (C++) final class Module : Package } else { - scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink); + scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv); p.nextToken(); p.parseModuleDeclaration(); md = p.md; diff --git a/dmd/doc.d b/dmd/doc.d index 88e8996ae3a..7674f775e3e 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -5184,7 +5184,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, - global.vendor, global.versionNumber()); + &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("highlightCode2('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 61811fe5449..5da2375fa55 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1933,7 +1933,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink); + scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink, &global.compileEnv); p.nextToken(); auto d = p.parseDeclDefs(0); diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 7c3ff4bccc2..f00b8dba86c 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -84,9 +84,9 @@ extern(C++) void genCppHdrFiles(ref Modules ms) m.accept(v); if (global.params.cxxhdr.fullOutput) - buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber()); + buf.printf("// Automatically generated by %s Compiler v%d", global.compileEnv.vendor.ptr, global.versionNumber()); else - buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr); + buf.printf("// Automatically generated by %s Compiler", global.compileEnv.vendor.ptr); buf.writenl(); buf.writenl(); diff --git a/dmd/errors.d b/dmd/errors.d index b89377237e2..6d6bd455666 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -823,7 +823,7 @@ private void colorHighlightCode(ref OutBuffer buf) if (!errorSinkNull) errorSinkNull = new ErrorSinkNull; - scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, errorSinkNull, global.vendor, global.versionNumber()); + scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, errorSinkNull, &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 3c22683b5d4..6a2c54f4dd3 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6106,7 +6106,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink); + scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink, &global.compileEnv); p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/frontend.h b/dmd/frontend.h index 5bf43bbef70..ff5294a6cbb 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3363,6 +3363,30 @@ struct Param final {} }; +struct CompileEnv final +{ + uint32_t versionNumber; + _d_dynamicArray< const char > date; + _d_dynamicArray< const char > time; + _d_dynamicArray< const char > vendor; + _d_dynamicArray< const char > timestamp; + CompileEnv() : + versionNumber(), + date(), + time(), + vendor(), + timestamp() + { + } + CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}) : + versionNumber(versionNumber), + date(date), + time(time), + vendor(vendor), + timestamp(timestamp) + {} +}; + struct Global final { _d_dynamicArray< const char > inifilename; @@ -3370,7 +3394,7 @@ struct Global final _d_dynamicArray< const char > written; Array* path; Array* filePath; - _d_dynamicArray< const char > vendor; + CompileEnv compileEnv; Param params; uint32_t errors; uint32_t warnings; @@ -3399,7 +3423,7 @@ struct Global final written(24, "written by Walter Bright"), path(), filePath(), - vendor(), + compileEnv(), params(), errors(), warnings(), @@ -3416,13 +3440,13 @@ struct Global final preprocess() { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : inifilename(inifilename), copyright(copyright), written(written), path(path), filePath(filePath), - vendor(vendor), + compileEnv(compileEnv), params(params), errors(errors), warnings(warnings), diff --git a/dmd/globals.d b/dmd/globals.d index 1919d9ae0ce..272f08607f8 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -11,7 +11,10 @@ module dmd.globals; +import core.stdc.stdio; import core.stdc.stdint; +import core.stdc.string; + import dmd.root.array; import dmd.root.filename; import dmd.common.outbuffer; @@ -20,6 +23,8 @@ import dmd.errors; import dmd.file_manager; import dmd.identifier; import dmd.location; +import dmd.lexer : CompileEnv; +import dmd.utils; /// Defines a setting for how compiler warnings and deprecations are handled enum DiagnosticReporting : ubyte @@ -270,9 +275,7 @@ extern (C++) struct Global Array!(const(char)*)* filePath; /// Array of char*'s which form the file import lookup path private enum string _version = import("VERSION"); - private enum uint _versionNumber = parseVersionNumber(_version); - - const(char)[] vendor; /// Compiler backend name + CompileEnv compileEnv; Param params; /// command line parameters uint errors; /// number of errors reported so far @@ -350,12 +353,12 @@ extern (C++) struct Global extern (C++) void _init() { - global.errorSink = new ErrorSinkCompiler; + errorSink = new ErrorSinkCompiler; this.fileManager = new FileManager(); version (MARS) { - vendor = "Digital Mars D"; + compileEnv.vendor = "Digital Mars D"; // -color=auto is the default value import dmd.console : detectTerminal; @@ -363,8 +366,38 @@ extern (C++) struct Global } else version (IN_GCC) { - vendor = "GNU D"; + compileEnv.vendor = "GNU D"; + } + compileEnv.versionNumber = parseVersionNumber(_version); + + /* Initialize date, time, and timestamp + */ + import core.stdc.time; + import core.stdc.stdlib : getenv; + + time_t ct; + // https://issues.dlang.org/show_bug.cgi?id=20444 + if (auto p = getenv("SOURCE_DATE_EPOCH")) + { + if (!ct.parseDigits(p[0 .. strlen(p)])) + errorSink.error(Loc.initial, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); } + else + core.stdc.time.time(&ct); + const p = ctime(&ct); + assert(p); + + __gshared char[11 + 1] date = 0; // put in BSS segment + __gshared char[8 + 1] time = 0; + __gshared char[24 + 1] timestamp = 0; + + const dsz = snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); + const tsz = snprintf(&time[0], time.length, "%.8s", p + 11); + const tssz = snprintf(×tamp[0], timestamp.length, "%.24s", p); + assert(dsz > 0 && tsz > 0 && tssz > 0); + compileEnv.time = time[0 .. tsz]; + compileEnv.date = date[0 .. dsz]; + compileEnv.timestamp = timestamp[0 .. tssz]; } /** @@ -415,7 +448,7 @@ extern (C++) struct Global */ extern(C++) uint versionNumber() { - return _versionNumber; + return compileEnv.versionNumber; } /** diff --git a/dmd/globals.h b/dmd/globals.h index 84fbec6977d..6266ff604e3 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -252,6 +252,15 @@ const DString hdr_ext = "di"; // for D 'header' import files const DString json_ext = "json"; // for JSON files const DString map_ext = "map"; // for .map files +struct CompileEnv +{ + uint32_t versionNumber; + DString date; + DString time; + DString vendor; + DString timestamp; +}; + struct Global { DString inifilename; @@ -261,7 +270,7 @@ struct Global Array *path; // Array of char*'s which form the import lookup path Array *filePath; // Array of char*'s which form the file import lookup path - DString vendor; // Compiler backend name + CompileEnv compileEnv; Param params; unsigned errors; // number of errors reported so far diff --git a/dmd/iasmgcc.d b/dmd/iasmgcc.d index f8c88ab536e..1ff5839b11e 100644 --- a/dmd/iasmgcc.d +++ b/dmd/iasmgcc.d @@ -302,7 +302,7 @@ Ldone: extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink); + scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv); // Make a safe copy of the token list before parsing. Token *toklist = null; @@ -410,7 +410,7 @@ unittest { const errors = global.errors; scope gas = new GccAsmStatement(Loc.initial, tokens); - scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink); + scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv); p.token = *tokens; p.parseGccAsm(gas); return global.errors - errors; @@ -420,7 +420,7 @@ unittest static void parseAsm(string input, bool expectError) { // Generate tokens from input test. - scope p = new Parser!ASTCodegen(null, input, false, global.errorSink); + scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv); p.nextToken(); Token* toklist = null; diff --git a/dmd/json.d b/dmd/json.d index 2af7faec354..dcf53b8f547 100644 --- a/dmd/json.d +++ b/dmd/json.d @@ -833,7 +833,7 @@ public: { import dmd.target : target; objectStart(); - requiredProperty("vendor", global.vendor); + requiredProperty("vendor", global.compileEnv.vendor); requiredProperty("version", global.versionString()); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); @@ -1070,13 +1070,13 @@ Determines and returns the compiler interface which is one of `dmd`, `ldc`, */ private extern(D) string determineCompilerInterface() { - if (global.vendor == "Digital Mars D") + if (global.compileEnv.vendor == "Digital Mars D") return "dmd"; - if (global.vendor == "LDC") + if (global.compileEnv.vendor == "LDC") return "ldc"; - if (global.vendor == "GNU D") + if (global.compileEnv.vendor == "GNU D") return "gdc"; - if (global.vendor == "SDC") + if (global.compileEnv.vendor == "SDC") return "sdc"; return null; } diff --git a/dmd/lexer.d b/dmd/lexer.d index c9ed35ffa6d..441b67dce9e 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -14,12 +14,8 @@ module dmd.lexer; import core.stdc.ctype; -import core.stdc.errno; -import core.stdc.stdarg; import core.stdc.stdio; -import core.stdc.stdlib : getenv; import core.stdc.string; -import core.stdc.time; import dmd.entity; import dmd.errorsink; @@ -31,10 +27,8 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; -import dmd.root.string; import dmd.root.utf; import dmd.tokens; -import dmd.utils; nothrow: @@ -43,6 +37,18 @@ version (DMDLIB) version = LocOffset; } +/*********************************************************** + * Values to use for various magic identifiers + */ +struct CompileEnv +{ + uint versionNumber; /// __VERSION__ + const(char)[] date; /// __DATE__ + const(char)[] time; /// __TIME__ + const(char)[] vendor; /// __VENDOR__ + const(char)[] timestamp; /// __TIMESTAMP__ +} + /*********************************************************** */ class Lexer @@ -87,8 +93,8 @@ class Lexer int lastDocLine; // last line of previous doc comment Token* tokenFreelist; - uint versionNumber; - const(char)[] vendor; + + CompileEnv compileEnv; // environment } nothrow: @@ -105,13 +111,12 @@ class Lexer * doDocComment = handle documentation comments * commentToken = comments become TOK.comment's * errorSink = where error messages go, must not be null - * vendor = name of the vendor - * versionNumber = version of the caller + * compileEnv = version, vendor, date, time, etc. */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, ErrorSink errorSink, - const(char)[] vendor = "DLF", uint versionNumber = 1) pure scope + const CompileEnv* compileEnv) pure scope { scanloc = Loc(filename, 1, 1); // debug printf("Lexer::Lexer(%p)\n", base); @@ -128,8 +133,13 @@ class Lexer this.lastDocLine = 0; this.eSink = errorSink; assert(errorSink); - this.versionNumber = versionNumber; - this.vendor = vendor; + if (compileEnv) + this.compileEnv = *compileEnv; + else + { + this.compileEnv.versionNumber = 1; + this.compileEnv.vendor = "DLF"; + } //initKeywords(); /* If first line starts with '#!', ignore the line */ @@ -169,10 +179,10 @@ class Lexer */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, bool whitespaceToken, - ErrorSink errorSink + ErrorSink errorSink, const CompileEnv* compileEnv = null ) { - this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink); + this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink, compileEnv); this.whitespaceToken = whitespaceToken; } @@ -571,36 +581,26 @@ class Lexer else if (*t.ptr == '_') // if special identifier token { - // Lazy initialization - TimeStampInfo.initialize(t.loc, eSink); - - if (id == Id.DATE) + void toToken(const(char)[] s) { - t.ustring = TimeStampInfo.date.ptr; - goto Lstr; + t.value = TOK.string_; + t.ustring = s.ptr; + t.len = cast(uint)s.length; + t.postfix = 0; } + + if (id == Id.DATE) + toToken(compileEnv.date); else if (id == Id.TIME) - { - t.ustring = TimeStampInfo.time.ptr; - goto Lstr; - } + toToken(compileEnv.time); else if (id == Id.VENDOR) - { - t.ustring = vendor.xarraydup.ptr; - goto Lstr; - } + toToken(compileEnv.vendor); else if (id == Id.TIMESTAMP) - { - t.ustring = TimeStampInfo.timestamp.ptr; - Lstr: - t.value = TOK.string_; - t.postfix = 0; - t.len = cast(uint)strlen(t.ustring); - } + toToken(compileEnv.timestamp); else if (id == Id.VERSIONX) { t.value = TOK.int64Literal; - t.unsvalue = versionNumber; + t.unsvalue = compileEnv.versionNumber; } else if (id == Id.EOFX) { @@ -3019,7 +3019,10 @@ class Lexer auto dc = (lineComment && anyToken) ? &t.lineComment : &t.blockComment; // Combine with previous doc comment, if any if (*dc) - *dc = combineComments(*dc, buf[], newParagraph).toDString(); + { + auto p = combineComments(*dc, buf[], newParagraph); + *dc = p ? p[0 .. strlen(p)] : null; + } else *dc = buf.extractSlice(true); } @@ -3067,42 +3070,6 @@ class Lexer private: -/// Support for `__DATE__`, `__TIME__`, and `__TIMESTAMP__` -private struct TimeStampInfo -{ - private __gshared bool initdone = false; - - // Note: Those properties need to be guarded by a call to `init` - // The API isn't safe, and quite brittle, but it was left this way - // over performance concerns. - // This is currently only called once, from the lexer. - __gshared char[11 + 1] date; - __gshared char[8 + 1] time; - __gshared char[24 + 1] timestamp; - - public static void initialize(const ref Loc loc, ErrorSink eSink) nothrow - { - if (initdone) - return; - - initdone = true; - time_t ct; - // https://issues.dlang.org/show_bug.cgi?id=20444 - if (auto p = getenv("SOURCE_DATE_EPOCH")) - { - if (!ct.parseDigits(p.toDString())) - eSink.error(loc, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); - } - else - .time(&ct); - const p = ctime(&ct); - assert(p); - snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); - snprintf(&time[0], time.length, "%.8s", p + 11); - snprintf(×tamp[0], timestamp.length, "%.24s", p); - } -} - private enum LS = 0x2028; // UTF line separator private enum PS = 0x2029; // UTF paragraph separator @@ -3352,7 +3319,7 @@ unittest */ string text = "int"; // We rely on the implicit null-terminator ErrorSink errorSink = new ErrorSinkStderr; - scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink); + scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink, null); TOK tok; tok = lex1.nextToken(); //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32); @@ -3388,7 +3355,7 @@ unittest foreach (testcase; testcases) { - scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink); + scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink, null); TOK tok = lex2.nextToken(); size_t iterations = 1; while ((tok != TOK.endOfFile) && (iterations++ < testcase.length)) diff --git a/dmd/parse.d b/dmd/parse.d index f396177b5c3..b837c99a78c 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -30,6 +30,8 @@ import dmd.root.rootobject; import dmd.root.string; import dmd.tokens; +alias CompileEnv = dmd.lexer.CompileEnv; + /*********************************************************** */ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer @@ -53,11 +55,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * loc location in source file of mixin */ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, - ErrorSink errorSink) scope + ErrorSink errorSink, const CompileEnv* compileEnv) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, - global.vendor, global.versionNumber()); + compileEnv); //printf("Parser::Parser()\n"); scanloc = loc; @@ -78,11 +80,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer //nextToken(); // start up the scanner } - extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink) scope + extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, + const CompileEnv* compileEnv) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, - global.vendor, global.versionNumber()); + compileEnv); //printf("Parser::Parser()\n"); mod = _module; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 45ebd589a8b..7062ba511f0 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -4751,7 +4751,7 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink); + scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink, &global.compileEnv); p.nextToken(); auto a = new Statements(); diff --git a/dmd/typesem.d b/dmd/typesem.d index 965acef8d22..aed7ccbd5a8 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -4918,7 +4918,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink); + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv); p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/tests/dmd/unit/lexer/diagnostic_reporter.d b/tests/dmd/unit/lexer/diagnostic_reporter.d index 030ce6c6eca..d8f748e0a59 100644 --- a/tests/dmd/unit/lexer/diagnostic_reporter.d +++ b/tests/dmd/unit/lexer/diagnostic_reporter.d @@ -64,7 +64,7 @@ private void lexUntilEndOfFile(string code) if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink); + scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink, null); lexer.nextToken; while (lexer.nextToken != TOK.endOfFile) {} diff --git a/tests/dmd/unit/lexer/lexer_dmdlib.d b/tests/dmd/unit/lexer/lexer_dmdlib.d index 4b53bafc9ac..ea7ebb4ab82 100644 --- a/tests/dmd/unit/lexer/lexer_dmdlib.d +++ b/tests/dmd/unit/lexer/lexer_dmdlib.d @@ -170,7 +170,7 @@ unittest TOK.rightCurly, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -191,7 +191,7 @@ unittest TOK.comment, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -217,7 +217,7 @@ unittest TOK.reserved, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); TOK[] result; @@ -266,7 +266,7 @@ unittest foreach (codeNum, code; codes) { auto fileName = text("file", codeNum, '\0'); - Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler); + Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler, null); // Generate the errors foreach(unused; lexer){} } diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index c52de839dbe..50c2ad8e2f6 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -17,7 +17,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -29,7 +29,7 @@ unittest { enum code = "ignored_token token"; - scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -41,7 +41,7 @@ unittest { enum code = "token1 token2 3"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -55,7 +55,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -68,7 +68,7 @@ unittest { enum code = "/* comment */"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -81,7 +81,7 @@ unittest { enum code = "// comment"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -94,7 +94,7 @@ unittest { enum code = "/+ comment +/"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -107,7 +107,7 @@ unittest { enum code = "/* comment */ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -119,7 +119,7 @@ unittest { enum code = "// comment\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -131,7 +131,7 @@ unittest { enum code = "/+ comment +/ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -143,7 +143,7 @@ unittest { enum code = "line\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -156,7 +156,7 @@ unittest { enum code = "line\r\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -169,7 +169,7 @@ unittest { enum code = "line\rtoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -182,7 +182,7 @@ unittest { enum code = "'🍺'"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -194,7 +194,7 @@ unittest { enum code = `"🍺🍺"`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -206,7 +206,7 @@ unittest { enum code = "'🍺' token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -219,7 +219,7 @@ unittest { enum code = `"🍺🍺" token`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -558,7 +558,7 @@ static foreach (tok; __traits(allMembers, TOK)) { const newCode = "first_token " ~ tests[tok].code; - scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; From 75d72d69108ecfbdf9fb8286cdc0dbe87b848ef9 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 6 Mar 2023 17:47:15 -0800 Subject: [PATCH 021/301] cparse.d: remove errors.d and globals.d dependencies --- dmd/astenums.d | 19 +++++++++++++++++++ dmd/cparse.d | 8 ++------ dmd/frontend.h | 50 +++++++++++++++++++++++++------------------------- dmd/globals.d | 24 ------------------------ 4 files changed, 46 insertions(+), 55 deletions(-) diff --git a/dmd/astenums.d b/dmd/astenums.d index 60ca4d015cc..97658d960d7 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -439,3 +439,22 @@ enum FileType : ubyte ddoc, /// Ddoc documentation file (.dd) c, /// C source file } + +extern (C++) struct structalign_t +{ + private: + ushort value = 0; // unknown + enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does + bool pack; // use #pragma pack semantics + + public: + pure @safe @nogc nothrow: + bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } + void setDefault() { value = STRUCTALIGN_DEFAULT; } + bool isUnknown() const { return value == 0; } // value is not set + void setUnknown() { value = 0; } + void set(uint value) { this.value = cast(ushort)value; } + uint get() const { return value; } + bool isPack() const { return pack; } + void setPack(bool pack) { this.pack = pack; } +} diff --git a/dmd/cparse.d b/dmd/cparse.d index 3b990988e88..a301f96be4c 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -14,22 +14,18 @@ module dmd.cparse; import core.stdc.stdio; -import core.stdc.string; +import core.stdc.string : memcpy; + import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; import dmd.parse; -import dmd.errors; import dmd.root.array; -import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; -import dmd.root.rootobject; -import dmd.root.string; import dmd.tokens; /*********************************************************** diff --git a/dmd/frontend.h b/dmd/frontend.h index ff5294a6cbb..5a04e4edeba 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -694,31 +694,6 @@ enum class LINK : uint8_t system = 6u, }; -struct structalign_t final -{ -private: - uint16_t value; - bool pack; -public: - bool isDefault() const; - void setDefault(); - bool isUnknown() const; - void setUnknown(); - void set(uint32_t value); - uint32_t get() const; - bool isPack() const; - void setPack(bool pack); - structalign_t() : - value(0u), - pack() - { - } - structalign_t(uint16_t value, bool pack = false) : - value(value), - pack(pack) - {} -}; - enum class BUILTIN : uint8_t { unknown = 255u, @@ -5427,6 +5402,31 @@ extern TypeTuple* toArgTypes_aarch64(Type* t); extern bool isHFVA(Type* t, int32_t maxNumElements = 4, Type** rewriteType = nullptr); +struct structalign_t final +{ +private: + uint16_t value; + bool pack; +public: + bool isDefault() const; + void setDefault(); + bool isUnknown() const; + void setUnknown(); + void set(uint32_t value); + uint32_t get() const; + bool isPack() const; + void setPack(bool pack); + structalign_t() : + value(0u), + pack() + { + } + structalign_t(uint16_t value, bool pack = false) : + value(value), + pack(pack) + {} +}; + class AttribDeclaration : public Dsymbol { public: diff --git a/dmd/globals.d b/dmd/globals.d index 272f08607f8..fde1667556c 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -227,30 +227,6 @@ extern (C++) struct Param const(char)[] mapfile; } -extern (C++) struct structalign_t -{ - private: - ushort value = 0; // unknown - enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does - bool pack; // use #pragma pack semantics - - public: - pure @safe @nogc nothrow: - bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } - void setDefault() { value = STRUCTALIGN_DEFAULT; } - bool isUnknown() const { return value == 0; } // value is not set - void setUnknown() { value = 0; } - void set(uint value) { this.value = cast(ushort)value; } - uint get() const { return value; } - bool isPack() const { return pack; } - void setPack(bool pack) { this.pack = pack; } -} -//alias structalign_t = uint; - -// magic value means "match whatever the underlying C compiler does" -// other values are all powers of 2 -//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0); - enum mars_ext = "d"; // for D source files enum doc_ext = "html"; // for Ddoc generated files enum ddoc_ext = "ddoc"; // for Ddoc macro include files From 47cee60a7bbddc89f7f05417ddf75041c729c5fc Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 7 Mar 2023 01:35:17 -0800 Subject: [PATCH 022/301] ArrayCtor: do not lower if in CTFE (dlang/dmd!14961) --- dmd/expressionsem.d | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 6a2c54f4dd3..6c730c529b6 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -10050,6 +10050,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } } + + if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // interpreter can handle these + return setResult(res); + const lowerToArrayCtor = ( (rhsType.ty == Tarray && !rhs.isArrayLiteralExp) || (rhsType.ty == Tsarray && rhs.isLvalue) ) && From 20e0fced5019dc2da6eb60f3679601472f317122 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 7 Mar 2023 01:36:09 -0800 Subject: [PATCH 023/301] ArrayLengthExp: do not lower it in CTFE (dlang/dmd!14960) --- dmd/expressionsem.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 6c730c529b6..85444933f70 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -9697,6 +9697,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setResult(res); } + if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // if compile time creature only + { + exp.type = Type.tsize_t; + return setResult(exp); + } + // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2) Expression id = new IdentifierExp(ale.loc, Id.empty); id = new DotIdExp(ale.loc, id, Id.object); From eaef68f90231ce97418e60550fd790df0fafca13 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 7 Mar 2023 01:36:57 -0800 Subject: [PATCH 024/301] do not lower AssertExp when interpreting (dlang/dmd!14959) --- dmd/expressionsem.d | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 85444933f70..6f065e85523 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6261,7 +6261,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("AssertExp::semantic('%s')\n", exp.toChars()); } - const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on; + const ctfe = (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) != 0; + + const generateMsg = !exp.msg && + !ctfe && // let ctfe interpreter handle the error message + global.params.checkAction == CHECKACTION.context && + global.params.useAssert == CHECKENABLE.on; Expression temporariesPrefix; if (generateMsg) From be96ef622148e13676b42076bb70da21e3fca64a Mon Sep 17 00:00:00 2001 From: Mathias LANG Date: Tue, 7 Mar 2023 10:51:36 +0100 Subject: [PATCH 025/301] Deprecate 'in ref' to prepare for -preview=in (dlang/dmd!14931) This should nudge people into using '-preview=in' and clear up the remaining projects which are incompatible with it. --- dmd/parse.d | 7 +++++-- tests/dmd/compilable/interpret3.d | 4 ++-- tests/dmd/compilable/warn3882.d | 2 +- tests/dmd/fail_compilation/deprecatedinref.d | 10 ++++++++++ tests/dmd/fail_compilation/ice11626.d | 2 +- tests/dmd/runnable/imports/link11069z.d | 2 +- tests/dmd/runnable/test42.d | 2 +- tests/dmd/runnable/xtest46.d | 9 --------- 8 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 tests/dmd/fail_compilation/deprecatedinref.d diff --git a/dmd/parse.d b/dmd/parse.d index b837c99a78c..0496b4c3854 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -1224,8 +1224,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return orig | added; } - const Redundant = (STC.const_ | STC.scope_ | - (global.params.previewIn ? STC.ref_ : 0)); + const Redundant = (STC.const_ | STC.scope_ | STC.ref_); orig |= added; if ((orig & STC.in_) && (added & Redundant)) @@ -1237,6 +1236,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("attribute `%s` is redundant with previously-applied `in`", (orig & STC.scope_) ? "scope".ptr : "ref".ptr); } + else if (added & STC.ref_) + deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead"); return orig; @@ -1253,6 +1254,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`", stc_str, stc_str); } + else if (orig & STC.ref_) + deprecation("using `ref in` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`"); return orig; diff --git a/tests/dmd/compilable/interpret3.d b/tests/dmd/compilable/interpret3.d index 2c9a84eddfb..14142630454 100644 --- a/tests/dmd/compilable/interpret3.d +++ b/tests/dmd/compilable/interpret3.d @@ -7208,7 +7208,7 @@ struct S13630(T) { T[3] arr; - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { auto p = arr.ptr; @@ -7238,7 +7238,7 @@ struct Matrix13827(T, uint N) T[N] flat; } - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { uint k; diff --git a/tests/dmd/compilable/warn3882.d b/tests/dmd/compilable/warn3882.d index f02a87bd04b..af7aed9059d 100644 --- a/tests/dmd/compilable/warn3882.d +++ b/tests/dmd/compilable/warn3882.d @@ -64,7 +64,7 @@ void test12909() const struct Foo13899 { - int opApply(immutable int delegate(in ref int) pure nothrow dg) pure nothrow + int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow { return 1; } diff --git a/tests/dmd/fail_compilation/deprecatedinref.d b/tests/dmd/fail_compilation/deprecatedinref.d new file mode 100644 index 00000000000..20c3666bef1 --- /dev/null +++ b/tests/dmd/fail_compilation/deprecatedinref.d @@ -0,0 +1,10 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecatedinref.d(9): Deprecation: using `in ref` is deprecated, use `-preview=in` and `in` instead +fail_compilation/deprecatedinref.d(10): Deprecation: using `ref in` is deprecated, use `-preview=in` and `in` instead +--- +*/ +void foo(in ref int); +void foor(ref in int); diff --git a/tests/dmd/fail_compilation/ice11626.d b/tests/dmd/fail_compilation/ice11626.d index 5dc5d5c1e66..6d347bcdd0e 100644 --- a/tests/dmd/fail_compilation/ice11626.d +++ b/tests/dmd/fail_compilation/ice11626.d @@ -5,4 +5,4 @@ fail_compilation/ice11626.d(8): Error: undefined identifier `Bar` --- */ -void foo(in ref Bar) {} +void foo(const ref Bar) {} diff --git a/tests/dmd/runnable/imports/link11069z.d b/tests/dmd/runnable/imports/link11069z.d index 5987cb4be7b..02301b9d1d2 100644 --- a/tests/dmd/runnable/imports/link11069z.d +++ b/tests/dmd/runnable/imports/link11069z.d @@ -1,7 +1,7 @@ module imports.link11069z; struct Matrix(T, uint _M) { - int opCmp()(auto ref in Matrix b) const + int opCmp()(const auto ref Matrix b) const { return 0; } diff --git a/tests/dmd/runnable/test42.d b/tests/dmd/runnable/test42.d index 35ca859eaa8..32c162546e3 100644 --- a/tests/dmd/runnable/test42.d +++ b/tests/dmd/runnable/test42.d @@ -2105,7 +2105,7 @@ void test12725() struct Matrix12728(T, uint m, uint n = m, ubyte f = 0) { - void foo(uint r)(auto ref in Matrix12728!(T, n, r) b) + void foo(uint r)(const auto ref Matrix12728!(T, n, r) b) { } } diff --git a/tests/dmd/runnable/xtest46.d b/tests/dmd/runnable/xtest46.d index e57f52f6578..2d4f559d78d 100644 --- a/tests/dmd/runnable/xtest46.d +++ b/tests/dmd/runnable/xtest46.d @@ -6170,14 +6170,6 @@ static assert(!__traits(compiles, foo8220(typeof(0)))); // fail /***************************************************/ -void func8105(in ref int x) { } - -void test8105() -{ -} - -/***************************************************/ - template ParameterTypeTuple159(alias foo) { static if (is(typeof(foo) P == __parameters)) @@ -8300,7 +8292,6 @@ int main() test12503(); test8004(); test8064(); - test8105(); test159(); test12824(); test8283(); From 7c92720a295361dec33cd6622bcac693dc4187d6 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 8 Mar 2023 00:37:56 +0800 Subject: [PATCH 026/301] Fix Issue 13577 - More informative error message for refused immutable foreach loop (dlang/dmd!14924) --- dmd/statementsem.d | 8 ++++++-- tests/dmd/fail_compilation/fail13577.d | 28 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/fail13577.d diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 7062ba511f0..aee2931af47 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -1349,7 +1349,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor auto exp = (*exps)[i]; version (none) { - printf("[%d] p = %s %s, exp = %s %s\n", i, + printf("[%lu] p = %s %s, exp = %s %s\n", i, p.type ? p.type.toChars() : "?", p.ident.toChars(), exp.type.toChars(), exp.toChars()); } @@ -1360,7 +1360,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor if (ignoreRef) sc &= ~STC.ref_; p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2); if (!exp.implicitConvTo(p.type)) - return rangeError(); + { + fs.error("cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`", + exp.type.toChars(), p.toChars(), p.type.toChars()); + return retError(); + } auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp)); var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_; diff --git a/tests/dmd/fail_compilation/fail13577.d b/tests/dmd/fail_compilation/fail13577.d new file mode 100644 index 00000000000..79f9068c759 --- /dev/null +++ b/tests/dmd/fail_compilation/fail13577.d @@ -0,0 +1,28 @@ +// https://issues.dlang.org/show_bug.cgi?id=13577 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail13577.d(27): Error: cannot implicilty convert range element of type `int[]` to variable `x` of type `immutable(int[])` +--- +*/ + +struct Tuple(Types...) +{ + Types items; + alias items this; +} + +struct Range(T) +{ + T[] arr; + alias ElemType = Tuple!(int, T); + ElemType front() { return typeof(return)(0, arr[0]); } + bool empty() { return false; } + void popFront() {} +} + +void main() +{ + foreach (immutable i, immutable x; Range!(int[])()) {} // Error +} From 36a4fc329a51f14516f4fe1cfbb091ec3951ed55 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 8 Mar 2023 00:40:54 -0800 Subject: [PATCH 027/301] fix Issue 23727 - ImportC support imaginary real numbers (dlang/dmd!14902) * fix Issue 23727 - ImportC support imaginary real numbers * fix Issue 23727 - ImportC support imaginary real numbers --- dmd/lexer.d | 14 ++++++++++++++ dmd/mtype.d | 2 ++ tests/dmd/compilable/testcomplex.i | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/dmd/compilable/testcomplex.i diff --git a/dmd/lexer.d b/dmd/lexer.d index 441b67dce9e..ba2c9f6ad10 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -2570,6 +2570,14 @@ class Lexer TOK result; bool isOutOfRange = false; t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, isOutOfRange) : CTFloat.zero); + + bool imaginary = false; + if (*p == 'i' && Ccompile) + { + ++p; + imaginary = true; + } + switch (*p) { case 'F': @@ -2595,11 +2603,17 @@ class Lexer result = TOK.float80Literal; break; } + if ((*p == 'i' || *p == 'I') && !Ccompile) { if (*p == 'I') error("use 'i' suffix instead of 'I'"); p++; + imaginary = true; + } + + if (imaginary) + { switch (result) { case TOK.float32Literal: diff --git a/dmd/mtype.d b/dmd/mtype.d index 5939db5ba85..3ed6be55fc9 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -2645,6 +2645,8 @@ extern (C++) abstract class Type : ASTNode if (t.isimaginary() || t.iscomplex()) { + if (sc.flags & SCOPE.Cfile) + return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) { diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i new file mode 100644 index 00000000000..61af9ca81d8 --- /dev/null +++ b/tests/dmd/compilable/testcomplex.i @@ -0,0 +1,21 @@ + +/* GCC header complex.h requires supporting `i` suffix extension + */ + +_Complex float testf() +{ + _Complex float x = 1.0if; + return x; +} + +_Complex double testd() +{ + _Complex double x = 1.0i; + return x; +} + +_Complex long double testld() +{ + _Complex long double x = 1.0iL; + return x; +} From ad262fe548b9ac0474024c1c28d4b02524c0f4a4 Mon Sep 17 00:00:00 2001 From: Mathias LANG Date: Wed, 8 Mar 2023 09:57:41 +0100 Subject: [PATCH 028/301] dmd: Deprecate 'in' parameters on non-extern(D,C++) functions (dlang/dmd!14951) This error was introduced in `-preview=in` in v2.101.0. In order to make `-preview=in` the default, we add a deprecation even if `-preview=in` is not used. --- dmd/typesem.d | 22 ++++++++++++++----- tests/dmd/compilable/warn3882.d | 2 +- .../deprecations_preview_in.d | 11 ++++++++++ tests/dmd/runnable/debug_info.d | 4 ++-- tests/dmd/runnable/imports/link13415a.d | 2 +- tests/dmd/runnable/objc_call.d | 4 ++-- tests/dmd/runnable/objc_objc_msgSend.d | 2 +- tests/dmd/runnable/objc_protocol_sections.d | 4 ++-- 8 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 tests/dmd/fail_compilation/deprecations_preview_in.d diff --git a/dmd/typesem.d b/dmd/typesem.d index aed7ccbd5a8..71f80f7f87b 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1185,19 +1185,31 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // -preview=in: Always add `ref` when used with `extern(C++)` functions // Done here to allow passing opaque types with `in` - if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) + if ((fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) { switch (tf.linkage) { case LINK.cpp: - fparam.storageClass |= STC.ref_; + if (global.params.previewIn) + fparam.storageClass |= STC.ref_; break; case LINK.default_, LINK.d: break; default: - .error(loc, "cannot use `in` parameters with `extern(%s)` functions", - linkageToChars(tf.linkage)); - .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + if (global.params.previewIn) + { + .error(loc, "cannot use `in` parameters with `extern(%s)` functions", + linkageToChars(tf.linkage)); + .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } + else + { + // Note that this deprecation will not trigger on `in ref` / `ref in` + // parameters, however the parser will trigger a deprecation on them. + .deprecation(loc, "using `in` parameters with `extern(%s)` functions is deprecated", + linkageToChars(tf.linkage)); + .deprecationSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } break; } } diff --git a/tests/dmd/compilable/warn3882.d b/tests/dmd/compilable/warn3882.d index af7aed9059d..0474315be57 100644 --- a/tests/dmd/compilable/warn3882.d +++ b/tests/dmd/compilable/warn3882.d @@ -12,7 +12,7 @@ void test3882() /******************************************/ // https://issues.dlang.org/show_bug.cgi?id=12619 -extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n); +extern (C) @system nothrow pure void* memcpy(void* s1, const void* s2, size_t n); // -> weakly pure void test12619() pure diff --git a/tests/dmd/fail_compilation/deprecations_preview_in.d b/tests/dmd/fail_compilation/deprecations_preview_in.d new file mode 100644 index 00000000000..33cc904e4be --- /dev/null +++ b/tests/dmd/fail_compilation/deprecations_preview_in.d @@ -0,0 +1,11 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecations_preview_in.d(1): Deprecation: using `in` parameters with `extern(C)` functions is deprecated +fail_compilation/deprecations_preview_in.d(1): parameter `__anonymous_param` declared as `in` here +--- +*/ + +#line 1 +extern(C) void fun1(in char*); diff --git a/tests/dmd/runnable/debug_info.d b/tests/dmd/runnable/debug_info.d index 902f2e569eb..7853d9a3d9f 100644 --- a/tests/dmd/runnable/debug_info.d +++ b/tests/dmd/runnable/debug_info.d @@ -27,8 +27,8 @@ else extern (C) { MachHeader* _dyld_get_image_header(uint image_index); - const(section)* getsectbynamefromheader(in mach_header* mhp, in char* segname, in char* sectname); - const(section_64)* getsectbynamefromheader_64(in mach_header_64* mhp, in char* segname, in char* sectname); + const(section)* getsectbynamefromheader(scope const mach_header* mhp, scope const char* segname, scope const char* sectname); + const(section_64)* getsectbynamefromheader_64(scope const mach_header_64* mhp, scope const char* segname, scope const char* sectname); } const(Section)* getSectByNameFromHeader(MachHeader* mhp, in char* segname, in char* sectname) diff --git a/tests/dmd/runnable/imports/link13415a.d b/tests/dmd/runnable/imports/link13415a.d index de3bbe2ac9b..077671b01c4 100644 --- a/tests/dmd/runnable/imports/link13415a.d +++ b/tests/dmd/runnable/imports/link13415a.d @@ -7,7 +7,7 @@ struct S(alias func) } } -extern(C) int printf(in char*, ...); +extern(C) int printf(const char*, ...); void f(int i = 77) { diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 1463ac34bae..98199a0abb4 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -12,12 +12,12 @@ extern class Class extern (Objective-C) extern class NSObject { - NSObject initWithUTF8String(in char* str) @selector("initWithUTF8String:"); + NSObject initWithUTF8String(scope const char* str) @selector("initWithUTF8String:"); void release() @selector("release"); } extern (C) void NSLog(NSObject, ...); -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); void main() { diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index 21fe95cbda5..8b7e8ab5c40 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -3,7 +3,7 @@ import core.attribute : selector; -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); struct Struct { diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index b2869d204d9..89790c1cfc7 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -31,8 +31,8 @@ struct objc_method_description } } -SEL sel_registerName(in char* str); -Protocol* objc_getProtocol(in char* name); +SEL sel_registerName(scope const char* str); +Protocol* objc_getProtocol(scope const char* name); objc_method_description protocol_getMethodDescription( Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod ); From d022a164e511211b72db0c9dbbbf194ef1dbf5ae Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 8 Mar 2023 01:52:12 -0800 Subject: [PATCH 029/301] parse.d: remove errors.d dependence and some globals (dlang/dmd!14957) --- dmd/dsymbolsem.d | 3 +++ dmd/parse.d | 26 +++++++++++--------------- dmd/typesem.d | 3 +++ tests/dmd/compilable/transition_in.d | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 5da2375fa55..ff800676b05 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1321,6 +1321,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.errors) return; + if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + dsym.error("use -preview=bitfields for bitfield support"); + if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration()) { dsym.error("- bit-field must be member of struct, union, or class"); diff --git a/dmd/parse.d b/dmd/parse.d index 0496b4c3854..b22aac2a516 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -15,6 +15,7 @@ module dmd.parse; import core.stdc.stdio; import core.stdc.string; + import dmd.astenums; import dmd.errorsink; import dmd.globals; @@ -22,7 +23,6 @@ import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; -import dmd.errors; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; @@ -64,7 +64,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer //printf("Parser::Parser()\n"); scanloc = loc; - if (!writeMixin(input, scanloc) && loc.filename) + if (!writeMixin(input, scanloc, global.params.mixinOut) && loc.filename) { /* Create a pseudo-filename for the mixin string, as it may not even exist * in the source file. @@ -2857,8 +2857,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (global.params.vin) - message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; goto L2; @@ -4618,8 +4616,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Dsymbol s; if (width) { - if (!global.params.bitfields) - error("use -preview=bitfields for bitfield support"); if (_init) error("initializer not allowed for bit-field declaration"); if (storage_class) @@ -9189,7 +9185,7 @@ LagainStc: void checkRequiredParens() { if (e.op == EXP.question && !e.parens) - dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", + eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(token.value)); } @@ -9720,20 +9716,20 @@ private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) /************************************** * dump mixin expansion to file for better debugging */ -private bool writeMixin(const(char)[] s, ref Loc loc) +private bool writeMixin(const(char)[] s, ref Loc loc, ref Output output) { - if (!global.params.mixinOut.doOutput) + if (!output.doOutput) return false; - OutBuffer* ob = global.params.mixinOut.buffer; + OutBuffer* ob = output.buffer; ob.writestring("// expansion at "); ob.writestring(loc.toChars()); ob.writenl(); - global.params.mixinOut.bufferLines++; + output.bufferLines++; - loc = Loc(global.params.mixinOut.name.ptr, global.params.mixinOut.bufferLines + 1, loc.charnum); + loc = Loc(output.name.ptr, output.bufferLines + 1, loc.charnum); // write by line to create consistent line endings size_t lastpos = 0; @@ -9745,7 +9741,7 @@ private bool writeMixin(const(char)[] s, ref Loc loc) { ob.writestring(s[lastpos .. i]); ob.writenl(); - global.params.mixinOut.bufferLines++; + output.bufferLines++; if (c == '\r') ++i; lastpos = i + 1; @@ -9758,10 +9754,10 @@ private bool writeMixin(const(char)[] s, ref Loc loc) if (s.length == 0 || s[$-1] != '\n') { ob.writenl(); // ensure empty line after expansion - global.params.mixinOut.bufferLines++; + output.bufferLines++; } ob.writenl(); - global.params.mixinOut.bufferLines++; + output.bufferLines++; return true; } diff --git a/dmd/typesem.d b/dmd/typesem.d index 71f80f7f87b..fa706fde349 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1119,6 +1119,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) fparam.type = fparam.type.addStorageClass(fparam.storageClass); + if (global.params.vin && fparam.storageClass & STC.in_) + message(loc, "Usage of 'in' on parameter"); + if (fparam.storageClass & (STC.auto_ | STC.alias_ | STC.static_)) { if (!fparam.type) diff --git a/tests/dmd/compilable/transition_in.d b/tests/dmd/compilable/transition_in.d index cc492a747fd..ec5d0adc803 100644 --- a/tests/dmd/compilable/transition_in.d +++ b/tests/dmd/compilable/transition_in.d @@ -5,7 +5,7 @@ TEST_OUTPUT: --- compilable/transition_in.d(3): Usage of 'in' on parameter compilable/transition_in.d(3): Usage of 'in' on parameter -compilable/transition_in.d(8): Usage of 'in' on parameter +compilable/transition_in.d(13): Usage of 'in' on parameter compilable/transition_in.d(13): Usage of 'in' on parameter --- */ From 40520e5c8d7f6a88bb81e994039f4363b327ee9d Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 8 Mar 2023 14:37:10 +0100 Subject: [PATCH 030/301] Fix 23764 - Message printed twice: Usage of in on parameter --- dmd/dmodule.d | 1 + dmd/dsymbolsem.d | 1 + dmd/errors.d | 8 ++++++++ dmd/errorsink.d | 20 ++++++++++++++++++++ dmd/expressionsem.d | 1 + dmd/parse.d | 4 ++++ dmd/statementsem.d | 1 + dmd/typesem.d | 4 +--- tests/dmd/compilable/transition_in.d | 2 +- 9 files changed, 38 insertions(+), 4 deletions(-) diff --git a/dmd/dmodule.d b/dmd/dmodule.d index c911ec4976b..1d30d58241e 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -777,6 +777,7 @@ extern (C++) final class Module : Package else { scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv); + p.transitionIn = global.params.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index ff800676b05..42e1a84b222 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1937,6 +1937,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + p.transitionIn = global.params.vin; p.nextToken(); auto d = p.parseDeclDefs(0); diff --git a/dmd/errors.d b/dmd/errors.d index 6d6bd455666..f1087ad9429 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -73,6 +73,14 @@ class ErrorSinkCompiler : ErrorSink vdeprecationSupplemental(loc, format, ap); va_end(ap); } + + void message(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } } diff --git a/dmd/errorsink.d b/dmd/errorsink.d index b519db7e9bc..e57c2b6e388 100644 --- a/dmd/errorsink.d +++ b/dmd/errorsink.d @@ -27,6 +27,8 @@ abstract class ErrorSink void warning(const ref Loc loc, const(char)* format, ...); + void message(const ref Loc loc, const(char)* format, ...); + void deprecation(const ref Loc loc, const(char)* format, ...); void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); @@ -47,6 +49,8 @@ class ErrorSinkNull : ErrorSink void warning(const ref Loc loc, const(char)* format, ...) { } + void message(const ref Loc loc, const(char)* format, ...) { } + void deprecation(const ref Loc loc, const(char)* format, ...) { } void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } @@ -117,5 +121,21 @@ class ErrorSinkStderr : ErrorSink va_end(ap); } + void message(const ref Loc loc, const(char)* format, ...) + { + const p = loc.toChars(); + if (*p) + { + fprintf(stderr, "%s: ", p); + //mem.xfree(cast(void*)p); // loc should provide the free() + } + + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fputc('\n', stderr); + va_end(ap); + } + void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } } diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 6f065e85523..dd13bf8c50b 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6107,6 +6107,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const len = buf.length; const str = buf.extractChars()[0 .. len]; scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/parse.d b/dmd/parse.d index b22aac2a516..88ed986918a 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -49,6 +49,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc lookingForElse; // location of lonely if looking for an else } + bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed + /********************* * Use this constructor for string mixins. * Input: @@ -2857,6 +2859,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: + if (transitionIn) + eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; goto L2; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index aee2931af47..1457bf75235 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -4756,6 +4756,7 @@ private Statements* flatten(Statement statement, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + p.transitionIn = global.params.vin; p.nextToken(); auto a = new Statements(); diff --git a/dmd/typesem.d b/dmd/typesem.d index fa706fde349..ed123ae997b 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1119,9 +1119,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) fparam.type = fparam.type.addStorageClass(fparam.storageClass); - if (global.params.vin && fparam.storageClass & STC.in_) - message(loc, "Usage of 'in' on parameter"); - if (fparam.storageClass & (STC.auto_ | STC.alias_ | STC.static_)) { if (!fparam.type) @@ -4934,6 +4931,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv); + p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/tests/dmd/compilable/transition_in.d b/tests/dmd/compilable/transition_in.d index ec5d0adc803..cc492a747fd 100644 --- a/tests/dmd/compilable/transition_in.d +++ b/tests/dmd/compilable/transition_in.d @@ -5,7 +5,7 @@ TEST_OUTPUT: --- compilable/transition_in.d(3): Usage of 'in' on parameter compilable/transition_in.d(3): Usage of 'in' on parameter -compilable/transition_in.d(13): Usage of 'in' on parameter +compilable/transition_in.d(8): Usage of 'in' on parameter compilable/transition_in.d(13): Usage of 'in' on parameter --- */ From b6a11e1e3877c4176cf1b931836ed873af6c839e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 8 Mar 2023 14:25:04 -0800 Subject: [PATCH 031/301] use template mixin for visiting Initializers (dlang/dmd!14955) --- dmd/frontend.h | 1 + dmd/hdrgen.d | 11 ++----- dmd/init.d | 84 ++++++++++++++++++++++++++++++++++++++++++++------ dmd/initsem.d | 35 +++++---------------- 4 files changed, 85 insertions(+), 46 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 5a04e4edeba..07e8125cf23 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -5017,6 +5017,7 @@ struct ASTCodegen final using Initializer = ::Initializer; using NeedInterpret = ::NeedInterpret; using StructInitializer = ::StructInitializer; + using VisitInitializer = ::VisitInitializer; using VoidInitializer = ::VoidInitializer; using Covariant = ::Covariant; using DotExpFlag = ::DotExpFlag; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 67fdce91c4b..f2c3a54f2f6 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -3814,15 +3814,8 @@ private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* h buf.writeByte('}'); } - final switch (inx.kind) - { - case InitKind.error: return visitError (inx.isErrorInitializer ()); - case InitKind.void_: return visitVoid (inx.isVoidInitializer ()); - case InitKind.struct_: return visitStruct(inx.isStructInitializer()); - case InitKind.array: return visitArray (inx.isArrayInitializer ()); - case InitKind.exp: return visitExp (inx.isExpInitializer ()); - case InitKind.C_: return visitC (inx.isCInitializer ()); - } + mixin VisitInitializer!void visit; + visit.VisitInitializer(inx); } diff --git a/dmd/init.d b/dmd/init.d index f646d0382eb..6f20a3c7a45 100644 --- a/dmd/init.d +++ b/dmd/init.d @@ -269,7 +269,22 @@ extern (C++) final class CInitializer : Initializer */ Initializer syntaxCopy(Initializer inx) { - static Initializer copyStruct(StructInitializer vi) + static Initializer visitVoid(VoidInitializer vi) + { + return new VoidInitializer(vi.loc); + } + + static Initializer visitError(ErrorInitializer vi) + { + return vi; + } + + static Initializer visitExp(ExpInitializer vi) + { + return new ExpInitializer(vi.loc, vi.exp.syntaxCopy()); + } + + static Initializer visitStruct(StructInitializer vi) { auto si = new StructInitializer(vi.loc); assert(vi.field.length == vi.value.length); @@ -283,7 +298,7 @@ Initializer syntaxCopy(Initializer inx) return si; } - static Initializer copyArray(ArrayInitializer vi) + static Initializer visitArray(ArrayInitializer vi) { auto ai = new ArrayInitializer(vi.loc); assert(vi.index.length == vi.value.length); @@ -297,7 +312,7 @@ Initializer syntaxCopy(Initializer inx) return ai; } - static Initializer copyC(CInitializer vi) + static Initializer visitC(CInitializer vi) { auto ci = new CInitializer(vi.loc); ci.initializerList.setDim(vi.initializerList.length); @@ -322,13 +337,62 @@ Initializer syntaxCopy(Initializer inx) return ci; } - final switch (inx.kind) + mixin VisitInitializer!Initializer visit; + return visit.VisitInitializer(inx); +} + +/*********************************************************** + * Visit each Initializer in init. Call a function visit%s(init) for + * each node, where %s is the op of the node. Otherwise call visitDefault(init) + * for that node. If the visit function returns R.init, continue + * visiting each node, otherwise return the value of R. + * Params: + * Result = return type + * init = Initializer tree to traverse + * Returns: + * Result.init for continue, value of type Result for early exit + */ + +mixin template VisitInitializer(Result) +{ + Result VisitInitializer(Initializer init) + { + final switch (init.kind) + { + case InitKind.void_: mixin(visitCase("Void")); break; + case InitKind.error: mixin(visitCase("Error")); break; + case InitKind.struct_: mixin(visitCase("Struct")); break; + case InitKind.array: mixin(visitCase("Array")); break; + case InitKind.exp: mixin(visitCase("Exp")); break; + case InitKind.C_: mixin(visitCase("C")); break; + } + static if (is(Result == void)) { } else + return Result.init; + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitCase(string handler) +{ + if (__ctfe) { - case InitKind.void_: return new VoidInitializer(inx.loc); - case InitKind.error: return inx; - case InitKind.struct_: return copyStruct(cast(StructInitializer)inx); - case InitKind.array: return copyArray(cast(ArrayInitializer)inx); - case InitKind.exp: return new ExpInitializer(inx.loc, (cast(ExpInitializer)inx).exp.syntaxCopy()); - case InitKind.C_: return copyC(cast(CInitializer)inx); + return + " + auto ix = init.is"~handler~"Initializer(); + static if (is(Result == void)) + visit"~handler~"(ix); + else + { + Result r = visit"~handler~"(ix); + if (r !is Result.init) + return r; + } + "; } + assert(0); } diff --git a/dmd/initsem.d b/dmd/initsem.d index 18b10b41a2d..893d2a627c3 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -958,15 +958,9 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ } } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1120,15 +1114,9 @@ Initializer inferType(Initializer init, Scope* sc) return new ErrorInitializer(); } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1333,15 +1321,8 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n return null; } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Expression visit; + return visit.VisitInitializer(init); } From c946a66b5cbb40939811d2f69d27ed2225a36915 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 9 Mar 2023 00:49:09 -0800 Subject: [PATCH 032/301] array compare: do not lower if CTFE --- dmd/expressionsem.d | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index dd13bf8c50b..66de0eecf48 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -11844,7 +11844,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars()); return setError(); } - if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) + + if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // interpreter can handle these + { } + else if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); From ca508ed9739634f4ae267a78e21f4ed3f78fcba6 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 9 Mar 2023 14:21:45 -0800 Subject: [PATCH 033/301] add Scope.needsCodegen --- dmd/dscope.d | 10 ++++++++++ dmd/dstruct.d | 2 +- dmd/expressionsem.d | 16 +++++++--------- dmd/statementsem.d | 5 ++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/dmd/dscope.d b/dmd/dscope.d index ab422fd0cf6..95cfec9c2b9 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -813,4 +813,14 @@ extern (C++) struct Scope { return this.intypeof || this.flags & SCOPE.compile; } + + + /** + * Returns: true if the code needs to go all the way through to code generation. + * This implies things like needing lowering to simpler forms. + */ + extern (D) bool needsCodegen() + { + return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + } } diff --git a/dmd/dstruct.d b/dmd/dstruct.d index 3268d5667de..c71e4a7be00 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -75,7 +75,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t) { if (sc.intypeof) return; - if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + if (!sc.needsCodegen()) return; } diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 66de0eecf48..1cda022e742 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3797,7 +3797,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = id.expressionSemantic(sc); return; } - else if (!(sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) && // interpreter can handle these + else if (sc.needsCodegen() && // interpreter doesn't need this lowered !exp.onstack && !exp.type.isscope()) // these won't use the GC { /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` @@ -6262,10 +6262,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("AssertExp::semantic('%s')\n", exp.toChars()); } - const ctfe = (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) != 0; - const generateMsg = !exp.msg && - !ctfe && // let ctfe interpreter handle the error message + sc.needsCodegen() && // let ctfe interpreter handle the error message global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on; Expression temporariesPrefix; @@ -9703,7 +9701,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setResult(res); } - if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // if compile time creature only + if (!sc.needsCodegen()) // if compile time creature only { exp.type = Type.tsize_t; return setResult(exp); @@ -10063,7 +10061,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // interpreter can handle these + if (!sc.needsCodegen()) // interpreter can handle these return setResult(res); const lowerToArrayCtor = @@ -11845,9 +11843,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (sc.flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) // interpreter can handle these - { } - else if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) + if (sc.needsCodegen() && + (t1.ty == Tarray || t1.ty == Tsarray) && + (t2.ty == Tarray || t2.ty == Tsarray)) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 1457bf75235..5890bba76dc 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -2289,7 +2289,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } - else if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + else if (!sc.needsCodegen()) { // something for the interpreter to deal with s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0)); @@ -2343,8 +2343,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } - if (!ss.condition.type.isString() || - sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + if (!(ss.condition.type.isString() && sc.needsCodegen())) { sc.pop(); result = ss; From f9345ad7bb7b58918f497722787a3a65350d31a9 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 9 Mar 2023 16:13:35 -0800 Subject: [PATCH 034/301] convert C imaginary numbers to core.stdc.config.c_complex_double (dlang/dmd!14971) * convert C imaginary to c_complex * convert C imaginary numbers to core.stdc.config.c_complex_double --- dmd/declaration.d | 4 ++-- dmd/dmodule.d | 30 +++++++++++++++++++++++------- dmd/expressionsem.d | 22 ++++++++++++++++++++++ dmd/frontend.h | 4 ++++ dmd/id.d | 4 ++++ dmd/typesem.d | 22 ++++++++++++++++++++++ tests/dmd/compilable/testcomplex.i | 4 ++++ 7 files changed, 81 insertions(+), 9 deletions(-) diff --git a/dmd/declaration.d b/dmd/declaration.d index 7cd8df19bdc..8eaac374d73 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -738,7 +738,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Type type) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type); + //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); //printf("type = '%s'\n", type.toChars()); this.type = type; assert(type); @@ -747,7 +747,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s); + //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); assert(s != this); this.aliassym = s; assert(s); diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 1d30d58241e..1e30550ec2f 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -1286,6 +1286,20 @@ extern (C++) final class Module : Package return _escapetable; } + /**************************** + * A Singleton that loads core.stdc.config + * Returns: + * Module of core.stdc.config, null if couldn't find it + */ + extern (D) static Module loadCoreStdcConfig() + { + __gshared Module core_stdc_config; + auto pkgids = new Identifier[2]; + pkgids[0] = Id.core; + pkgids[1] = Id.stdc; + return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config); + } + /**************************** * A Singleton that loads core.atomic * Returns: @@ -1294,7 +1308,9 @@ extern (C++) final class Module : Package extern (D) static Module loadCoreAtomic() { __gshared Module core_atomic; - return loadModuleFromLibrary(core_atomic, Id.core, Id.atomic); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.core; + return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic); } /**************************** @@ -1305,26 +1321,26 @@ extern (C++) final class Module : Package extern (D) static Module loadStdMath() { __gshared Module std_math; - return loadModuleFromLibrary(std_math, Id.std, Id.math); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.std; + return loadModuleFromLibrary(std_math, pkgids, Id.math); } /********************************** * Load a Module from the library. * Params: * mod = cached return value of this call - * pkgid = package id + * pkgids = package identifiers * modid = module id * Returns: * Module loaded, null if cannot load it */ - private static Module loadModuleFromLibrary(ref Module mod, Identifier pkgid, Identifier modid) + extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid) { if (mod) return mod; - auto ids = new Identifier[1]; - ids[0] = pkgid; - auto imp = new Import(Loc.initial, ids[], modid, null, true); + auto imp = new Import(Loc.initial, pkgids[], modid, null, true); // Module.load will call fatal() if there's no module available. // Gag the error here, pushing the error handling to the caller. const errors = global.startGagging(); diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 1cda022e742..1506857da9e 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2608,6 +2608,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; + else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + { + /* Convert to core.stdc.config.complex + */ + Module mConfig = Module.loadCoreStdcConfig(); + if (!mConfig) + { + e.error("`%s` requires `core.stdc.config` for complex numbers", e.toChars()); + return setError(); + } + + Dsymbol s = mConfig.searchX(e.loc, sc, Id.c_complex_double, IgnorePrivateImports); + s = s.toAlias(); + AliasDeclaration ad = s.isAliasDeclaration(); + TypeStruct ts = ad.getType().toBasetype().isTypeStruct(); + Expressions* elements = new Expressions(2); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, Type.tfloat64); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), Type.tfloat64); + Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); + result = sle.expressionSemantic(sc); + return; + } else e.type = e.type.typeSemantic(e.loc, sc); result = e; diff --git a/dmd/frontend.h b/dmd/frontend.h index 07e8125cf23..7c428c6eac1 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8685,6 +8685,10 @@ struct Id final static Identifier* va_start; static Identifier* std; static Identifier* core; + static Identifier* config; + static Identifier* c_complex_float; + static Identifier* c_complex_double; + static Identifier* c_complex_real; static Identifier* etc; static Identifier* attribute; static Identifier* atomic; diff --git a/dmd/id.d b/dmd/id.d index ec5cb25ef28..9ccbc025910 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -370,6 +370,10 @@ immutable Msgtable[] msgtable = // Builtin functions { "std" }, { "core" }, + { "config" }, + { "c_complex_float" }, + { "c_complex_double" }, + { "c_complex_real" }, { "etc" }, { "attribute" }, { "atomic" }, diff --git a/dmd/typesem.d b/dmd/typesem.d index ed123ae997b..d81d97f1122 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -456,6 +456,27 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) return t.merge(); } + Type visitComplex64(TypeBasic t) + { + if (!(sc.flags & SCOPE.Cfile)) + return visitType(t); + + /* Convert to core.stdc.config.complex + */ + Module mConfig = Module.loadCoreStdcConfig(); + if (!mConfig) + { + .error(loc, "`%s` requires `core.stdc.config` for complex numbers", t.toChars()); + return error(); + } + + Dsymbol s = mConfig.searchX(Loc.initial, sc, Id.c_complex_double, IgnorePrivateImports); + s = s.toAlias(); + AliasDeclaration ad = s.isAliasDeclaration(); + TypeStruct ts = ad.getType().toBasetype().isTypeStruct(); + return ts.merge(); + } + Type visitVector(TypeVector mtype) { const errors = global.errors; @@ -1940,6 +1961,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) switch (type.ty) { default: return visitType(type); + case Tcomplex64: return visitComplex64(type.isTypeBasic()); case Tvector: return visitVector(type.isTypeVector()); case Tsarray: return visitSArray(type.isTypeSArray()); case Tarray: return visitDArray(type.isTypeDArray()); diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i index 61af9ca81d8..6cfd09c181f 100644 --- a/tests/dmd/compilable/testcomplex.i +++ b/tests/dmd/compilable/testcomplex.i @@ -2,11 +2,13 @@ /* GCC header complex.h requires supporting `i` suffix extension */ +/* _Complex float testf() { _Complex float x = 1.0if; return x; } +*/ _Complex double testd() { @@ -14,8 +16,10 @@ _Complex double testd() return x; } +/* _Complex long double testld() { _Complex long double x = 1.0iL; return x; } +*/ From b060901379ec136b3c6de65d0d4e468217097319 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 9 Mar 2023 17:45:32 -0800 Subject: [PATCH 035/301] move decision on parsing unittests out of Parser (dlang/dmd!14973) --- dmd/cparse.d | 3 ++- dmd/dmodule.d | 3 ++- dmd/dsymbolsem.d | 3 ++- dmd/expressionsem.d | 3 ++- dmd/iasmgcc.d | 9 ++++++--- dmd/parse.d | 14 +++++++++----- dmd/statementsem.d | 3 ++- dmd/typesem.d | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index a301f96be4c..831c5eec7e7 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -69,7 +69,8 @@ final class CParser(AST) : Parser!AST ErrorSink errorSink, const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope { - super(_module, input, doDocComment, errorSink, compileEnv); + const bool doUnittests = false; + super(_module, input, doDocComment, errorSink, compileEnv, doUnittests); //printf("CParser.this()\n"); mod = _module; diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 1e30550ec2f..149a5b1aeb5 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -776,7 +776,8 @@ extern (C++) final class Module : Package } else { - scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); p.parseModuleDeclaration(); diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 42e1a84b222..2f3e167865e 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1936,7 +1936,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 1506857da9e..9b7f187f091 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6128,7 +6128,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/iasmgcc.d b/dmd/iasmgcc.d index 1ff5839b11e..1d4dea47b81 100644 --- a/dmd/iasmgcc.d +++ b/dmd/iasmgcc.d @@ -302,7 +302,8 @@ Ldone: extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); // Make a safe copy of the token list before parsing. Token *toklist = null; @@ -410,7 +411,8 @@ unittest { const errors = global.errors; scope gas = new GccAsmStatement(Loc.initial, tokens); - scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests); p.token = *tokens; p.parseGccAsm(gas); return global.errors - errors; @@ -420,7 +422,8 @@ unittest static void parseAsm(string input, bool expectError) { // Generate tokens from input test. - scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests); p.nextToken(); Token* toklist = null; diff --git a/dmd/parse.d b/dmd/parse.d index 88ed986918a..65fc4e94261 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -47,6 +47,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc endloc; // set to location of last right curly int inBrackets; // inside [] of array index or slice Loc lookingForElse; // location of lonely if looking for an else + bool doUnittests; // parse unittest blocks } bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed @@ -57,14 +58,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * loc location in source file of mixin */ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, - ErrorSink errorSink, const CompileEnv* compileEnv) scope + ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, compileEnv); - //printf("Parser::Parser()\n"); + //printf("Parser::Parser()1 %d\n", doUnittests); scanloc = loc; + this.doUnittests = doUnittests; if (!writeMixin(input, scanloc, global.params.mixinOut) && loc.filename) { @@ -83,15 +85,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, - const CompileEnv* compileEnv) scope + const CompileEnv* compileEnv, const bool doUnittests) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, compileEnv); - //printf("Parser::Parser()\n"); + //printf("Parser::Parser()2 %d\n", doUnittests); mod = _module; linkage = LINK.d; + this.doUnittests = doUnittests; //nextToken(); // start up the scanner } @@ -503,7 +506,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * template instantiations in these unittests as candidates for * further codegen culling. */ - if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput)) + // The isRoot check is here because it can change after parsing begins (see dmodule.d) + if (doUnittests && mod.isRoot()) { linkage = LINK.d; // unittests have D linkage s = parseUnitTest(pAttrs); diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 5890bba76dc..7ad608492d7 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -4754,7 +4754,8 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); diff --git a/dmd/typesem.d b/dmd/typesem.d index d81d97f1122..5469a382946 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -4952,7 +4952,8 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); From 19502d8113779217b610e9924a7b366733813d7a Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 9 Mar 2023 22:16:11 -0800 Subject: [PATCH 036/301] recognize if(not __ctfe) (dlang/dmd!14972) * recognize if(not __ctfe) * recognize if(not __ctfe) --- dmd/statementsem.d | 14 ++++++++++++++ tests/dmd/fail_compilation/ctfeblock.d | 13 ++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 7ad608492d7..a297f8a3374 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -1960,6 +1960,20 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor // Save 'root' of two branches (then and else) at the point where it forks CtorFlow ctorflow_root = scd.ctorflow.clone(); + /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A` + */ + NotExp notExp; + if (ifs.elsebody && + (notExp = ifs.condition.isNotExp()) !is null && + notExp.e1.isVarExp() && + notExp.e1.isVarExp().var.ident == Id.ctfe) + { + ifs.condition = notExp.e1; + auto sbody = ifs.ifbody; + ifs.ifbody = ifs.elsebody; + ifs.elsebody = sbody; + } + /* Detect `if (__ctfe)` */ if (ifs.isIfCtfeBlock()) diff --git a/tests/dmd/fail_compilation/ctfeblock.d b/tests/dmd/fail_compilation/ctfeblock.d index 2d8bf7a0fef..9c901033223 100644 --- a/tests/dmd/fail_compilation/ctfeblock.d +++ b/tests/dmd/fail_compilation/ctfeblock.d @@ -19,7 +19,7 @@ struct T { } { L1: new T(); - a = 3; + a = 3; } goto L1; } @@ -31,3 +31,14 @@ L1: new T(); } } + +@nogc void test3() +{ + if (!__ctfe) + { + } + else + { + int* p = new int; + } +} From 51a2982cb407e8c0e37783a209ef87bc64bfa2fa Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 10 Mar 2023 01:43:17 -0800 Subject: [PATCH 037/301] lower array cast only if it needs code gen (dlang/dmd!14979) --- dmd/expressionsem.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 9b7f187f091..66b95ce77c3 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -7784,7 +7784,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0) + if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && sc.needsCodegen()) { auto tFrom = t1b.nextOf(); auto tTo = tob.nextOf(); From ccda04ba8393ad4dbfa2e7a5ca883092fe295362 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 10 Mar 2023 16:21:06 -0800 Subject: [PATCH 038/301] overlooked another sc.needsCodegen() --- dmd/expressionsem.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 66b95ce77c3..a8fbe47f4cd 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -10475,7 +10475,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = res; if ((exp.op == EXP.concatenateAssign || exp.op == EXP.concatenateElemAssign) && - !(sc.flags & (SCOPE.ctfe | SCOPE.compile))) + sc.needsCodegen()) { // if aa ordering is triggered, `res` will be a CommaExp // and `.e2` will be the rewritten original expression. From fa23aa28cc84d4071bc9538e1a255486318d0353 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 12 Mar 2023 10:40:42 +0100 Subject: [PATCH 039/301] Add test for function attributes on variables (dlang/dmd!14983) --- tests/dmd/fail_compilation/var_func_attr.d | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/dmd/fail_compilation/var_func_attr.d diff --git a/tests/dmd/fail_compilation/var_func_attr.d b/tests/dmd/fail_compilation/var_func_attr.d new file mode 100644 index 00000000000..8609d29b58c --- /dev/null +++ b/tests/dmd/fail_compilation/var_func_attr.d @@ -0,0 +1,35 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure` +--- +*/ + +// Test the effect of function attributes on variables +// See: +// https://issues.dlang.org/show_bug.cgi?id=7432 +// https://github.com/dlang/dmd/pull/14199 +// Usually it's a no-op, but the attribute can apply to the function/delegate type of the variable +// The current behavior is weird, so this is a test of the current behavior, not necessarily the desired behavior + +// No-op +pure int x; + +// Applies to function type (existing code in dmd and Phobos relies on this) +pure void function() pf = () { + static int g; + g++; +}; + +// Function attributes currently don't apply to inferred types (somewhat surprisingly) +nothrow nf = () { + throw new Exception(""); +}; + +// Neither do they apply to indirections +alias F = void function(); + +pure F pf2 = () { + static int g; + g++; +}; From c9fecc0154b25b333baa3b7830793487d8a039ea Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 13 Mar 2023 02:24:08 -0700 Subject: [PATCH 040/301] add _Complex float, _Complex real, and error checking (dlang/dmd!14977) --- dmd/expressionsem.d | 26 +++++---- dmd/typesem.d | 92 ++++++++++++++++++++++++------ tests/dmd/compilable/testcomplex.i | 4 -- 3 files changed, 92 insertions(+), 30 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index a8fbe47f4cd..ecfb7769589 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2612,20 +2612,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { /* Convert to core.stdc.config.complex */ - Module mConfig = Module.loadCoreStdcConfig(); - if (!mConfig) - { - e.error("`%s` requires `core.stdc.config` for complex numbers", e.toChars()); + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) return setError(); + + Type tf; + switch (e.type.ty) + { + case Timaginary32: tf = Type.tfloat32; break; + case Timaginary64: tf = Type.tfloat64; break; + case Timaginary80: tf = Type.tfloat80; break; + default: + assert(0); } - Dsymbol s = mConfig.searchX(e.loc, sc, Id.c_complex_double, IgnorePrivateImports); - s = s.toAlias(); - AliasDeclaration ad = s.isAliasDeclaration(); - TypeStruct ts = ad.getType().toBasetype().isTypeStruct(); + /* Construct ts{re : 0.0, im : e} + */ + TypeStruct ts = t.isTypeStruct; Expressions* elements = new Expressions(2); - (*elements)[0] = new RealExp(e.loc, CTFloat.zero, Type.tfloat64); - (*elements)[1] = new RealExp(e.loc, e.toImaginary(), Type.tfloat64); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); result = sle.expressionSemantic(sc); return; diff --git a/dmd/typesem.d b/dmd/typesem.d index 5469a382946..b2669f84ff6 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -456,25 +456,15 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) return t.merge(); } - Type visitComplex64(TypeBasic t) + Type visitComplex(TypeBasic t) { if (!(sc.flags & SCOPE.Cfile)) return visitType(t); - /* Convert to core.stdc.config.complex - */ - Module mConfig = Module.loadCoreStdcConfig(); - if (!mConfig) - { - .error(loc, "`%s` requires `core.stdc.config` for complex numbers", t.toChars()); - return error(); - } - - Dsymbol s = mConfig.searchX(Loc.initial, sc, Id.c_complex_double, IgnorePrivateImports); - s = s.toAlias(); - AliasDeclaration ad = s.isAliasDeclaration(); - TypeStruct ts = ad.getType().toBasetype().isTypeStruct(); - return ts.merge(); + auto tc = getComplexLibraryType(loc, sc, t.ty); + if (tc.ty == Terror) + return tc; + return tc.addMod(t.mod).merge(); } Type visitVector(TypeVector mtype) @@ -1961,7 +1951,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) switch (type.ty) { default: return visitType(type); - case Tcomplex64: return visitComplex64(type.isTypeBasic()); + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: return visitComplex(type.isTypeBasic()); case Tvector: return visitVector(type.isTypeVector()); case Tsarray: return visitSArray(type.isTypeSArray()); case Tarray: return visitDArray(type.isTypeDArray()); @@ -4632,6 +4624,74 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi } } + +/********************************************** + * Extract complex type from core.stdc.config + * Params: + * loc = for error messages + * sc = context + * ty = a complex or imaginary type + * Returns: + * Complex!float, Complex!double, Complex!real or null for error + */ + +Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) +{ + // singleton + __gshared Type complex_float; + __gshared Type complex_double; + __gshared Type complex_real; + + Type* pt; + Identifier id; + switch (ty) + { + case Timaginary32: + case Tcomplex32: id = Id.c_complex_float; pt = &complex_float; break; + case Timaginary64: + case Tcomplex64: id = Id.c_complex_double; pt = &complex_double; break; + case Timaginary80: + case Tcomplex80: id = Id.c_complex_real; pt = &complex_real; break; + default: + return Type.terror; + } + + if (*pt) + return *pt; + *pt = Type.terror; + + Module mConfig = Module.loadCoreStdcConfig(); + if (!mConfig) + { + error(loc, "`core.stdc.config` is required for complex numbers"); + return *pt; + } + + Dsymbol s = mConfig.searchX(Loc.initial, sc, id, IgnorePrivateImports); + if (!s) + { + error(loc, "`%s` not found in core.stdc.config", id.toChars()); + return *pt; + } + s = s.toAlias(); + if (auto t = s.getType()) + { + if (auto ts = t.toBasetype().isTypeStruct()) + { + *pt = ts; + return ts; + } + } + if (auto sd = s.isStructDeclaration()) + { + *pt = sd.type; + return sd.type; + } + + error(loc, "`%s` must be an alias for a complex struct", s.toChars()); + return *pt; +} + /******************************* Private *****************************************/ private: diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i index 6cfd09c181f..61af9ca81d8 100644 --- a/tests/dmd/compilable/testcomplex.i +++ b/tests/dmd/compilable/testcomplex.i @@ -2,13 +2,11 @@ /* GCC header complex.h requires supporting `i` suffix extension */ -/* _Complex float testf() { _Complex float x = 1.0if; return x; } -*/ _Complex double testd() { @@ -16,10 +14,8 @@ _Complex double testd() return x; } -/* _Complex long double testld() { _Complex long double x = 1.0iL; return x; } -*/ From 43e470bfa5d2f6358afe36af4b84b8867b2d401e Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 13 Mar 2023 15:42:18 +0000 Subject: [PATCH 041/301] Fix Issue 12118 - Modify immutable data using throw (dlang/dmd!14706) * Fix Issue 12118 - Modify immutable data using throw * Change immutable test to const * Deprecate throwing any qualified type * Allow throw shared as std.concurrency does that * Use static this instead of static const for test * Only allow single shared qualifier or none * Add changelog * Disallow throwing shared objects too * Update changelog/dmd.throw-qualifier.dd Co-authored-by: Razvan Nitu --------- Co-authored-by: Razvan Nitu --- dmd/statementsem.d | 8 +++++++- tests/dmd/fail_compilation/ice10651.d | 21 ++++++++++++++++++++- tests/dmd/runnable/class_destructors.d | 8 +++++++- tests/dmd/runnable/eh2.d | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index a297f8a3374..f83daa5c4a3 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -3796,7 +3796,13 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor exp = checkGC(sc, exp); if (exp.op == EXP.error) return false; - + if (!exp.type.isNaked()) + { + // @@@DEPRECATED_2.112@@@ + // Deprecated in 2.102, change into an error & return false in 2.112 + exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); + //return false; + } checkThrowEscape(sc, exp, false); ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); diff --git a/tests/dmd/fail_compilation/ice10651.d b/tests/dmd/fail_compilation/ice10651.d index 1f87955b959..8b6c7200bb7 100644 --- a/tests/dmd/fail_compilation/ice10651.d +++ b/tests/dmd/fail_compilation/ice10651.d @@ -1,7 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10651.d(11): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(13): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(19): Deprecation: cannot throw object of qualified type `immutable(Exception)` +fail_compilation/ice10651.d(20): Deprecation: cannot throw object of qualified type `const(Dummy)` --- */ @@ -10,3 +12,20 @@ void main() alias T = int; throw new T(); // ICE } + +void f() +{ + immutable c = new Exception(""); + if (c) throw c; + throw new const Dummy([]); +} + +class Dummy: Exception +{ + int[] data; + @safe pure nothrow this(immutable int[] data) immutable + { + super("Dummy"); + this.data = data; + } +} diff --git a/tests/dmd/runnable/class_destructors.d b/tests/dmd/runnable/class_destructors.d index b01c4d9e54e..ce126bfc85d 100644 --- a/tests/dmd/runnable/class_destructors.d +++ b/tests/dmd/runnable/class_destructors.d @@ -154,7 +154,13 @@ void testDeleteWithoutCpp() class ThrowingChildD : ChildD { - static immutable ex = new Exception("STOP"); + static Exception ex; + + static this() + { + ex = new Exception("STOP"); + } + ~this() { throw ex; diff --git a/tests/dmd/runnable/eh2.d b/tests/dmd/runnable/eh2.d index dc285a5dfab..2b469d2f803 100644 --- a/tests/dmd/runnable/eh2.d +++ b/tests/dmd/runnable/eh2.d @@ -24,7 +24,7 @@ class Abc : Throwable { printf("foo 1\n"); x |= 4; - throw this; + throw cast() this; printf("foo 2\n"); x |= 8; } From f9f87e43870b4ae7942fa9bfe50122729463b260 Mon Sep 17 00:00:00 2001 From: Atila Neves Date: Fri, 17 Mar 2023 09:42:11 +0100 Subject: [PATCH 042/301] Change globals.fieldwise from bool to FeatureState --- dmd/frontend.h | 5 ++--- dmd/globals.d | 2 +- dmd/globals.h | 2 +- dmd/opover.d | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 7c428c6eac1..e0ee8bfb1e3 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3078,7 +3078,7 @@ struct Param final FeatureState useDIP1000; bool ehnogc; bool useDIP1021; - bool fieldwise; + FeatureState fieldwise; bool fixAliasThis; FeatureState rvalueRefParam; FeatureState noSharedAccess; @@ -3181,7 +3181,6 @@ struct Param final useDIP25((FeatureState)1), ehnogc(), useDIP1021(), - fieldwise(), fixAliasThis(), previewIn(), inclusiveInContracts(), @@ -3231,7 +3230,7 @@ struct Param final mapfile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, bool fieldwise = false, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)-1, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : obj(obj), multiobj(multiobj), trace(trace), diff --git a/dmd/globals.d b/dmd/globals.d index fde1667556c..4d5d2461ae5 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -154,7 +154,7 @@ extern (C++) struct Param FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params bool ehnogc; // use @nogc exception handling bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html diff --git a/dmd/globals.h b/dmd/globals.h index 6266ff604e3..6b86df76c65 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -151,7 +151,7 @@ struct Param FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params d_bool ehnogc; // use @nogc exception handling d_bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - d_bool fieldwise; // do struct equality testing field-wise rather than by memcmp() + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() d_bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html diff --git a/dmd/opover.d b/dmd/opover.d index 3c80e5e1d0e..8d202db69ea 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -997,7 +997,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return null; import dmd.clone : needOpEquals; - if (!global.params.fieldwise && !needOpEquals(sd)) + if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) { // Use bitwise equality. auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; From 0979aca29300764263ea4144cc45ee9a2ad24156 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 17 Mar 2023 17:48:16 -0700 Subject: [PATCH 043/301] add operator overloading to core.stdc.config._Complex (dlang/dmd!14984) --- runtime/druntime/src/core/stdc/config.d | 337 +++++++++++++++++++++++- 1 file changed, 333 insertions(+), 4 deletions(-) diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index 8033018bf66..0c24dcf0cd4 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -259,11 +259,178 @@ else alias cpp_ptrdiff_t = ptrdiff_t; } -// ABI layout of native complex types. -private struct _Complex(T) +/** ABI layout of native complex types. + */ +struct _Complex(T) + if (is(T == float) || is(T == double) || is(T == c_long_double)) { - T re; - T im; + T re = 0; + T im = 0; + + // Construction +/+ https://issues.dlang.org/show_bug.cgi?id=23788 dmd codegen problem with constructors and _Complex!float + this(_Complex!float c) { re = c.re; im = c.im; } + this(_Complex!double c) { re = c.re; im = c.im; } + this(_Complex!c_long_double c) { re = c.re; im = c.im; } + + this(T re, T im) { this.re = re; this.im = im; } + + this(T re) { this.re = re; this.im = 0; } ++/ + // Assignment + + ref _Complex opAssign(_Complex!float c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!double c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!c_long_double c) { re = c.re; im = c.im; return this; } + + ref _Complex opAssign(T t) { re = t; im = 0; return this; } + + // Equals + + bool opEquals(_Complex!float c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!double c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!c_long_double c) { return re == c.re && im == c.im; } + + bool opEquals(T t) { return re == t && im == 0; } + + // Unary operators + + // +complex + _Complex opUnary(string op)() + if (op == "+") + { + return this; + } + + // -complex + _Complex opUnary(string op)() + if (op == "-") + { + return _Complex(-re, -im); + } + + // BINARY OPERATORS + + // complex op complex + _Complex!(CommonType!(T,R)) opBinary(string op, R)(_Complex!R z) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(z); + } + + // complex op numeric + _Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) + if (is(R : c_long_double)) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(r); + } + + // numeric + complex, numeric * complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if ((op == "+" || op == "*") && is(R : c_long_double)) + { + return opBinary!(op)(r); + } + + // numeric - complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "-" && is(R : c_long_double)) + { + return _Complex(r - re, -im); + } + + // numeric / complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "/" && is(R : c_long_double)) + { + import core.math : fabs; + typeof(return) w = void; + if (fabs(re) < fabs(im)) + { + immutable ratio = re/im; + immutable rdivd = r/(re*ratio + im); + + w.re = rdivd*ratio; + w.im = -rdivd; + } + else + { + immutable ratio = im/re; + immutable rdivd = r/(re + im*ratio); + + w.re = rdivd; + w.im = -rdivd*ratio; + } + + return w; + } + + // OP-ASSIGN OPERATORS + + // complex += complex, complex -= complex + ref _Complex opOpAssign(string op, C)(C z) + if ((op == "+" || op == "-") && is(C R == _Complex!R)) + { + mixin ("re "~op~"= z.re;"); + mixin ("im "~op~"= z.im;"); + return this; + } + + // complex *= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "*" && is(C R == _Complex!R)) + { + auto temp = re*z.re - im*z.im; + im = im*z.re + re*z.im; + re = temp; + return this; + } + + // complex /= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "/" && is(C R == _Complex!R)) + { + import core.math : fabs; + if (fabs(z.re) < fabs(z.im)) + { + immutable ratio = z.re/z.im; + immutable denom = z.re*ratio + z.im; + + immutable temp = (re*ratio + im)/denom; + im = (im*ratio - re)/denom; + re = temp; + } + else + { + immutable ratio = z.im/z.re; + immutable denom = z.re + z.im*ratio; + + immutable temp = (re + im*ratio)/denom; + im = (im - re*ratio)/denom; + re = temp; + } + return this; + } + + // complex += numeric, complex -= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "+" || op == "-") + { + mixin ("re "~op~"= a;"); + return this; + } + + // complex *= numeric, complex /= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "*" || op == "/") + { + mixin ("re "~op~"= a;"); + mixin ("im "~op~"= a;"); + return this; + } // Helper properties. pragma(inline, true) @@ -289,6 +456,168 @@ enum __c_complex_real : _Complex!c_long_double; alias c_complex_float = __c_complex_float; alias c_complex_double = __c_complex_double; alias c_complex_real = __c_complex_real; + +private template CommonType(T, R) +{ + // Special kludge for Microsoft c_long_double + static if (is(T == c_long_double)) + alias CommonType = T; + else static if (is(R == c_long_double)) + alias CommonType = R; + else + alias CommonType = typeof(true ? T.init : R.init); +} + +/************ unittests ****************/ + +version (unittest) +{ + private: + + alias _cfloat = _Complex!float; + alias _cdouble = _Complex!double; + alias _creal = _Complex!c_long_double; + + T abs(T)(T t) => t < 0 ? -t : t; +} + +@safe pure nothrow unittest +{ + auto c1 = _cdouble(1.0, 1.0); + + // Check unary operations. + auto c2 = _cdouble(0.5, 2.0); + + assert(c2 == +c2); + + assert((-c2).re == -(c2.re)); + assert((-c2).im == -(c2.im)); + assert(c2 == -(-c2)); + + // Check complex-complex operations. + auto cpc = c1 + c2; + assert(cpc.re == c1.re + c2.re); + assert(cpc.im == c1.im + c2.im); + + auto cmc = c1 - c2; + assert(cmc.re == c1.re - c2.re); + assert(cmc.im == c1.im - c2.im); + + auto ctc = c1 * c2; + assert(ctc == _cdouble(-1.5, 2.5)); + + auto cdc = c1 / c2; + assert(abs(cdc.re - 0.5882352941177) < 1e-12); + assert(abs(cdc.im - -0.3529411764706) < 1e-12); + + // Check complex-real operations. + double a = 123.456; + + auto cpr = c1 + a; + assert(cpr.re == c1.re + a); + assert(cpr.im == c1.im); + + auto cmr = c1 - a; + assert(cmr.re == c1.re - a); + assert(cmr.im == c1.im); + + auto ctr = c1 * a; + assert(ctr.re == c1.re*a); + assert(ctr.im == c1.im*a); + + auto cdr = c1 / a; + assert(abs(cdr.re - 0.00810005184033) < 1e-12); + assert(abs(cdr.im - 0.00810005184033) < 1e-12); + + auto rpc = a + c1; + assert(rpc == cpr); + + auto rmc = a - c1; + assert(rmc.re == a-c1.re); + assert(rmc.im == -c1.im); + + auto rtc = a * c1; + assert(rtc == ctr); + + auto rdc = a / c1; + assert(abs(rdc.re - 61.728) < 1e-12); + assert(abs(rdc.im - -61.728) < 1e-12); + + rdc = a / c2; + assert(abs(rdc.re - 14.5242352941) < 1e-10); + assert(abs(rdc.im - -58.0969411765) < 1e-10); + + // Check operations between different complex types. + auto cf = _cfloat(1.0, 1.0); + auto cr = _creal(1.0, 1.0); + auto c1pcf = c1 + cf; + auto c1pcr = c1 + cr; + static assert(is(typeof(c1pcf) == _cdouble)); + static assert(is(typeof(c1pcr) == _creal)); + assert(c1pcf.re == c1pcr.re); + assert(c1pcf.im == c1pcr.im); + + auto c1c = c1; + auto c2c = c2; + + c1c /= c1; + assert(abs(c1c.re - 1.0) < 1e-10); + assert(abs(c1c.im - 0.0) < 1e-10); + + c1c = c1; + c1c /= c2; + assert(abs(c1c.re - 0.5882352941177) < 1e-12); + assert(abs(c1c.im - -0.3529411764706) < 1e-12); + + c2c /= c1; + assert(abs(c2c.re - 1.25) < 1e-11); + assert(abs(c2c.im - 0.75) < 1e-12); + + c2c = c2; + c2c /= c2; + assert(abs(c2c.re - 1.0) < 1e-11); + assert(abs(c2c.im - 0.0) < 1e-12); +} + +@safe pure nothrow unittest +{ + // Initialization + _cdouble a = _cdouble(1, 0); + assert(a.re == 1 && a.im == 0); + _cdouble b = _cdouble(1.0, 0); + assert(b.re == 1.0 && b.im == 0); +// _cdouble c = _creal(1.0, 2); +// assert(c.re == 1.0 && c.im == 2); +} + +@safe pure nothrow unittest +{ + // Assignments and comparisons + _cdouble z; + + z = 1; + assert(z == 1); + assert(z.re == 1.0 && z.im == 0.0); + + z = 2.0; + assert(z == 2.0); + assert(z.re == 2.0 && z.im == 0.0); + + z = 1.0L; + assert(z == 1.0L); + assert(z.re == 1.0 && z.im == 0.0); + + auto w = _creal(1.0, 1.0); + z = w; + assert(z == w); + assert(z.re == 1.0 && z.im == 1.0); + + auto c = _cfloat(2.0, 2.0); + z = c; + assert(z == c); + assert(z.re == 2.0 && z.im == 2.0); +} + } From 36f36e3113cc58b8bbf6d631676fab9ea3a299f1 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 18 Mar 2023 11:06:20 +0000 Subject: [PATCH 044/301] [core.atomic] Add example (dlang/dmd!14998) * [core.atomic] Add example * Use NOTE instead of undefined WARNING macro --- runtime/druntime/src/core/atomic.d | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/runtime/druntime/src/core/atomic.d b/runtime/druntime/src/core/atomic.d index 4af3fdf2bd2..5f6f407bc35 100644 --- a/runtime/druntime/src/core/atomic.d +++ b/runtime/druntime/src/core/atomic.d @@ -2,6 +2,9 @@ * The atomic module provides basic support for lock-free * concurrent programming. * + * $(NOTE Use the `-preview=nosharedaccess` compiler flag to detect + * unsafe individual read or write operations on shared data.) + * * Copyright: Copyright Sean Kelly 2005 - 2016. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans @@ -10,6 +13,22 @@ module core.atomic; +/// +@safe unittest +{ + int y = 2; + shared int x = y; // OK + + //x++; // read modify write error + x.atomicOp!"+="(1); // OK + //y = x; // read error with preview flag + y = x.atomicLoad(); // OK + assert(y == 3); + //x = 5; // write error with preview flag + x.atomicStore(5); // OK + assert(x.atomicLoad() == 5); +} + import core.internal.atomic; import core.internal.attributes : betterC; import core.internal.traits : hasUnsharedIndirections; From 2aa6e952383f8fd5233168975176c37c329c0a0f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 19 Mar 2023 04:41:23 -0700 Subject: [PATCH 045/301] improve doc for parseAsm() (dlang/dmd!15006) --- dmd/parse.d | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dmd/parse.d b/dmd/parse.d index 65fc4e94261..fb660a0cf4c 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -6903,6 +6903,15 @@ LagainStc: /******************** * Parse inline assembler block. + * Enters with token on the `asm`. + * https://dlang.org/spec/iasm.html + * + * AsmStatement: + * asm FunctionAttributes(opt) { AsmInstructionListopt } + * AsmInstructionList: + * AsmInstruction ; + * AsmInstruction ; AsmInstruction + * * Returns: * inline assembler block as a Statement */ @@ -6917,7 +6926,7 @@ LagainStc: Loc labelloc; nextToken(); - StorageClass stc = parsePostfix(STC.undefined_, null); + StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild)) error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks"); From 0591347c6e55156dd5d664fa49b50eeef879ee6b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 19 Mar 2023 04:41:39 -0700 Subject: [PATCH 046/301] simple asm is actually Asm Label (dlang/dmd!15005) --- dmd/cparse.d | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 831c5eec7e7..1098a18c3c0 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -1756,7 +1756,7 @@ final class CParser(AST) : Parser!AST case TOK.asm_: case TOK.__attribute__: if (token.value == TOK.asm_) - asmName = cparseSimpleAsmExpr(); + asmName = cparseGnuAsmLabel(); if (token.value == TOK.__attribute__) { cparseGnuAttributes(specifier); @@ -3139,7 +3139,8 @@ final class CParser(AST) : Parser!AST } /************************* - * Simple asm parser + * Parser for asm label. It appears after the declarator, and has apparently + * nothing to do with inline assembler. * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html * simple-asm-expr: * asm ( asm-string-literal ) @@ -3147,12 +3148,12 @@ final class CParser(AST) : Parser!AST * asm-string-literal: * string-literal */ - private AST.StringExp cparseSimpleAsmExpr() + private AST.StringExp cparseGnuAsmLabel() { nextToken(); // move past asm check(TOK.leftParenthesis); if (token.value != TOK.string_) - error("string literal expected"); + error("string literal expected for Asm Label, not `%s`", token.toChars()); auto label = cparsePrimaryExp(); check(TOK.rightParenthesis); return cast(AST.StringExp) label; From 468f182caf93d63496af7fa4645bf1b3ab7f794e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 19 Mar 2023 21:42:06 -0700 Subject: [PATCH 047/301] ImportC: support casting of _Complex --- runtime/druntime/src/core/stdc/config.d | 7 +++++++ tests/dmd/compilable/testcomplex.i | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index 0c24dcf0cd4..3ab41f834da 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -277,6 +277,13 @@ struct _Complex(T) this(T re) { this.re = re; this.im = 0; } +/ + // Cast + R opCast(R)() + if (is(R == _Complex!float) || is(R == _Complex!double) || is(R == _Complex!c_long_double)) + { + return R(this.re, this.im); + } + // Assignment ref _Complex opAssign(_Complex!float c) { re = c.re; im = c.im; return this; } diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i index 61af9ca81d8..9946f26d562 100644 --- a/tests/dmd/compilable/testcomplex.i +++ b/tests/dmd/compilable/testcomplex.i @@ -19,3 +19,9 @@ _Complex long double testld() _Complex long double x = 1.0iL; return x; } + +_Complex float testcast() +{ + _Complex double y = 1.0i; + return (_Complex float)y; +} From 04fbb3e61f9fc23eb80ab63132cf98ecfb1feadc Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Mon, 20 Mar 2023 21:37:14 +0100 Subject: [PATCH 048/301] =?UTF-8?q?partial=20fix=20Issue=2022722=20-=20Imp?= =?UTF-8?q?ortC:=20parser=20doesn=E2=80=99t=20understand=20'asm=20volatile?= =?UTF-8?q?'=20syntax=20(dlang/dmd!14890)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dmd/cparse.d | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 1098a18c3c0..9094324441d 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -601,7 +601,22 @@ final class CParser(AST) : Parser!AST } case TOK.asm_: - s = parseAsm(); + switch (peekNext()) + { + case TOK.goto_: + case TOK.inline: + case TOK.volatile: + case TOK.leftParenthesis: + version (IN_GCC) + { + s = cparseAsm(); + break; + } + default: + // ImportC extensions: parse as a D asm block. + s = parseAsm(); + break; + } break; default: @@ -3159,6 +3174,83 @@ final class CParser(AST) : Parser!AST return cast(AST.StringExp) label; } + /******************** + * Parse C inline assembler statement. + * gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * Returns: + * inline assembler expression as a Statement + */ + private AST.Statement cparseAsm() + { + // Defer parsing of AsmStatements until semantic processing. + const loc = token.loc; + + nextToken(); + + // Consume all asm-qualifiers. As a future optimization, we could record + // the `inline` and `volatile` storage classes against the statement. + while (token.value == TOK.goto_ || + token.value == TOK.inline || + token.value == TOK.volatile) + nextToken(); + + check(TOK.leftParenthesis); + Token* toklist = null; + Token** ptoklist = &toklist; + Identifier label = null; + auto statements = new AST.Statements(); + while (1) + { + switch (token.value) + { + case TOK.rightParenthesis: + break; + + case TOK.semicolon: + error("matching `)` expected, not `;`"); + break; + + case TOK.endOfFile: + /* ( */ + error("matching `)` expected, not end of file"); + break; + + case TOK.colonColon: // treat as two separate : tokens for iasmgcc + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = null; + nextToken(); + continue; + + default: + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + nextToken(); + continue; + } + if (toklist) + { + // Create AsmStatement from list of tokens we've saved + AST.Statement s = new AST.AsmStatement(token.loc, toklist); + statements.push(s); + } + break; + } + nextToken(); + auto s = new AST.CompoundAsmStatement(loc, statements, 0); + return s; + } + /************************* * __attribute__ parser * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html From 6c29c108107bd3feab4ed1c0d8640740b6e9835e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 20 Mar 2023 15:37:53 -0700 Subject: [PATCH 049/301] fix buildkite failure (dlang/dmd!15010) --- dmd/cparse.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 9094324441d..d22733be5e4 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3197,7 +3197,7 @@ final class CParser(AST) : Parser!AST check(TOK.leftParenthesis); Token* toklist = null; Token** ptoklist = &toklist; - Identifier label = null; + //Identifier label = null; auto statements = new AST.Statements(); while (1) { From 236b6d62f34209bafb7041236da7a429b9ee7166 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 20 Mar 2023 17:17:49 -0700 Subject: [PATCH 050/301] minor improvements to cparseAsm() (dlang/dmd!15011) --- dmd/cparse.d | 16 ++++++++-------- runtime/druntime/src/importc.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index d22733be5e4..05d6cb15e9c 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -607,11 +607,9 @@ final class CParser(AST) : Parser!AST case TOK.inline: case TOK.volatile: case TOK.leftParenthesis: - version (IN_GCC) - { - s = cparseAsm(); - break; - } + s = cparseGnuAsm(); + break; + default: // ImportC extensions: parse as a D asm block. s = parseAsm(); @@ -3175,12 +3173,14 @@ final class CParser(AST) : Parser!AST } /******************** - * Parse C inline assembler statement. - * gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * Parse C inline assembler statement in Gnu format. + * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels ) + * Current token is on the `asm`. * Returns: * inline assembler expression as a Statement */ - private AST.Statement cparseAsm() + private AST.Statement cparseGnuAsm() { // Defer parsing of AsmStatements until semantic processing. const loc = token.loc; diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 5d50e541969..1ada6f0ea50 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -100,6 +100,7 @@ #endif #if __FreeBSD__ +#define __volatile volatile #endif #if _MSC_VER From 22d72f973b8b868f67fa26d584b7602a9ce5f198 Mon Sep 17 00:00:00 2001 From: Dennis Date: Tue, 21 Mar 2023 09:56:42 +0100 Subject: [PATCH 051/301] Remove `size` field from `Expression` (dlang/dmd!14989) * Remove unused `EXP` enum members * Add mapping from `EXP` to class size * Remove `size` field from `Expression` --- dmd/ctfeexpr.d | 8 +- dmd/dinterpret.d | 1 - dmd/dstruct.d | 1 - dmd/expression.d | 399 +++++++++++++++++++++++++++++++---------------- dmd/expression.h | 4 +- dmd/frontend.h | 253 +++++++++++++++--------------- dmd/hdrgen.d | 2 - dmd/parse.d | 2 - dmd/tokens.d | 9 -- dmd/tokens.h | 9 -- 10 files changed, 392 insertions(+), 296 deletions(-) diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index 8109e12d43d..1b93429b8a2 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -47,7 +47,7 @@ extern (C++) final class ClassReferenceExp : Expression extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) { - super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp)); + super(loc, EXP.classReference); assert(lit && lit.sd && lit.sd.isClassDeclaration()); this.value = lit; this.type = type; @@ -132,7 +132,7 @@ extern (C++) final class ThrownExceptionExp : Expression extern (D) this(const ref Loc loc, ClassReferenceExp victim) { - super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp)); + super(loc, EXP.thrownException); this.thrown = victim; this.type = victim.type; } @@ -170,7 +170,7 @@ extern (C++) final class CTFEExp : Expression { extern (D) this(EXP tok) { - super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp)); + super(Loc.initial, tok); type = Type.tvoid; } @@ -369,7 +369,6 @@ UnionExp copyLiteral(Expression e) case EXP.dotVariable: case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: case EXP.void_: case EXP.vector: @@ -1837,7 +1836,6 @@ bool isCtfeValueValid(Expression newval) { case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: return tb.isscalar(); diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index e6ef704be86..141574ff2e5 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -6978,7 +6978,6 @@ private Expression copyRegionExp(Expression e) case EXP.null_: case EXP.void_: case EXP.symbolOffset: - case EXP.char_: break; case EXP.cantExpression: diff --git a/dmd/dstruct.d b/dmd/dstruct.d index c71e4a7be00..67f29d28970 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -575,7 +575,6 @@ private bool _isZeroInit(Expression exp) return exp.toInteger() == 0; case EXP.null_: - case EXP.false_: return true; case EXP.structLiteral: diff --git a/dmd/expression.d b/dmd/expression.d index df5e9ddc98e..aa9d7497377 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -721,19 +721,20 @@ enum WANTexpand = 1; // expand const/immutable variables if possible extern (C++) abstract class Expression : ASTNode { const EXP op; // to minimize use of dynamic_cast - ubyte size; // # of bytes in Expression so we can copy() it bool parens; // if this is a parenthesized expression Type type; // !=null means that semantic() has been run Loc loc; // file location - extern (D) this(const ref Loc loc, EXP op, int size) scope + extern (D) this(const ref Loc loc, EXP op) scope { //printf("Expression::Expression(op = %d) this = %p\n", op, this); this.loc = loc; this.op = op; - this.size = cast(ubyte)size; } + /// Returns: class instance size of this expression (implemented manually because `extern(C++)`) + final size_t size() nothrow @nogc pure @safe const { return expSize[op]; } + static void _init() { CTFEExp.cantexp = new CTFEExp(EXP.cantExpression); @@ -1866,7 +1867,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(const ref Loc loc, dinteger_t value, Type type) { - super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(loc, EXP.int64); //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : ""); assert(type); if (!type.isscalar()) @@ -1882,7 +1883,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(dinteger_t value) { - super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(Loc.initial, EXP.int64); this.type = Type.tint32; this.value = cast(int)value; } @@ -2082,7 +2083,7 @@ extern (C++) final class ErrorExp : Expression { private extern (D) this() { - super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp)); + super(Loc.initial, EXP.error); type = Type.terror; } @@ -2130,7 +2131,7 @@ extern (C++) final class VoidInitExp : Expression extern (D) this(VarDeclaration var) { - super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp)); + super(var.loc, EXP.void_); this.var = var; this.type = var.type; } @@ -2156,7 +2157,7 @@ extern (C++) final class RealExp : Expression extern (D) this(const ref Loc loc, real_t value, Type type) { - super(loc, EXP.float64, __traits(classInstanceSize, RealExp)); + super(loc, EXP.float64); //printf("RealExp::RealExp(%Lg)\n", value); this.value = value; this.type = type; @@ -2239,7 +2240,7 @@ extern (C++) final class ComplexExp : Expression extern (D) this(const ref Loc loc, complex_t value, Type type) { - super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp)); + super(loc, EXP.complex80); this.value = value; this.type = type; //printf("ComplexExp::ComplexExp(%s)\n", toChars()); @@ -2330,7 +2331,7 @@ extern (C++) class IdentifierExp : Expression extern (D) this(const ref Loc loc, Identifier ident) scope { - super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp)); + super(loc, EXP.identifier); this.ident = ident; } @@ -2383,7 +2384,7 @@ extern (C++) final class DsymbolExp : Expression extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) { - super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp)); + super(loc, EXP.dSymbol); this.s = s; this.hasOverloads = hasOverloads; } @@ -2413,13 +2414,13 @@ extern (C++) class ThisExp : Expression extern (D) this(const ref Loc loc) { - super(loc, EXP.this_, __traits(classInstanceSize, ThisExp)); + super(loc, EXP.this_); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } this(const ref Loc loc, const EXP tok) { - super(loc, tok, __traits(classInstanceSize, ThisExp)); + super(loc, tok); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } @@ -2485,7 +2486,7 @@ extern (C++) final class NullExp : Expression { extern (D) this(const ref Loc loc, Type type = null) scope { - super(loc, EXP.null_, __traits(classInstanceSize, NullExp)); + super(loc, EXP.null_); this.type = type; } @@ -2544,7 +2545,7 @@ extern (C++) final class StringExp : Expression extern (D) this(const ref Loc loc, const(void)[] string) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = string.length; this.sz = 1; // work around LDC bug #1286 @@ -2552,7 +2553,7 @@ extern (C++) final class StringExp : Expression extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = len; this.sz = sz; @@ -2939,7 +2940,7 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.e0 = e0; this.exps = exps; @@ -2947,14 +2948,14 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.exps = exps; } extern (D) this(const ref Loc loc, TupleDeclaration tup) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); this.exps = new Expressions(); this.exps.reserve(tup.objects.length); @@ -3043,14 +3044,14 @@ extern (C++) final class ArrayLiteralExp : Expression extern (D) this(const ref Loc loc, Type type, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.elements = elements; } extern (D) this(const ref Loc loc, Type type, Expression e) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; elements = new Expressions(); elements.push(e); @@ -3058,7 +3059,7 @@ extern (C++) final class ArrayLiteralExp : Expression extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.basis = basis; this.elements = elements; @@ -3203,7 +3204,7 @@ extern (C++) final class AssocArrayLiteralExp : Expression extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) { - super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp)); + super(loc, EXP.assocArrayLiteral); assert(keys.length == values.length); this.keys = keys; this.values = values; @@ -3296,7 +3297,7 @@ extern (C++) final class StructLiteralExp : Expression extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) { - super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp)); + super(loc, EXP.structLiteral); this.sd = sd; if (!elements) elements = new Expressions(); @@ -3475,7 +3476,7 @@ extern (C++) final class CompoundLiteralExp : Expression extern (D) this(const ref Loc loc, Type type_name, Initializer initializer) { - super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp)); + super(loc, EXP.compoundLiteral); super.type = type_name; this.initializer = initializer; //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars()); @@ -3494,7 +3495,7 @@ extern (C++) final class TypeExp : Expression { extern (D) this(const ref Loc loc, Type type) { - super(loc, EXP.type, __traits(classInstanceSize, TypeExp)); + super(loc, EXP.type); //printf("TypeExp::TypeExp(%s)\n", type.toChars()); this.type = type; } @@ -3536,7 +3537,7 @@ extern (C++) final class ScopeExp : Expression extern (D) this(const ref Loc loc, ScopeDsymbol sds) { - super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp)); + super(loc, EXP.scope_); //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars()); //static int count; if (++count == 38) *(char*)0=0; this.sds = sds; @@ -3591,7 +3592,7 @@ extern (C++) final class TemplateExp : Expression extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) { - super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp)); + super(loc, EXP.template_); //printf("TemplateExp(): %s\n", td.toChars()); this.td = td; this.fd = fd; @@ -3652,7 +3653,7 @@ extern (C++) final class NewExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) { - super(loc, EXP.new_, __traits(classInstanceSize, NewExp)); + super(loc, EXP.new_); this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; @@ -3690,7 +3691,7 @@ extern (C++) final class NewAnonClassExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) { - super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp)); + super(loc, EXP.newAnonymousClass); this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -3715,9 +3716,9 @@ extern (C++) class SymbolExp : Expression Dsymbol originalScope; // original scope before inlining bool hasOverloads; - extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads) + extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads) { - super(loc, op, size); + super(loc, op); assert(var); this.var = var; this.hasOverloads = hasOverloads; @@ -3746,7 +3747,7 @@ extern (C++) final class SymOffExp : SymbolExp .error(loc, "need `this` for address of `%s`", v.toChars()); hasOverloads = false; } - super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads); + super(loc, EXP.symbolOffset, var, hasOverloads); this.offset = offset; } @@ -3772,7 +3773,7 @@ extern (C++) final class VarExp : SymbolExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads); + super(loc, EXP.variable, var, hasOverloads); //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars()); //if (strcmp(var.ident.toChars(), "func") == 0) assert(0); this.type = var.type; @@ -3856,7 +3857,7 @@ extern (C++) final class OverExp : Expression extern (D) this(const ref Loc loc, OverloadSet s) { - super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp)); + super(loc, EXP.overloadSet); //printf("OverExp(this = %p, '%s')\n", this, var.toChars()); vars = s; type = Type.tvoid; @@ -3890,7 +3891,7 @@ extern (C++) final class FuncExp : Expression extern (D) this(const ref Loc loc, Dsymbol s) { - super(loc, EXP.function_, __traits(classInstanceSize, FuncExp)); + super(loc, EXP.function_); this.td = s.isTemplateDeclaration(); this.fd = s.isFuncLiteralDeclaration(); if (td) @@ -4189,7 +4190,7 @@ extern (C++) final class DeclarationExp : Expression extern (D) this(const ref Loc loc, Dsymbol declaration) { - super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp)); + super(loc, EXP.declaration); this.declaration = declaration; } @@ -4222,7 +4223,7 @@ extern (C++) final class TypeidExp : Expression extern (D) this(const ref Loc loc, RootObject o) { - super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp)); + super(loc, EXP.typeid_); this.obj = o; } @@ -4247,7 +4248,7 @@ extern (C++) final class TraitsExp : Expression extern (D) this(const ref Loc loc, Identifier ident, Objects* args) { - super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp)); + super(loc, EXP.traits); this.ident = ident; this.args = args; } @@ -4272,7 +4273,7 @@ extern (C++) final class HaltExp : Expression { extern (D) this(const ref Loc loc) { - super(loc, EXP.halt, __traits(classInstanceSize, HaltExp)); + super(loc, EXP.halt); } override void accept(Visitor v) @@ -4296,7 +4297,7 @@ extern (C++) final class IsExp : Expression extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope { - super(loc, EXP.is_, __traits(classInstanceSize, IsExp)); + super(loc, EXP.is_); this.targ = targ; this.id = id; this.tok = tok; @@ -4334,9 +4335,9 @@ extern (C++) abstract class UnaExp : Expression Expression e1; Type att1; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; } @@ -4407,9 +4408,9 @@ extern (C++) abstract class BinExp : Expression Type att1; // Save alias this type to detect recursion Type att2; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; this.e2 = e2; } @@ -4698,9 +4699,9 @@ extern (C++) abstract class BinExp : Expression */ extern (C++) class BinAssignExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size, e1, e2); + super(loc, op, e1, e2); } override final bool isLvalue() @@ -4737,7 +4738,7 @@ extern (C++) final class MixinExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp)); + super(loc, EXP.mixin_); this.exps = exps; } @@ -4785,7 +4786,7 @@ extern (C++) final class ImportExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e); + super(loc, EXP.import_, e); } override void accept(Visitor v) @@ -4805,7 +4806,7 @@ extern (C++) final class AssertExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression msg = null) { - super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e); + super(loc, EXP.assert_, e); this.msg = msg; } @@ -4830,7 +4831,7 @@ extern (C++) final class ThrowExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.throw_, __traits(classInstanceSize, ThrowExp), e); + super(loc, EXP.throw_, e); this.type = Type.tnoreturn; } @@ -4856,7 +4857,7 @@ extern (C++) final class DotIdExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier ident) { - super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e); + super(loc, EXP.dotIdentifier, e); this.ident = ident; } @@ -4880,7 +4881,7 @@ extern (C++) final class DotTemplateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td) { - super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e); + super(loc, EXP.dotTemplateDeclaration, e); this.td = td; } @@ -4914,7 +4915,7 @@ extern (C++) final class DotVarExp : UnaExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e); + super(loc, EXP.dotVariable, e); //printf("DotVarExp()\n"); this.var = var; this.hasOverloads = hasOverloads; @@ -4995,14 +4996,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + super(loc, EXP.dotTemplateInstance, e); //printf("DotTemplateInstanceExp()\n"); this.ti = new TemplateInstance(loc, name, tiargs); } extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + super(loc, EXP.dotTemplateInstance, e); this.ti = ti; } @@ -5095,7 +5096,7 @@ extern (C++) final class DelegateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) { - super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e); + super(loc, EXP.delegate_, e); this.func = f; this.hasOverloads = hasOverloads; this.vthis2 = vthis2; @@ -5115,7 +5116,7 @@ extern (C++) final class DotTypeExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Dsymbol s) { - super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e); + super(loc, EXP.dotType, e); this.sym = s; } @@ -5169,19 +5170,19 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = exps; this.names = names; } extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); } extern (D) this(const ref Loc loc, Expression e, Expression earg1) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = new Expressions(); if (earg1) this.arguments.push(earg1); @@ -5189,7 +5190,7 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); auto arguments = new Expressions(2); (*arguments)[0] = earg1; (*arguments)[1] = earg2; @@ -5345,7 +5346,7 @@ extern (C++) final class AddrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e); + super(loc, EXP.address, e); } extern (D) this(const ref Loc loc, Expression e, Type t) @@ -5367,14 +5368,14 @@ extern (C++) final class PtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); //if (e.type) // type = ((TypePointer *)e.type).next; } extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); type = t; } @@ -5420,7 +5421,7 @@ extern (C++) final class NegExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e); + super(loc, EXP.negate, e); } override void accept(Visitor v) @@ -5436,7 +5437,7 @@ extern (C++) final class UAddExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) scope { - super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e); + super(loc, EXP.uadd, e); } override void accept(Visitor v) @@ -5452,7 +5453,7 @@ extern (C++) final class ComExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e); + super(loc, EXP.tilde, e); } override void accept(Visitor v) @@ -5468,7 +5469,7 @@ extern (C++) final class NotExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.not, __traits(classInstanceSize, NotExp), e); + super(loc, EXP.not, e); } override void accept(Visitor v) @@ -5488,7 +5489,7 @@ extern (C++) final class DeleteExp : UnaExp extern (D) this(const ref Loc loc, Expression e, bool isRAII) { - super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e); + super(loc, EXP.delete_, e); this.isRAII = isRAII; } @@ -5512,7 +5513,7 @@ extern (C++) final class CastExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.to = t; } @@ -5520,7 +5521,7 @@ extern (C++) final class CastExp : UnaExp */ extern (D) this(const ref Loc loc, Expression e, ubyte mod) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.mod = mod; } @@ -5574,7 +5575,7 @@ extern (C++) final class VectorExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e); + super(loc, EXP.vector, e); assert(t.ty == Tvector); to = cast(TypeVector)t; } @@ -5610,7 +5611,7 @@ extern (C++) final class VectorArrayExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1); + super(loc, EXP.vectorArray, e1); } override bool isLvalue() @@ -5648,14 +5649,14 @@ extern (C++) final class SliceExp : UnaExp /************************************************************/ extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + super(loc, EXP.slice, e1); this.upr = ie ? ie.upr : null; this.lwr = ie ? ie.lwr : null; } extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + super(loc, EXP.slice, e1); this.upr = upr; this.lwr = lwr; } @@ -5705,7 +5706,7 @@ extern (C++) final class ArrayLengthExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1); + super(loc, EXP.arrayLength, e1); } override void accept(Visitor v) @@ -5728,7 +5729,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expression index = null) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = new Expressions(); if (index) arguments.push(index); @@ -5736,7 +5737,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expressions* args) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = args; } @@ -5773,7 +5774,7 @@ extern (C++) final class DotExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2); + super(loc, EXP.dot, e1, e2); } override void accept(Visitor v) @@ -5799,7 +5800,7 @@ extern (C++) final class CommaExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true) { - super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2); + super(loc, EXP.comma, e1, e2); allowCommaExp = isGenerated = generated; } @@ -5868,7 +5869,7 @@ extern (C++) final class IntervalExp : Expression extern (D) this(const ref Loc loc, Expression lwr, Expression upr) { - super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp)); + super(loc, EXP.interval); this.lwr = lwr; this.upr = upr; } @@ -5893,7 +5894,7 @@ extern (C++) final class DelegatePtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1); + super(loc, EXP.delegatePointer, e1); } override bool isLvalue() @@ -5931,7 +5932,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1); + super(loc, EXP.delegateFunctionPointer, e1); } override bool isLvalue() @@ -5971,13 +5972,13 @@ extern (C++) final class IndexExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + super(loc, EXP.index, e1, e2); //printf("IndexExp::IndexExp('%s')\n", toChars()); } extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + super(loc, EXP.index, e1, e2); this.indexIsInBounds = indexIsInBounds; //printf("IndexExp::IndexExp('%s')\n", toChars()); } @@ -6054,7 +6055,7 @@ extern (C++) final class PostExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1); + super(loc, op, e, IntegerExp.literal!1); assert(op == EXP.minusMinus || op == EXP.plusPlus); } @@ -6071,7 +6072,7 @@ extern (C++) final class PreExp : UnaExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PreExp), e); + super(loc, op, e); assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus); } @@ -6101,12 +6102,12 @@ extern (C++) class AssignExp : BinExp /* op can be EXP.assign, EXP.construct, or EXP.blit */ extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, EXP.assign, e1, e2); } this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, tok, e1, e2); } override final bool isLvalue() @@ -6204,7 +6205,7 @@ extern (C++) final class AddAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2); + super(loc, EXP.addAssign, e1, e2); } override void accept(Visitor v) @@ -6220,7 +6221,7 @@ extern (C++) final class MinAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2); + super(loc, EXP.minAssign, e1, e2); } override void accept(Visitor v) @@ -6236,7 +6237,7 @@ extern (C++) final class MulAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2); + super(loc, EXP.mulAssign, e1, e2); } override void accept(Visitor v) @@ -6252,7 +6253,7 @@ extern (C++) final class DivAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2); + super(loc, EXP.divAssign, e1, e2); } override void accept(Visitor v) @@ -6268,7 +6269,7 @@ extern (C++) final class ModAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2); + super(loc, EXP.modAssign, e1, e2); } override void accept(Visitor v) @@ -6284,7 +6285,7 @@ extern (C++) final class AndAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2); + super(loc, EXP.andAssign, e1, e2); } override void accept(Visitor v) @@ -6300,7 +6301,7 @@ extern (C++) final class OrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2); + super(loc, EXP.orAssign, e1, e2); } override void accept(Visitor v) @@ -6316,7 +6317,7 @@ extern (C++) final class XorAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2); + super(loc, EXP.xorAssign, e1, e2); } override void accept(Visitor v) @@ -6332,7 +6333,7 @@ extern (C++) final class PowAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2); + super(loc, EXP.powAssign, e1, e2); } override void accept(Visitor v) @@ -6348,7 +6349,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2); + super(loc, EXP.leftShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6364,7 +6365,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2); + super(loc, EXP.rightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6380,7 +6381,7 @@ extern (C++) final class UshrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2); + super(loc, EXP.unsignedRightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6405,12 +6406,12 @@ extern (C++) class CatAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, EXP.concatenateAssign, e1, e2); } extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, tok, e1, e2); } override void accept(Visitor v) @@ -6462,7 +6463,7 @@ extern (C++) final class AddExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2); + super(loc, EXP.add, e1, e2); } override void accept(Visitor v) @@ -6480,7 +6481,7 @@ extern (C++) final class MinExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2); + super(loc, EXP.min, e1, e2); } override void accept(Visitor v) @@ -6498,7 +6499,7 @@ extern (C++) final class CatExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope { - super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2); + super(loc, EXP.concatenate, e1, e2); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -6523,7 +6524,7 @@ extern (C++) final class MulExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2); + super(loc, EXP.mul, e1, e2); } override void accept(Visitor v) @@ -6541,7 +6542,7 @@ extern (C++) final class DivExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2); + super(loc, EXP.div, e1, e2); } override void accept(Visitor v) @@ -6559,7 +6560,7 @@ extern (C++) final class ModExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2); + super(loc, EXP.mod, e1, e2); } override void accept(Visitor v) @@ -6577,7 +6578,7 @@ extern (C++) final class PowExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2); + super(loc, EXP.pow, e1, e2); } override void accept(Visitor v) @@ -6595,7 +6596,7 @@ extern (C++) final class ShlExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2); + super(loc, EXP.leftShift, e1, e2); } override void accept(Visitor v) @@ -6613,7 +6614,7 @@ extern (C++) final class ShrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2); + super(loc, EXP.rightShift, e1, e2); } override void accept(Visitor v) @@ -6631,7 +6632,7 @@ extern (C++) final class UshrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2); + super(loc, EXP.unsignedRightShift, e1, e2); } override void accept(Visitor v) @@ -6649,7 +6650,7 @@ extern (C++) final class AndExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2); + super(loc, EXP.and, e1, e2); } override void accept(Visitor v) @@ -6667,7 +6668,7 @@ extern (C++) final class OrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2); + super(loc, EXP.or, e1, e2); } override void accept(Visitor v) @@ -6685,7 +6686,7 @@ extern (C++) final class XorExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2); + super(loc, EXP.xor, e1, e2); } override void accept(Visitor v) @@ -6704,7 +6705,7 @@ extern (C++) final class LogicalExp : BinExp { extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.andAnd || op == EXP.orOr); } @@ -6726,7 +6727,7 @@ extern (C++) final class CmpExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual); } @@ -6747,7 +6748,7 @@ extern (C++) final class InExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2); + super(loc, EXP.in_, e1, e2); } override void accept(Visitor v) @@ -6765,7 +6766,7 @@ extern (C++) final class RemoveExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2); + super(loc, EXP.remove, e1, e2); type = Type.tbool; } @@ -6786,7 +6787,7 @@ extern (C++) final class EqualExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.equal || op == EXP.notEqual); } @@ -6807,7 +6808,7 @@ extern (C++) final class IdentityExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.identity || op == EXP.notIdentity); } @@ -6828,7 +6829,7 @@ extern (C++) final class CondExp : BinExp extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope { - super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2); + super(loc, EXP.question, e1, e2); this.econd = econd; } @@ -6966,9 +6967,9 @@ bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc */ extern (C++) class DefaultInitExp : Expression { - extern (D) this(const ref Loc loc, EXP op, int size) + extern (D) this(const ref Loc loc, EXP op) { - super(loc, op, size); + super(loc, op); } override void accept(Visitor v) @@ -6984,7 +6985,7 @@ extern (C++) final class FileInitExp : DefaultInitExp { extern (D) this(const ref Loc loc, EXP tok) { - super(loc, tok, __traits(classInstanceSize, FileInitExp)); + super(loc, tok); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7015,7 +7016,7 @@ extern (C++) final class LineInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.line, __traits(classInstanceSize, LineInitExp)); + super(loc, EXP.line); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7038,7 +7039,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp)); + super(loc, EXP.moduleString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7063,7 +7064,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp)); + super(loc, EXP.functionString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7094,7 +7095,7 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp)); + super(loc, EXP.prettyFunction); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7139,8 +7140,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) { - super(loc, EXP.objcClassReference, - __traits(classInstanceSize, ObjcClassReferenceExp)); + super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; type = objc.getRuntimeMetaclass(classDeclaration).getType(); } @@ -7163,7 +7163,7 @@ extern (C++) final class GenericExp : Expression extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps) { - super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp)); + super(loc, EXP._Generic); this.cntlExp = cntlExp; this.types = types; this.exps = exps; @@ -7398,3 +7398,134 @@ private enum EbinaryAssign = EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign, EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign, ]; + +/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class. +/// Needed because the classes are `extern(C++)` +private immutable ubyte[EXP.max+1] expSize = [ + EXP.reserved: 0, + EXP.negate: __traits(classInstanceSize, NegExp), + EXP.cast_: __traits(classInstanceSize, CastExp), + EXP.null_: __traits(classInstanceSize, NullExp), + EXP.assert_: __traits(classInstanceSize, AssertExp), + EXP.array: __traits(classInstanceSize, ArrayExp), + EXP.call: __traits(classInstanceSize, CallExp), + EXP.address: __traits(classInstanceSize, AddrExp), + EXP.type: __traits(classInstanceSize, TypeExp), + EXP.throw_: __traits(classInstanceSize, ThrowExp), + EXP.new_: __traits(classInstanceSize, NewExp), + EXP.delete_: __traits(classInstanceSize, DeleteExp), + EXP.star: __traits(classInstanceSize, PtrExp), + EXP.symbolOffset: __traits(classInstanceSize, SymOffExp), + EXP.variable: __traits(classInstanceSize, VarExp), + EXP.dotVariable: __traits(classInstanceSize, DotVarExp), + EXP.dotIdentifier: __traits(classInstanceSize, DotIdExp), + EXP.dotTemplateInstance: __traits(classInstanceSize, DotTemplateInstanceExp), + EXP.dotType: __traits(classInstanceSize, DotTypeExp), + EXP.slice: __traits(classInstanceSize, SliceExp), + EXP.arrayLength: __traits(classInstanceSize, ArrayLengthExp), + EXP.dollar: __traits(classInstanceSize, DollarExp), + EXP.template_: __traits(classInstanceSize, TemplateExp), + EXP.dotTemplateDeclaration: __traits(classInstanceSize, DotTemplateExp), + EXP.declaration: __traits(classInstanceSize, DeclarationExp), + EXP.dSymbol: __traits(classInstanceSize, DsymbolExp), + EXP.typeid_: __traits(classInstanceSize, TypeidExp), + EXP.uadd: __traits(classInstanceSize, UAddExp), + EXP.remove: __traits(classInstanceSize, RemoveExp), + EXP.newAnonymousClass: __traits(classInstanceSize, NewAnonClassExp), + EXP.arrayLiteral: __traits(classInstanceSize, ArrayLiteralExp), + EXP.assocArrayLiteral: __traits(classInstanceSize, AssocArrayLiteralExp), + EXP.structLiteral: __traits(classInstanceSize, StructLiteralExp), + EXP.classReference: __traits(classInstanceSize, ClassReferenceExp), + EXP.thrownException: __traits(classInstanceSize, ThrownExceptionExp), + EXP.delegatePointer: __traits(classInstanceSize, DelegatePtrExp), + EXP.delegateFunctionPointer: __traits(classInstanceSize, DelegateFuncptrExp), + EXP.lessThan: __traits(classInstanceSize, CmpExp), + EXP.greaterThan: __traits(classInstanceSize, CmpExp), + EXP.lessOrEqual: __traits(classInstanceSize, CmpExp), + EXP.greaterOrEqual: __traits(classInstanceSize, CmpExp), + EXP.equal: __traits(classInstanceSize, EqualExp), + EXP.notEqual: __traits(classInstanceSize, EqualExp), + EXP.identity: __traits(classInstanceSize, IdentityExp), + EXP.notIdentity: __traits(classInstanceSize, IdentityExp), + EXP.index: __traits(classInstanceSize, IndexExp), + EXP.is_: __traits(classInstanceSize, IsExp), + EXP.leftShift: __traits(classInstanceSize, ShlExp), + EXP.rightShift: __traits(classInstanceSize, ShrExp), + EXP.leftShiftAssign: __traits(classInstanceSize, ShlAssignExp), + EXP.rightShiftAssign: __traits(classInstanceSize, ShrAssignExp), + EXP.unsignedRightShift: __traits(classInstanceSize, UshrExp), + EXP.unsignedRightShiftAssign: __traits(classInstanceSize, UshrAssignExp), + EXP.concatenate: __traits(classInstanceSize, CatExp), + EXP.concatenateAssign: __traits(classInstanceSize, CatAssignExp), + EXP.concatenateElemAssign: __traits(classInstanceSize, CatElemAssignExp), + EXP.concatenateDcharAssign: __traits(classInstanceSize, CatDcharAssignExp), + EXP.add: __traits(classInstanceSize, AddExp), + EXP.min: __traits(classInstanceSize, MinExp), + EXP.addAssign: __traits(classInstanceSize, AddAssignExp), + EXP.minAssign: __traits(classInstanceSize, MinAssignExp), + EXP.mul: __traits(classInstanceSize, MulExp), + EXP.div: __traits(classInstanceSize, DivExp), + EXP.mod: __traits(classInstanceSize, ModExp), + EXP.mulAssign: __traits(classInstanceSize, MulAssignExp), + EXP.divAssign: __traits(classInstanceSize, DivAssignExp), + EXP.modAssign: __traits(classInstanceSize, ModAssignExp), + EXP.and: __traits(classInstanceSize, AndExp), + EXP.or: __traits(classInstanceSize, OrExp), + EXP.xor: __traits(classInstanceSize, XorExp), + EXP.andAssign: __traits(classInstanceSize, AndAssignExp), + EXP.orAssign: __traits(classInstanceSize, OrAssignExp), + EXP.xorAssign: __traits(classInstanceSize, XorAssignExp), + EXP.assign: __traits(classInstanceSize, AssignExp), + EXP.not: __traits(classInstanceSize, NotExp), + EXP.tilde: __traits(classInstanceSize, ComExp), + EXP.plusPlus: __traits(classInstanceSize, PostExp), + EXP.minusMinus: __traits(classInstanceSize, PostExp), + EXP.construct: __traits(classInstanceSize, ConstructExp), + EXP.blit: __traits(classInstanceSize, BlitExp), + EXP.dot: __traits(classInstanceSize, DotExp), + EXP.comma: __traits(classInstanceSize, CommaExp), + EXP.question: __traits(classInstanceSize, CondExp), + EXP.andAnd: __traits(classInstanceSize, LogicalExp), + EXP.orOr: __traits(classInstanceSize, LogicalExp), + EXP.prePlusPlus: __traits(classInstanceSize, PreExp), + EXP.preMinusMinus: __traits(classInstanceSize, PreExp), + EXP.identifier: __traits(classInstanceSize, IdentifierExp), + EXP.string_: __traits(classInstanceSize, StringExp), + EXP.this_: __traits(classInstanceSize, ThisExp), + EXP.super_: __traits(classInstanceSize, SuperExp), + EXP.halt: __traits(classInstanceSize, HaltExp), + EXP.tuple: __traits(classInstanceSize, TupleExp), + EXP.error: __traits(classInstanceSize, ErrorExp), + EXP.void_: __traits(classInstanceSize, VoidInitExp), + EXP.int64: __traits(classInstanceSize, IntegerExp), + EXP.float64: __traits(classInstanceSize, RealExp), + EXP.complex80: __traits(classInstanceSize, ComplexExp), + EXP.import_: __traits(classInstanceSize, ImportExp), + EXP.delegate_: __traits(classInstanceSize, DelegateExp), + EXP.function_: __traits(classInstanceSize, FuncExp), + EXP.mixin_: __traits(classInstanceSize, MixinExp), + EXP.in_: __traits(classInstanceSize, InExp), + EXP.break_: __traits(classInstanceSize, CTFEExp), + EXP.continue_: __traits(classInstanceSize, CTFEExp), + EXP.goto_: __traits(classInstanceSize, CTFEExp), + EXP.scope_: __traits(classInstanceSize, ScopeExp), + EXP.traits: __traits(classInstanceSize, TraitsExp), + EXP.overloadSet: __traits(classInstanceSize, OverExp), + EXP.line: __traits(classInstanceSize, LineInitExp), + EXP.file: __traits(classInstanceSize, FileInitExp), + EXP.fileFullPath: __traits(classInstanceSize, FileInitExp), + EXP.moduleString: __traits(classInstanceSize, ModuleInitExp), + EXP.functionString: __traits(classInstanceSize, FuncInitExp), + EXP.prettyFunction: __traits(classInstanceSize, PrettyFuncInitExp), + EXP.pow: __traits(classInstanceSize, PowExp), + EXP.powAssign: __traits(classInstanceSize, PowAssignExp), + EXP.vector: __traits(classInstanceSize, VectorExp), + EXP.voidExpression: __traits(classInstanceSize, CTFEExp), + EXP.cantExpression: __traits(classInstanceSize, CTFEExp), + EXP.showCtfeContext: __traits(classInstanceSize, CTFEExp), + EXP.objcClassReference: __traits(classInstanceSize, ObjcClassReferenceExp), + EXP.vectorArray: __traits(classInstanceSize, VectorArrayExp), + EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp), + EXP._Generic: __traits(classInstanceSize, GenericExp), + EXP.interval: __traits(classInstanceSize, IntervalExp), +]; diff --git a/dmd/expression.h b/dmd/expression.h index 1bc78e7ef2a..8f0df2b7352 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -80,11 +80,11 @@ class Expression : public ASTNode { public: EXP op; // to minimize use of dynamic_cast - unsigned char size; // # of bytes in Expression so we can copy() it d_bool parens; // if this is a parenthesized expression Type *type; // !=NULL means that semantic() has been run Loc loc; // file location + size_t size() const; static void _init(); Expression *copy(); virtual Expression *syntaxCopy(); @@ -1372,7 +1372,7 @@ struct UnionExp UnionExp(Expression *e) { - memcpy(this, (void *)e, e->size); + memcpy(this, (void *)e, e->size()); } /* Extract pointer to Expression diff --git a/dmd/frontend.h b/dmd/frontend.h index e0ee8bfb1e3..63904fb5aca 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -796,136 +796,127 @@ enum class EXP : uint8_t cast_ = 2u, null_ = 3u, assert_ = 4u, - true_ = 5u, - false_ = 6u, - array = 7u, - call = 8u, - address = 9u, - type = 10u, - throw_ = 11u, - new_ = 12u, - delete_ = 13u, - star = 14u, - symbolOffset = 15u, - variable = 16u, - dotVariable = 17u, - dotIdentifier = 18u, - dotTemplateInstance = 19u, - dotType = 20u, - slice = 21u, - arrayLength = 22u, - version_ = 23u, - dollar = 24u, - template_ = 25u, - dotTemplateDeclaration = 26u, - declaration = 27u, - typeof_ = 28u, - pragma_ = 29u, - dSymbol = 30u, - typeid_ = 31u, - uadd = 32u, - remove = 33u, - newAnonymousClass = 34u, - arrayLiteral = 35u, - assocArrayLiteral = 36u, - structLiteral = 37u, - classReference = 38u, - thrownException = 39u, - delegatePointer = 40u, - delegateFunctionPointer = 41u, - lessThan = 42u, - greaterThan = 43u, - lessOrEqual = 44u, - greaterOrEqual = 45u, - equal = 46u, - notEqual = 47u, - identity = 48u, - notIdentity = 49u, - index = 50u, - is_ = 51u, - leftShift = 52u, - rightShift = 53u, - leftShiftAssign = 54u, - rightShiftAssign = 55u, - unsignedRightShift = 56u, - unsignedRightShiftAssign = 57u, - concatenate = 58u, - concatenateAssign = 59u, - concatenateElemAssign = 60u, - concatenateDcharAssign = 61u, - add = 62u, - min = 63u, - addAssign = 64u, - minAssign = 65u, - mul = 66u, - div = 67u, - mod = 68u, - mulAssign = 69u, - divAssign = 70u, - modAssign = 71u, - and_ = 72u, - or_ = 73u, - xor_ = 74u, - andAssign = 75u, - orAssign = 76u, - xorAssign = 77u, - assign = 78u, - not_ = 79u, - tilde = 80u, - plusPlus = 81u, - minusMinus = 82u, - construct = 83u, - blit = 84u, - dot = 85u, - comma = 86u, - question = 87u, - andAnd = 88u, - orOr = 89u, - prePlusPlus = 90u, - preMinusMinus = 91u, - identifier = 92u, - string_ = 93u, - this_ = 94u, - super_ = 95u, - halt = 96u, - tuple = 97u, - error = 98u, - void_ = 99u, - int64 = 100u, - float64 = 101u, - complex80 = 102u, - char_ = 103u, - import_ = 104u, - delegate_ = 105u, - function_ = 106u, - mixin_ = 107u, - in_ = 108u, - default_ = 109u, - break_ = 110u, - continue_ = 111u, - goto_ = 112u, - scope_ = 113u, - traits = 114u, - overloadSet = 115u, - line = 116u, - file = 117u, - fileFullPath = 118u, - moduleString = 119u, - functionString = 120u, - prettyFunction = 121u, - shared_ = 122u, - pow = 123u, - powAssign = 124u, - vector = 125u, - voidExpression = 126u, - cantExpression = 127u, - showCtfeContext = 128u, - objcClassReference = 129u, - vectorArray = 130u, - arrow = 131u, - compoundLiteral = 132u, - _Generic = 133u, - interval = 134u, + array = 5u, + call = 6u, + address = 7u, + type = 8u, + throw_ = 9u, + new_ = 10u, + delete_ = 11u, + star = 12u, + symbolOffset = 13u, + variable = 14u, + dotVariable = 15u, + dotIdentifier = 16u, + dotTemplateInstance = 17u, + dotType = 18u, + slice = 19u, + arrayLength = 20u, + dollar = 21u, + template_ = 22u, + dotTemplateDeclaration = 23u, + declaration = 24u, + dSymbol = 25u, + typeid_ = 26u, + uadd = 27u, + remove = 28u, + newAnonymousClass = 29u, + arrayLiteral = 30u, + assocArrayLiteral = 31u, + structLiteral = 32u, + classReference = 33u, + thrownException = 34u, + delegatePointer = 35u, + delegateFunctionPointer = 36u, + lessThan = 37u, + greaterThan = 38u, + lessOrEqual = 39u, + greaterOrEqual = 40u, + equal = 41u, + notEqual = 42u, + identity = 43u, + notIdentity = 44u, + index = 45u, + is_ = 46u, + leftShift = 47u, + rightShift = 48u, + leftShiftAssign = 49u, + rightShiftAssign = 50u, + unsignedRightShift = 51u, + unsignedRightShiftAssign = 52u, + concatenate = 53u, + concatenateAssign = 54u, + concatenateElemAssign = 55u, + concatenateDcharAssign = 56u, + add = 57u, + min = 58u, + addAssign = 59u, + minAssign = 60u, + mul = 61u, + div = 62u, + mod = 63u, + mulAssign = 64u, + divAssign = 65u, + modAssign = 66u, + and_ = 67u, + or_ = 68u, + xor_ = 69u, + andAssign = 70u, + orAssign = 71u, + xorAssign = 72u, + assign = 73u, + not_ = 74u, + tilde = 75u, + plusPlus = 76u, + minusMinus = 77u, + construct = 78u, + blit = 79u, + dot = 80u, + comma = 81u, + question = 82u, + andAnd = 83u, + orOr = 84u, + prePlusPlus = 85u, + preMinusMinus = 86u, + identifier = 87u, + string_ = 88u, + this_ = 89u, + super_ = 90u, + halt = 91u, + tuple = 92u, + error = 93u, + void_ = 94u, + int64 = 95u, + float64 = 96u, + complex80 = 97u, + import_ = 98u, + delegate_ = 99u, + function_ = 100u, + mixin_ = 101u, + in_ = 102u, + break_ = 103u, + continue_ = 104u, + goto_ = 105u, + scope_ = 106u, + traits = 107u, + overloadSet = 108u, + line = 109u, + file = 110u, + fileFullPath = 111u, + moduleString = 112u, + functionString = 113u, + prettyFunction = 114u, + pow = 115u, + powAssign = 116u, + vector = 117u, + voidExpression = 118u, + cantExpression = 119u, + showCtfeContext = 120u, + objcClassReference = 121u, + vectorArray = 122u, + compoundLiteral = 123u, + _Generic = 124u, + interval = 125u, }; typedef uint64_t dinteger_t; @@ -968,10 +959,10 @@ class Expression : public ASTNode { public: const EXP op; - uint8_t size; bool parens; Type* type; Loc loc; + size_t size() const; static void _init(); static void deinitialize(); Expression* copy(); diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index f2c3a54f2f6..c0fbf1ebe48 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -4091,7 +4091,6 @@ string EXPtoString(EXP op) EXP.error : "error", EXP.objcClassReference : "class", - EXP.typeof_ : "typeof", EXP.mixin_ : "mixin", EXP.import_ : "import", @@ -4131,7 +4130,6 @@ string EXPtoString(EXP op) EXP.remove : "remove", EXP.tuple : "tuple", EXP.traits : "__traits", - EXP.default_ : "default", EXP.overloadSet : "__overloadset", EXP.void_ : "void", EXP.vectorArray : "vectorarray", diff --git a/dmd/parse.d b/dmd/parse.d index fb660a0cf4c..9a2448fb57d 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -9519,7 +9519,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.error : PREC.expr, EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type - EXP.typeof_ : PREC.primary, EXP.mixin_ : PREC.primary, EXP.import_ : PREC.primary, @@ -9559,7 +9558,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.remove : PREC.primary, EXP.tuple : PREC.primary, EXP.traits : PREC.primary, - EXP.default_ : PREC.primary, EXP.overloadSet : PREC.primary, EXP.void_ : PREC.primary, EXP.vectorArray : PREC.primary, diff --git a/dmd/tokens.d b/dmd/tokens.d index aec3a77dee8..f2701fe42ee 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -289,8 +289,6 @@ enum EXP : ubyte cast_, null_, assert_, - true_, - false_, array, call, address, @@ -307,13 +305,10 @@ enum EXP : ubyte dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -394,13 +389,11 @@ enum EXP : ubyte int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -414,7 +407,6 @@ enum EXP : ubyte moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -424,7 +416,6 @@ enum EXP : ubyte showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic, interval, diff --git a/dmd/tokens.h b/dmd/tokens.h index 87361f327a4..404c6330675 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -299,8 +299,6 @@ enum class EXP : unsigned char cast_, null_, assert_, - true_, - false_, array, call, address, @@ -317,13 +315,10 @@ enum class EXP : unsigned char dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -404,13 +399,11 @@ enum class EXP : unsigned char int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -424,7 +417,6 @@ enum class EXP : unsigned char moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -434,7 +426,6 @@ enum class EXP : unsigned char showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic_, interval, From 4a66c59a5a1840b55137c9272c6df45cecdff065 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Tue, 21 Mar 2023 13:20:46 +0100 Subject: [PATCH 052/301] Change `StringExp.committed` to `bool` --- dmd/constfold.d | 2 +- dmd/cparse.d | 2 +- dmd/ctfeexpr.d | 4 ++-- dmd/dcast.d | 4 ++-- dmd/expression.d | 14 ++++++++++++-- dmd/expression.h | 2 +- dmd/expressionsem.d | 6 +++--- dmd/frontend.h | 2 +- 8 files changed, 23 insertions(+), 13 deletions(-) diff --git a/dmd/constfold.d b/dmd/constfold.d index e4be63cda3e..415606bd704 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -1437,7 +1437,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.type = type; - es.committed = 1; + es.committed = true; } else { diff --git a/dmd/cparse.d b/dmd/cparse.d index 05d6cb15e9c..ebc2b73c10c 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -4916,7 +4916,7 @@ final class CParser(AST) : Parser!AST auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn); efn.type = tfn.immutableOf(); - efn.committed = 1; + efn.committed = true; auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_); auto e = new AST.DeclarationExp(loc, sfn); return new AST.ExpStatement(loc, e); diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index 1b93429b8a2..289ebeb81ca 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -1467,7 +1467,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) memset(cast(char*)s + len * sz, 0, sz); emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); - es.committed = 0; + es.committed = false; es.type = type; return ue; } @@ -1498,7 +1498,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.sz = sz; - es.committed = 0; //es1.committed; + es.committed = false; //es1.committed; es.type = type; return ue; } diff --git a/dmd/dcast.d b/dmd/dcast.d index 2830b25d651..8ffbef3c966 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -1845,7 +1845,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (!e.committed) { se = e.copy().isStringExp(); - se.committed = 1; + se.committed = true; copied = 1; } @@ -1887,7 +1887,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) assert(szx <= 255); se.sz = cast(ubyte)szx; se.len = cast(size_t)tb.isTypeSArray().dim.toInteger(); - se.committed = 1; + se.committed = true; se.type = t; /* If larger than source, pad with zeros. diff --git a/dmd/expression.d b/dmd/expression.d index aa9d7497377..4d7d5b281b6 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -2538,7 +2538,17 @@ extern (C++) final class StringExp : Expression } // (const if ownedByCtfe == OwnedBy.code) size_t len; // number of code units ubyte sz = 1; // 1: char, 2: wchar, 4: dchar - ubyte committed; // !=0 if type is committed + + /** + * Whether the string literal's type is fixed + * Example: + * --- + * wstring x = "abc"; // OK, string literal is flexible + * wstring y = cast(string) "abc"; // Error: type was committed after cast + * --- + */ + bool committed; + enum char NoPostfix = 0; char postfix = NoPostfix; // 'c', 'w', 'd' OwnedBy ownedByCtfe = OwnedBy.code; @@ -2751,7 +2761,7 @@ extern (C++) final class StringExp : Expression if (sz != 1) { // Convert to UTF-8 string - committed = 0; + committed = false; Expression e = castTo(sc, Type.tchar.arrayOf()); e = e.optimize(WANTvalue); auto se = e.isStringExp(); diff --git a/dmd/expression.h b/dmd/expression.h index 8f0df2b7352..c41c9b5721e 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -373,7 +373,7 @@ class StringExp final : public Expression void *string; // char, wchar, or dchar data size_t len; // number of chars, wchars, or dchars unsigned char sz; // 1: char, 2: wchar, 4: dchar - unsigned char committed; // !=0 if type is committed + bool committed; // if type is committed utf8_t postfix; // 'c', 'w', 'd' OwnedBy ownedByCtfe; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index df82e9facba..870a8bfd3fb 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3137,7 +3137,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'w': @@ -3162,11 +3162,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'c': - e.committed = 1; + e.committed = true; goto default; default: diff --git a/dmd/frontend.h b/dmd/frontend.h index 63904fb5aca..569405dae26 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -7106,7 +7106,7 @@ class StringExp final : public Expression public: size_t len; uint8_t sz; - uint8_t committed; + bool committed; enum : char { NoPostfix = 0u }; char postfix; From d89884b73529d22feb615916d221ea7d78cfcc6a Mon Sep 17 00:00:00 2001 From: Dennis Date: Tue, 21 Mar 2023 14:50:44 +0100 Subject: [PATCH 053/301] Improve inferred `pure` `@nogc` `notrhow` errors (dlang/dmd!14911) * Improve inferred `pure` `@nogc` errors * Improve diagnostic for inferred `nothrow` --- dmd/blockexit.d | 13 +- dmd/canthrow.d | 11 +- dmd/declaration.h | 3 + dmd/expression.d | 23 +++- dmd/expressionsem.d | 4 +- dmd/frontend.h | 3 + dmd/func.d | 128 +++++++++++++++--- dmd/mtype.d | 2 +- dmd/nogc.d | 6 +- dmd/statementsem.d | 4 +- .../fail_compilation/attributediagnostic.d | 6 +- .../attributediagnostic_nogc.d | 45 ++++++ .../attributediagnostic_nothrow.d | 45 ++++++ .../attributediagnostic_pure.d | 21 +++ tests/dmd/fail_compilation/diag10319.d | 26 ++-- tests/dmd/fail_compilation/diag9620.d | 6 +- .../fail_compilation/dip1000_deprecation.d | 4 +- tests/dmd/fail_compilation/dtor_attributes.d | 2 - .../fail_compilation/dtorfields_attributes.d | 1 - tests/dmd/fail_compilation/fail10968.d | 9 ++ tests/dmd/fail_compilation/fail11375.d | 5 +- tests/dmd/fail_compilation/fail13120.d | 2 +- .../systemvariables_deprecation.d | 2 +- tests/dmd/fail_compilation/testInference.d | 15 ++ 24 files changed, 323 insertions(+), 63 deletions(-) create mode 100644 tests/dmd/fail_compilation/attributediagnostic_nogc.d create mode 100644 tests/dmd/fail_compilation/attributediagnostic_nothrow.d create mode 100644 tests/dmd/fail_compilation/attributediagnostic_pure.d diff --git a/dmd/blockexit.d b/dmd/blockexit.d index eccc15d9e6d..1d37ebfbf6f 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -486,7 +486,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) return; } - result = checkThrow(s.loc, s.exp, mustNotThrow); + result = checkThrow(s.loc, s.exp, mustNotThrow, func); } override void visit(GotoStatement s) @@ -509,8 +509,10 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt; if (!(s.stc & STC.nothrow_)) { - if (mustNotThrow && !(s.stc & STC.nothrow_)) - s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if(func) + func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if (mustNotThrow) + s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO else result |= BE.throw_; } @@ -537,10 +539,11 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) + loc = location of the `throw` + exp = expression yielding the throwable + mustNotThrow = inside of a `nothrow` scope? + + func = function containing the `throw` + + Returns: `BE.[err]throw` depending on the type of `exp` +/ -BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) +BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow, FuncDeclaration func) { import dmd.errors : error; @@ -554,6 +557,8 @@ BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) } if (mustNotThrow) loc.error("`%s` is thrown but not caught", exp.type.toChars()); + else if (func) + func.setThrow(loc, "`%s` is thrown but not caught", exp.type); return BE.throw_; } diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 0c237e6da56..79f1760bae5 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -76,11 +76,16 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { if (mustNotThrow) { - e.error("%s `%s` is not `nothrow`", - f.kind(), f.toPrettyChars()); + e.error("%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); } + else if (func) + { + func.setThrowCall(e.loc, f); + } result |= CT.exception; } } @@ -205,7 +210,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN override void visit(ThrowExp te) { - const res = checkThrow(te.loc, te.e1, mustNotThrow); + const res = checkThrow(te.loc, te.e1, mustNotThrow, func); assert((res & ~(CT.exception | CT.error)) == 0); result |= res; } diff --git a/dmd/declaration.h b/dmd/declaration.h index c12917f4404..1fe89b7b12c 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -623,6 +623,9 @@ class FuncDeclaration : public Declaration FuncDeclarations *inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; // Formerly FUNCFLAGS uint32_t flags; diff --git a/dmd/expression.d b/dmd/expression.d index 4d7d5b281b6..d139d0dd14a 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -1220,12 +1220,15 @@ extern (C++) abstract class Expression : ASTNode return false; // If the call has a pure parent, then the called func must be pure. - if (!f.isPure() && checkImpure(sc)) + if (!f.isPure() && checkImpure(sc, loc, null, f)) { error("`pure` %s `%s` cannot call impure %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; } @@ -1356,7 +1359,7 @@ extern (C++) abstract class Expression : ASTNode if (v.ident == Id.gate) return false; - if (checkImpure(sc)) + if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) { error("`pure` %s `%s` cannot access mutable static data `%s`", sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); @@ -1432,11 +1435,11 @@ extern (C++) abstract class Expression : ASTNode Check if sc.func is impure or can be made impure. Returns true on error, i.e. if sc.func is pure and cannot be made impure. */ - private static bool checkImpure(Scope* sc) + private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0) { return sc.func && (isRootTraitsCompilesScope(sc) ? sc.func.isPureBypassingInference() >= PURE.weak - : sc.func.setImpure()); + : sc.func.setImpure(loc, fmt, arg0)); } /********************************************* @@ -1485,7 +1488,8 @@ extern (C++) abstract class Expression : ASTNode error("`@safe` %s `%s` cannot call `@system` %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); - f.errorSupplementalInferredSafety(/*max depth*/ 10, /*deprecation*/ false); + if (!f.isDtorDeclaration) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -1499,7 +1503,7 @@ extern (C++) abstract class Expression : ASTNode if (sc.func.isSafeBypassingInference()) { .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredSafety(f, 10, true); + errorSupplementalInferredAttr(f, 10, true, STC.safe); } else if (!sc.func.safetyViolation) { @@ -1529,7 +1533,7 @@ extern (C++) abstract class Expression : ASTNode if (!f.isNogc()) { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGC()) + if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) { if (loc.linnum == 0) // e.g. implicitly generated dtor loc = sc.func.loc; @@ -1539,9 +1543,14 @@ extern (C++) abstract class Expression : ASTNode if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX || f.ident == Id._d_newclassT)) + { error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); + } + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); return true; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 870a8bfd3fb..e6caab64974 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -5235,13 +5235,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) { bool err = false; - if (!tf.purity && sc.func.setImpure()) + if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) { exp.error("`pure` %s `%s` cannot call impure %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); err = true; } - if (!tf.isnogc && sc.func.setGC()) + if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1)) { exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); diff --git a/dmd/frontend.h b/dmd/frontend.h index 569405dae26..7275fbadeff 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2465,6 +2465,9 @@ class FuncDeclaration : public Declaration Array siblingCallers; Array* inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; bool purityInprocess() const; bool purityInprocess(bool v); bool safetyInprocess() const; diff --git a/dmd/func.d b/dmd/func.d index 4b6b5b58233..7d8f00dc27c 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -356,6 +356,9 @@ extern (C++) class FuncDeclaration : Declaration /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; /// See the `FUNCFLAG` struct import dmd.common.bitfields; @@ -1441,17 +1444,27 @@ extern (C++) class FuncDeclaration : Declaration } /************************************** - * The function is doing something impure, - * so mark it as impure. - * If there's a purity error, return true. + * The function is doing something impure, so mark it as impure. + * + * Params: + * loc = location of impure action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * arg0 = (optional) argument to format string + * + * Returns: `true` if there's a purity error */ - extern (D) final bool setImpure() + extern (D) final bool setImpure(Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null) { if (purityInprocess) { purityInprocess = false; + if (fmt) + pureViolation = new AttributeViolation(loc, fmt, this, arg0); // impure action + else if (arg0) + pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function + if (fes) - fes.func.setImpure(); + fes.func.setImpure(loc, fmt, arg0); } else if (isPure()) return true; @@ -1540,7 +1553,7 @@ extern (C++) class FuncDeclaration : Declaration { //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); if (nogcInprocess) - setGC(); + setGC(loc, null); return type.toTypeFunction().isnogc; } @@ -1552,10 +1565,16 @@ extern (C++) class FuncDeclaration : Declaration /************************************** * The function is doing something that may allocate with the GC, * so mark it as not nogc (not no-how). + * + * Params: + * loc = location of impure action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * arg0 = (optional) argument to format string + * * Returns: * true if function is marked as @nogc, meaning a user error occurred */ - extern (D) final bool setGC() + extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) { //printf("setGC() %s\n", toChars()); if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) @@ -1567,15 +1586,59 @@ extern (C++) class FuncDeclaration : Declaration if (nogcInprocess) { nogcInprocess = false; + if (fmt) + nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC + else if (arg0) + nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function + type.toTypeFunction().isnogc = false; if (fes) - fes.func.setGC(); + fes.func.setGC(Loc.init, null, null); } else if (isNogc()) return true; return false; } + /************************************** + * The function calls non-`@nogc` function f, mark it as not nogc. + * Params: + * f = function being called + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ + extern (D) final bool setGCCall(FuncDeclaration f) + { + return setGC(loc, null, f); + } + + /************************************** + * The function is doing something that may throw an exception, register that in case nothrow is being inferred + * + * Params: + * loc = location of action + * fmt = format string for error message + * arg0 = (optional) argument to format string + */ + extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + { + if (nothrowInprocess && !nothrowViolation) + { + nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + } + } + + /************************************** + * The function calls non-`nothrow` function f, register that in case nothrow is being inferred + * Params: + * loc = location of call + * f = function being called + */ + extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) + { + return setThrow(loc, null, f); + } + extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) { if (!global.params.vgc) @@ -2119,7 +2182,7 @@ extern (C++) class FuncDeclaration : Declaration if (!needsClosure()) return false; - if (setGC()) + if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) { error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars()); if (global.gag) // need not report supplemental errors @@ -4531,18 +4594,51 @@ struct AttributeViolation /// fd = function to check /// maxDepth = up to how many functions deep to report errors /// deprecation = print deprecations instead of errors -void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool deprecation) +/// stc = storage class of attribute to check +void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) { auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; - if (auto s = fd.safetyViolation) + + AttributeViolation* s; + const(char)* attr; + if (stc & STC.safe) + { + s = fd.safetyViolation; + attr = "@safe"; + } + else if (stc & STC.pure_) + { + s = fd.pureViolation; + attr = "pure"; + } + else if (stc & STC.nothrow_) + { + s = fd.nothrowViolation; + attr = "nothrow"; + } + else if (stc & STC.nogc) + { + s = fd.nogcViolation; + attr = "@nogc"; + } + + if (s) { if (s.fmtStr) { errorFunc(s.loc, deprecation ? - "which would be `@system` because of:" : - "which was inferred `@system` because of:"); - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + "which wouldn't be `%s` because of:" : + "which wasn't inferred `%s` because of:", attr); + if (stc == STC.nogc || stc == STC.pure_) + { + auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); + errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); + } + else + { + errorFunc(s.loc, s.fmtStr, + s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + } } else if (s.arg0.dyncast() == DYNCAST.dsymbol) { @@ -4551,7 +4647,7 @@ void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool depr if (maxDepth > 0) { errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); + errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); } } } diff --git a/dmd/mtype.d b/dmd/mtype.d index 3ed6be55fc9..4f9ad991862 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -7254,7 +7254,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, s ~= "pure "; if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) s ~= "@safe "; - if (!f.isNogc && sc.func.setGC()) + if (!f.isNogc && sc.func.setGC(arg.loc, null)) s ~= "nogc "; if (s) { diff --git a/dmd/nogc.d b/dmd/nogc.d index 201f168527c..a0f3e60861b 100644 --- a/dmd/nogc.d +++ b/dmd/nogc.d @@ -83,7 +83,7 @@ public: err = true; return true; } - if (f.setGC()) + if (f.setGC(e.loc, format)) { e.error(format, f.kind(), f.toPrettyChars()); err = true; @@ -135,7 +135,7 @@ public: override void visit(NewExp e) { - if (e.member && !e.member.isNogc() && f.setGC()) + if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic return; @@ -195,7 +195,7 @@ public: err = true; return; } - if (f.setGC()) + if (f.setGC(e.loc, null)) { err = true; return; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index f83daa5c4a3..6c0f3aad1af 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -3941,9 +3941,9 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } assert(sc.func); - if (!(cas.stc & STC.pure_) && sc.func.setImpure()) + if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not")) cas.error("`asm` statement is assumed to be impure - mark it with `pure` if it is not"); - if (!(cas.stc & STC.nogc) && sc.func.setGC()) + if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not")) cas.error("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not"); if (!(cas.stc & (STC.trusted | STC.safe))) { diff --git a/tests/dmd/fail_compilation/attributediagnostic.d b/tests/dmd/fail_compilation/attributediagnostic.d index 8360e1ac484..523a183d767 100644 --- a/tests/dmd/fail_compilation/attributediagnostic.d +++ b/tests/dmd/fail_compilation/attributediagnostic.d @@ -4,15 +4,15 @@ TEST_OUTPUT: fail_compilation/attributediagnostic.d(24): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` fail_compilation/attributediagnostic.d(26): which calls `attributediagnostic.layer0` fail_compilation/attributediagnostic.d(28): which calls `attributediagnostic.system` -fail_compilation/attributediagnostic.d(30): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(30): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(30): `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not fail_compilation/attributediagnostic.d(25): `attributediagnostic.layer1` is declared here fail_compilation/attributediagnostic.d(46): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1` -fail_compilation/attributediagnostic.d(35): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(35): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(35): cast from `uint` to `int*` not allowed in safe code fail_compilation/attributediagnostic.d(33): `attributediagnostic.system1` is declared here fail_compilation/attributediagnostic.d(47): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2` -fail_compilation/attributediagnostic.d(41): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(41): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(41): `@safe` function `system2` cannot call `@system` `fsys` fail_compilation/attributediagnostic.d(39): `attributediagnostic.system2` is declared here --- diff --git a/tests/dmd/fail_compilation/attributediagnostic_nogc.d b/tests/dmd/fail_compilation/attributediagnostic_nogc.d new file mode 100644 index 00000000000..558796c73b2 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nogc.d @@ -0,0 +1,45 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_nogc.d(21): Error: `@nogc` function `attributediagnostic_nogc.layer2` cannot call non-@nogc function `attributediagnostic_nogc.layer1` +fail_compilation/attributediagnostic_nogc.d(22): which calls `attributediagnostic_nogc.layer0` +fail_compilation/attributediagnostic_nogc.d(23): which calls `attributediagnostic_nogc.gc` +fail_compilation/attributediagnostic_nogc.d(27): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(27): `asm` statement in function `attributediagnostic_nogc.gc` is assumed to use the GC - mark it with `@nogc` if it does not +fail_compilation/attributediagnostic_nogc.d(43): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc1` +fail_compilation/attributediagnostic_nogc.d(32): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(32): cannot use `new` in `@nogc` function `attributediagnostic_nogc.gc1` +fail_compilation/attributediagnostic_nogc.d(44): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc2` +fail_compilation/attributediagnostic_nogc.d(38): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(38): `@nogc` function `attributediagnostic_nogc.gc2` cannot call non-@nogc `fgc` +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() @nogc { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + int* x = new int; +} + +auto fgc = function void() {new int[10];}; +auto gc2() +{ + fgc(); +} + +void main() @nogc +{ + gc1(); + gc2(); +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_nothrow.d b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d new file mode 100644 index 00000000000..7fea3224412 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d @@ -0,0 +1,45 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(22): which calls `attributediagnostic_nothrow.layer0` +fail_compilation/attributediagnostic_nothrow.d(23): which calls `attributediagnostic_nothrow.gc` +fail_compilation/attributediagnostic_nothrow.d(27): which wasn't inferred `nothrow` because of: +fail_compilation/attributediagnostic_nothrow.d(27): `asm` statement is assumed to throw - mark it with `nothrow` if it does not +fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer2` may throw but is marked as `nothrow` +fail_compilation/attributediagnostic_nothrow.d(43): Error: function `attributediagnostic_nothrow.gc1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(32): which wasn't inferred `nothrow` because of: +fail_compilation/attributediagnostic_nothrow.d(32): `object.Exception` is thrown but not caught +fail_compilation/attributediagnostic_nothrow.d(44): Error: function `attributediagnostic_nothrow.gc2` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(41): Error: function `D main` may throw but is marked as `nothrow` +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() nothrow { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + throw new Exception("msg"); +} + +auto fgc = function void() {throw new Exception("msg");}; +auto gc2() +{ + fgc(); +} + +void main() nothrow +{ + gc1(); + gc2(); +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_pure.d b/tests/dmd/fail_compilation/attributediagnostic_pure.d new file mode 100644 index 00000000000..a120dabf852 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_pure.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_pure.d(20): Error: `pure` function `D main` cannot call impure function `attributediagnostic_pure.gc` +fail_compilation/attributediagnostic_pure.d(15): which wasn't inferred `pure` because of: +fail_compilation/attributediagnostic_pure.d(15): `asm` statement is assumed to be impure - mark it with `pure` if it is not +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto gc() +{ + asm {} +} + +void main() pure +{ + gc(); +} diff --git a/tests/dmd/fail_compilation/diag10319.d b/tests/dmd/fail_compilation/diag10319.d index 7b2eca79b80..efc818f30be 100644 --- a/tests/dmd/fail_compilation/diag10319.d +++ b/tests/dmd/fail_compilation/diag10319.d @@ -1,17 +1,21 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(29): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(29): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(18): `diag10319.foo` is declared here -fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(23): which was inferred `@system` because of: -fail_compilation/diag10319.d(23): cannot take address of local `x` in `@safe` function `bar` -fail_compilation/diag10319.d(20): `diag10319.bar!int.bar` is declared here -fail_compilation/diag10319.d(29): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(30): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(27): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/diag10319.d(33): Error: `pure` function `D main` cannot call impure function `diag10319.foo` +fail_compilation/diag10319.d(33): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` +fail_compilation/diag10319.d(22): `diag10319.foo` is declared here +fail_compilation/diag10319.d(34): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(26): which wasn't inferred `pure` because of: +fail_compilation/diag10319.d(26): `pure` function `diag10319.bar!int.bar` cannot access mutable static data `g` +fail_compilation/diag10319.d(34): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(27): which wasn't inferred `@safe` because of: +fail_compilation/diag10319.d(27): cannot take address of local `x` in `@safe` function `bar` +fail_compilation/diag10319.d(24): `diag10319.bar!int.bar` is declared here +fail_compilation/diag10319.d(33): Error: function `diag10319.foo` is not `nothrow` +fail_compilation/diag10319.d(34): Error: function `diag10319.bar!int.bar` is not `nothrow` +fail_compilation/diag10319.d(28): which wasn't inferred `nothrow` because of: +fail_compilation/diag10319.d(28): `object.Exception` is thrown but not caught +fail_compilation/diag10319.d(31): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/diag9620.d b/tests/dmd/fail_compilation/diag9620.d index d99290c6a39..4af87df0a2f 100644 --- a/tests/dmd/fail_compilation/diag9620.d +++ b/tests/dmd/fail_compilation/diag9620.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9620.d(18): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` -fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(20): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` +fail_compilation/diag9620.d(21): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(14): which wasn't inferred `pure` because of: +fail_compilation/diag9620.d(14): `pure` function `diag9620.foo2!().foo2` cannot access mutable static data `x` --- */ diff --git a/tests/dmd/fail_compilation/dip1000_deprecation.d b/tests/dmd/fail_compilation/dip1000_deprecation.d index bf51363c07e..61174395be3 100644 --- a/tests/dmd/fail_compilation/dip1000_deprecation.d +++ b/tests/dmd/fail_compilation/dip1000_deprecation.d @@ -3,11 +3,11 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- fail_compilation/dip1000_deprecation.d(20): Deprecation: `@safe` function `main` calling `inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(22): Deprecation: `@safe` function `main` calling `inferredC` fail_compilation/dip1000_deprecation.d(39): which calls `dip1000_deprecation.inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(54): Deprecation: escaping reference to stack allocated value returned by `S(null)` fail_compilation/dip1000_deprecation.d(55): Deprecation: escaping reference to stack allocated value returned by `createS()` diff --git a/tests/dmd/fail_compilation/dtor_attributes.d b/tests/dmd/fail_compilation/dtor_attributes.d index ce81d6bfd35..21a12ed0253 100644 --- a/tests/dmd/fail_compilation/dtor_attributes.d +++ b/tests/dmd/fail_compilation/dtor_attributes.d @@ -8,8 +8,6 @@ fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is impu fail_compilation/dtor_attributes.d(111): - HasDtor member fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtor_attributes.d(118): Error: `@safe` function `dtor_attributes.test1` cannot call `@system` destructor `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(113): which calls `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(103): which calls `dtor_attributes.HasDtor.~this` fail_compilation/dtor_attributes.d(113): `dtor_attributes.Strict.~this` is declared here fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtor_attributes.d(111): - HasDtor member diff --git a/tests/dmd/fail_compilation/dtorfields_attributes.d b/tests/dmd/fail_compilation/dtorfields_attributes.d index 45b23cece4d..f6cab893bb4 100644 --- a/tests/dmd/fail_compilation/dtorfields_attributes.d +++ b/tests/dmd/fail_compilation/dtorfields_attributes.d @@ -9,7 +9,6 @@ fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` i fail_compilation/dtorfields_attributes.d(115): - HasDtor member fail_compilation/dtorfields_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtorfields_attributes.d(117): Error: `@safe` constructor `dtorfields_attributes.Strict.this` cannot call `@system` destructor `dtorfields_attributes.Strict.~this` -fail_compilation/dtorfields_attributes.d(103): which calls `dtorfields_attributes.HasDtor.~this` fail_compilation/dtorfields_attributes.d(119): `dtorfields_attributes.Strict.~this` is declared here fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtorfields_attributes.d(115): - HasDtor member diff --git a/tests/dmd/fail_compilation/fail10968.d b/tests/dmd/fail_compilation/fail10968.d index cfda8f4d9ad..a436197d047 100644 --- a/tests/dmd/fail_compilation/fail10968.d +++ b/tests/dmd/fail_compilation/fail10968.d @@ -8,10 +8,14 @@ fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arraysetassign!(SA[], SA)._d_arraysetassign` +$p:druntime/import/core/internal/array/arrayassign.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l` +$p:druntime/import/core/internal/array/arrayassign.d$-mixin-$n$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here @@ -19,13 +23,18 @@ fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` +$p:druntime/import/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor` +$p:druntime/import/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` --- */ +#line 29 struct SA { this(this) diff --git a/tests/dmd/fail_compilation/fail11375.d b/tests/dmd/fail_compilation/fail11375.d index 7592a5a1dfd..cabf87a6cae 100644 --- a/tests/dmd/fail_compilation/fail11375.d +++ b/tests/dmd/fail_compilation/fail11375.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not `nothrow` -fail_compilation/fail11375.d(15): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/fail11375.d(18): Error: constructor `fail11375.D!().D.this` is not `nothrow` + which calls `fail11375.B.this` +fail_compilation/fail11375.d(16): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail13120.d b/tests/dmd/fail_compilation/fail13120.d index f1cf340b6a0..6a1335eebd5 100644 --- a/tests/dmd/fail_compilation/fail13120.d +++ b/tests/dmd/fail_compilation/fail13120.d @@ -17,13 +17,13 @@ void g1(char[] s) pure @nogc TEST_OUTPUT: --- fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2` +fail_compilation/fail13120.d(30): which calls `fail13120.f2` fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` --- */ void f2() {} - void g2()(char[] s) { foreach (dchar dc; s) diff --git a/tests/dmd/fail_compilation/systemvariables_deprecation.d b/tests/dmd/fail_compilation/systemvariables_deprecation.d index 75dbe2dc1a0..b5115351efe 100644 --- a/tests/dmd/fail_compilation/systemvariables_deprecation.d +++ b/tests/dmd/fail_compilation/systemvariables_deprecation.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/systemvariables_deprecation.d(16): Deprecation: `@safe` function `main` calling `middle` fail_compilation/systemvariables_deprecation.d(21): which calls `systemvariables_deprecation.inferred` -fail_compilation/systemvariables_deprecation.d(27): which would be `@system` because of: +fail_compilation/systemvariables_deprecation.d(27): which wouldn't be `@safe` because of: fail_compilation/systemvariables_deprecation.d(27): cannot access `@system` variable `x0` in @safe code --- */ diff --git a/tests/dmd/fail_compilation/testInference.d b/tests/dmd/fail_compilation/testInference.d index c0d5a05d05c..145fc9e8b9d 100644 --- a/tests/dmd/fail_compilation/testInference.d +++ b/tests/dmd/fail_compilation/testInference.d @@ -138,8 +138,13 @@ immutable(void)* g10063(inout int* p) pure TEST_OUTPUT: --- fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049` +fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda2` +fail_compilation/testInference.d(148): which calls `testInference.impure14049` +fail_compilation/testInference.d(143): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(143): `pure` function `testInference.impure14049` cannot access mutable static data `i` --- */ +#line 143 auto impure14049() { static int i = 1; return i; } void foo14049(T)(T val) @@ -170,8 +175,10 @@ int* f14160() pure TEST_OUTPUT: --- fail_compilation/testInference.d(180): Error: `pure` function `testInference.test12422` cannot call impure function `testInference.test12422.bar12422!().bar12422` +fail_compilation/testInference.d(179): which calls `testInference.foo12422` --- */ +#line 175 int g12422; void foo12422() { ++g12422; } void test12422() pure @@ -184,9 +191,15 @@ void test12422() pure TEST_OUTPUT: --- fail_compilation/testInference.d(198): Error: `pure` function `testInference.test13729a` cannot call impure function `testInference.test13729a.foo` +fail_compilation/testInference.d(196): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(196): `pure` function `testInference.test13729a.foo` cannot access mutable static data `g13729` fail_compilation/testInference.d(206): Error: `pure` function `testInference.test13729b` cannot call impure function `testInference.test13729b.foo!().foo` +fail_compilation/testInference.d(204): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(204): `pure` function `testInference.test13729b.foo!().foo` cannot access mutable static data `g13729` --- */ + +#line 190 int g13729; void test13729a() pure @@ -229,8 +242,10 @@ void test17086_call () TEST_OUTPUT: --- fail_compilation/testInference.d(238): Error: `pure` function `testInference.test20047_pure_function` cannot call impure function `testInference.test20047_pure_function.bug` +fail_compilation/testInference.d(237): which calls `testInference.test20047_impure_function` --- */ +#line 234 void test20047_impure_function() {} void test20047_pure_function() pure { From 8cdf0d9f0874c3c8a0a9f6660c5a41d4dc09ea41 Mon Sep 17 00:00:00 2001 From: Dennis Date: Tue, 21 Mar 2023 17:20:37 +0100 Subject: [PATCH 054/301] Improve layout of Expression classes (dlang/dmd!15014) * Reduce class instance size of `Expression` 40->34 * Shrink `stageflags` from 4 bytes to 1 * Optimize layout of `ArrayLiteralExp` * Reduce size of `AssocArrayLiteralExp` * Use bitfields for bool fields in `SliceExp` * Reduce size of `StringExp` --- dmd/apply.d | 2 +- dmd/expression.d | 31 +++++++++++++++++++------------ dmd/expression.h | 28 +++++++++++++++++----------- dmd/frontend.h | 46 ++++++++++++++++++++++++++-------------------- dmd/inline.d | 2 +- dmd/optimize.d | 2 +- dmd/visitor.d | 2 +- 7 files changed, 66 insertions(+), 47 deletions(-) diff --git a/dmd/apply.d b/dmd/apply.d index 59ba9f5ecd6..d18b81f044f 100644 --- a/dmd/apply.d +++ b/dmd/apply.d @@ -170,7 +170,7 @@ public: { if (e.stageflags & stageApply) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageApply; doCond(e.elements.peekSlice()) || applyTo(e); e.stageflags = old; diff --git a/dmd/expression.d b/dmd/expression.d index d139d0dd14a..9316fc37dc3 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -720,10 +720,10 @@ enum WANTexpand = 1; // expand const/immutable variables if possible */ extern (C++) abstract class Expression : ASTNode { - const EXP op; // to minimize use of dynamic_cast - bool parens; // if this is a parenthesized expression Type type; // !=null means that semantic() has been run Loc loc; // file location + const EXP op; // to minimize use of dynamic_cast + bool parens; // if this is a parenthesized expression extern (D) this(const ref Loc loc, EXP op) scope { @@ -2539,6 +2539,8 @@ extern (C++) final class NullExp : Expression */ extern (C++) final class StringExp : Expression { + char postfix = NoPostfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe = OwnedBy.code; private union { char* string; // if sz == 1 @@ -2559,8 +2561,6 @@ extern (C++) final class StringExp : Expression bool committed; enum char NoPostfix = 0; - char postfix = NoPostfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe = OwnedBy.code; extern (D) this(const ref Loc loc, const(void)[] string) scope { @@ -3051,6 +3051,9 @@ extern (C++) final class TupleExp : Expression */ extern (C++) final class ArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + bool onstack = false; + /** If !is null, elements[] can be sparse and basis is used for the * "default" element value. In other words, non-null elements[i] overrides * this 'basis' value. @@ -3058,8 +3061,6 @@ extern (C++) final class ArrayLiteralExp : Expression Expression basis; Expressions* elements; - OwnedBy ownedByCtfe = OwnedBy.code; - bool onstack = false; extern (D) this(const ref Loc loc, Type type, Expressions* elements) { @@ -3216,11 +3217,11 @@ extern (C++) final class ArrayLiteralExp : Expression */ extern (C++) final class AssocArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + Expressions* keys; Expressions* values; - OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) { super(loc, EXP.assocArrayLiteral); @@ -3308,7 +3309,7 @@ extern (C++) final class StructLiteralExp : Expression * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + ubyte stageflags; bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` @@ -5661,9 +5662,15 @@ extern (C++) final class SliceExp : UnaExp Expression lwr; // null if implicit [length - 1] VarDeclaration lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + + private extern(D) static struct BitFields + { + bool upperIsInBounds; // true if upr <= e1.length + bool lowerIsLessThanUpper; // true if lwr <= upr + bool arrayop; // an array operation, rather than a slice + } + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ubyte)); /************************************************************/ extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) diff --git a/dmd/expression.h b/dmd/expression.h index c41c9b5721e..80d8030b470 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -79,10 +79,10 @@ enum class ModifyFlags class Expression : public ASTNode { public: - EXP op; // to minimize use of dynamic_cast - d_bool parens; // if this is a parenthesized expression Type *type; // !=NULL means that semantic() has been run Loc loc; // file location + EXP op; // to minimize use of dynamic_cast + d_bool parens; // if this is a parenthesized expression size_t size() const; static void _init(); @@ -370,12 +370,12 @@ class NullExp final : public Expression class StringExp final : public Expression { public: + utf8_t postfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe; void *string; // char, wchar, or dchar data size_t len; // number of chars, wchars, or dchars unsigned char sz; // 1: char, 2: wchar, 4: dchar bool committed; // if type is committed - utf8_t postfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe; static StringExp *create(const Loc &loc, const char *s); static StringExp *create(const Loc &loc, const void *s, d_size_t len); @@ -419,10 +419,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: - Expression *basis; - Expressions *elements; OwnedBy ownedByCtfe; d_bool onstack; + Expression *basis; + Expressions *elements; static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements); @@ -439,9 +439,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Expressions *keys; Expressions *values; - OwnedBy ownedByCtfe; bool equals(const RootObject * const o) const override; AssocArrayLiteralExp *syntaxCopy() override; @@ -474,7 +474,7 @@ class StructLiteralExp final : public Expression * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + uint8_t stageflags; d_bool useStaticInit; // if this is true, use the StructDeclaration's init symbol d_bool isOriginal; // used when moving instances to indicate `this is this.origin` @@ -937,9 +937,15 @@ class SliceExp final : public UnaExp Expression *upr; // NULL if implicit 0 Expression *lwr; // NULL if implicit [length - 1] VarDeclaration *lengthVar; - d_bool upperIsInBounds; // true if upr <= e1.length - d_bool lowerIsLessThanUpper; // true if lwr <= upr - d_bool arrayop; // an array operation, rather than a slice + + bool upperIsInBounds() const; // true if upr <= e1.length + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; // true if lwr <= upr + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; // an array operation, rather than a slice + bool arrayop(bool v); +private: + uint8_t bitFields; SliceExp *syntaxCopy() override; bool isLvalue() override; diff --git a/dmd/frontend.h b/dmd/frontend.h index 7275fbadeff..3af9f7e88b5 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -958,10 +958,10 @@ struct Optional final class Expression : public ASTNode { public: - const EXP op; - bool parens; Type* type; Loc loc; + const EXP op; + bool parens; size_t size() const; static void _init(); static void deinitialize(); @@ -6945,22 +6945,22 @@ struct UnionExp final private: union __AnonStruct__u { - char exp[40LLU]; + char exp[34LLU]; char integerexp[48LLU]; - char errorexp[40LLU]; + char errorexp[34LLU]; char realexp[64LLU]; char complexexp[80LLU]; char symoffexp[72LLU]; - char stringexp[60LLU]; - char arrayliteralexp[58LLU]; - char assocarrayliteralexp[57LLU]; - char structliteralexp[95LLU]; + char stringexp[58LLU]; + char arrayliteralexp[56LLU]; + char assocarrayliteralexp[56LLU]; + char structliteralexp[92LLU]; char compoundliteralexp[48LLU]; - char nullexp[40LLU]; + char nullexp[34LLU]; char dotvarexp[65LLU]; char addrexp[56LLU]; char indexexp[82LLU]; - char sliceexp[83LLU]; + char sliceexp[81LLU]; char vectorexp[69LLU]; }; #pragma pack(pop) @@ -7100,20 +7100,20 @@ class NullExp final : public Expression class StringExp final : public Expression { +public: + char postfix; + OwnedBy ownedByCtfe; union { char* string; char16_t* wstring; char32_t* dstring; }; -public: size_t len; uint8_t sz; bool committed; enum : char { NoPostfix = 0u }; - char postfix; - OwnedBy ownedByCtfe; static StringExp* create(const Loc& loc, const char* s); static StringExp* create(const Loc& loc, const void* string, size_t len); static void emplace(UnionExp* pue, const Loc& loc, const char* s); @@ -7146,10 +7146,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: - Expression* basis; - Array* elements; OwnedBy ownedByCtfe; bool onstack; + Expression* basis; + Array* elements; static ArrayLiteralExp* create(const Loc& loc, Array* elements); static void emplace(UnionExp* pue, const Loc& loc, Array* elements); ArrayLiteralExp* syntaxCopy() override; @@ -7164,9 +7164,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Array* keys; Array* values; - OwnedBy ownedByCtfe; bool equals(const RootObject* const o) const override; AssocArrayLiteralExp* syntaxCopy() override; Optional toBool() override; @@ -7182,7 +7182,7 @@ class StructLiteralExp final : public Expression Symbol* sym; StructLiteralExp* origin; StructLiteralExp* inlinecopy; - int32_t stageflags; + uint8_t stageflags; bool useStaticInit; bool isOriginal; OwnedBy ownedByCtfe; @@ -7586,9 +7586,15 @@ class SliceExp final : public UnaExp Expression* upr; Expression* lwr; VarDeclaration* lengthVar; - bool upperIsInBounds; - bool lowerIsLessThanUpper; - bool arrayop; + bool upperIsInBounds() const; + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; + bool arrayop(bool v); +private: + uint8_t bitFields; +public: SliceExp* syntaxCopy() override; bool isLvalue() override; Expression* toLvalue(Scope* sc, Expression* e) override; diff --git a/dmd/inline.d b/dmd/inline.d index 3f9d4d56d82..a84ab1826a5 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -1488,7 +1488,7 @@ public: //printf("StructLiteralExp.inlineScan()\n"); if (e.stageflags & stageInlineScan) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageInlineScan; arrayInlineScan(e.elements); e.stageflags = old; diff --git a/dmd/optimize.d b/dmd/optimize.d index b5d32b2932d..61c385fc061 100644 --- a/dmd/optimize.d +++ b/dmd/optimize.d @@ -371,7 +371,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) { if (e.stageflags & stageOptimize) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageOptimize; if (e.elements) { diff --git a/dmd/visitor.d b/dmd/visitor.d index 8990ce49a2a..e8c77d47293 100644 --- a/dmd/visitor.d +++ b/dmd/visitor.d @@ -152,7 +152,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor // need to avoid infinite recursion. if (!(e.stageflags & stageToCBuffer)) { - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageToCBuffer; foreach (el; *e.elements) if (el) From 3925809503972a012bf7e43b113e29e06531ae10 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 21 Mar 2023 11:59:06 -0700 Subject: [PATCH 055/301] replace Visitor for Statement_toIR() with mixin (dlang/dmd!15013) --- dmd/frontend.h | 18 ++++++++ dmd/statement.d | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/dmd/frontend.h b/dmd/frontend.h index 3af9f7e88b5..a6d353e4030 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -320,6 +320,13 @@ class ForwardingStatement; class ContinueStatement; class ThrowStatement; class SwitchErrorStatement; +class CompoundAsmStatement; +class PragmaStatement; +class StaticAssertStatement; +class AsmStatement; +class InlineAsmStatement; +class GccAsmStatement; +class ImportStatement; struct Token; struct code; class Object; @@ -4109,6 +4116,7 @@ class Statement : public ASTNode void accept(Visitor* v) override; virtual ReturnStatement* endsWithReturnStatement(); ErrorStatement* isErrorStatement(); + PeelStatement* isPeelStatement(); ScopeStatement* isScopeStatement(); ExpStatement* isExpStatement(); CompoundStatement* isCompoundStatement(); @@ -4142,6 +4150,15 @@ class Statement : public ASTNode UnrolledLoopStatement* isUnrolledLoopStatement(); ForeachRangeStatement* isForeachRangeStatement(); CompoundDeclarationStatement* isCompoundDeclarationStatement(); + CompoundAsmStatement* isCompoundAsmStatement(); + PragmaStatement* isPragmaStatement(); + StaticAssertStatement* isStaticAssertStatement(); + CaseRangeStatement* isCaseRangeStatement(); + SynchronizedStatement* isSynchronizedStatement(); + AsmStatement* isAsmStatement(); + InlineAsmStatement* isInlineAsmStatement(); + GccAsmStatement* isGccAsmStatement(); + ImportStatement* isImportStatement(); }; class AsmStatement : public Statement @@ -5093,6 +5110,7 @@ struct ASTCodegen final using TryCatchStatement = ::TryCatchStatement; using TryFinallyStatement = ::TryFinallyStatement; using UnrolledLoopStatement = ::UnrolledLoopStatement; + using VisitStatement = ::VisitStatement; using WhileStatement = ::WhileStatement; using WithStatement = ::WithStatement; using StaticAssert = ::StaticAssert; diff --git a/dmd/statement.d b/dmd/statement.d index 30f9ad44d35..3ccf228d1e9 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -373,6 +373,7 @@ extern (C++) abstract class Statement : ASTNode * the downcast statement if it can be downcasted, otherwise `null` */ inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } + inout(PeelStatement) isPeelStatement() { return stmt == STMT.Peel ? cast(typeof(return))this : null; } inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } @@ -406,6 +407,15 @@ extern (C++) abstract class Statement : ASTNode inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; } + inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } + inout(PragmaStatement) isPragmaStatement() { return stmt == STMT.Pragma ? cast(typeof(return))this : null; } + inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; } + inout(CaseRangeStatement) isCaseRangeStatement() { return stmt == STMT.CaseRange ? cast(typeof(return))this : null; } + inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; } + inout(AsmStatement) isAsmStatement() { return stmt == STMT.Asm ? cast(typeof(return))this : null; } + inout(InlineAsmStatement) isInlineAsmStatement() { return stmt == STMT.InlineAsm ? cast(typeof(return))this : null; } + inout(GccAsmStatement) isGccAsmStatement() { return stmt == STMT.GccAsm ? cast(typeof(return))this : null; } + inout(ImportStatement) isImportStatement() { return stmt == STMT.Import ? cast(typeof(return))this : null; } } /*********************************************************** @@ -2085,3 +2095,107 @@ extern (C++) final class ImportStatement : Statement v.visit(this); } } + + +mixin template VisitStatement(Result) +{ + Result VisitStatement(Statement s) + { + final switch (s.stmt) + { + case STMT.Error: mixin(visitStmtCase("Error")); + case STMT.Scope: mixin(visitStmtCase("Scope")); + case STMT.Exp: mixin(visitStmtCase("Exp")); + case STMT.Compound: mixin(visitStmtCase("Compound")); + case STMT.Return: mixin(visitStmtCase("Return")); + case STMT.If: mixin(visitStmtCase("If")); + case STMT.Conditional: mixin(visitStmtCase("Conditional")); + case STMT.StaticForeach: mixin(visitStmtCase("StaticForeach")); + case STMT.Case: mixin(visitStmtCase("Case")); + case STMT.Default: mixin(visitStmtCase("Default")); + case STMT.Label: mixin(visitStmtCase("Label")); + case STMT.Goto: mixin(visitStmtCase("Goto")); + case STMT.GotoDefault: mixin(visitStmtCase("GotoDefault")); + case STMT.GotoCase: mixin(visitStmtCase("GotoCase")); + case STMT.Break: mixin(visitStmtCase("Break")); + case STMT.DtorExp: mixin(visitStmtCase("DtorExp")); + case STMT.Mixin: mixin(visitStmtCase("Mixin")); + case STMT.Forwarding: mixin(visitStmtCase("Forwarding")); + case STMT.Do: mixin(visitStmtCase("Do")); + case STMT.While: mixin(visitStmtCase("While")); + case STMT.For: mixin(visitStmtCase("For")); + case STMT.Foreach: mixin(visitStmtCase("Foreach")); + case STMT.Switch: mixin(visitStmtCase("Switch")); + case STMT.Continue: mixin(visitStmtCase("Continue")); + case STMT.With: mixin(visitStmtCase("With")); + case STMT.TryCatch: mixin(visitStmtCase("TryCatch")); + case STMT.Throw: mixin(visitStmtCase("Throw")); + case STMT.Debug: mixin(visitStmtCase("Debug")); + case STMT.TryFinally: mixin(visitStmtCase("TryFinally")); + case STMT.ScopeGuard: mixin(visitStmtCase("ScopeGuard")); + case STMT.SwitchError: mixin(visitStmtCase("SwitchError")); + case STMT.UnrolledLoop: mixin(visitStmtCase("UnrolledLoop")); + case STMT.ForeachRange: mixin(visitStmtCase("ForeachRange")); + case STMT.CompoundDeclaration: mixin(visitStmtCase("CompoundDeclaration")); + case STMT.Peel: mixin(visitStmtCase("Peel")); + case STMT.CompoundAsm: mixin(visitStmtCase("CompoundAsm")); + case STMT.Pragma: mixin(visitStmtCase("Pragma")); + case STMT.StaticAssert: mixin(visitStmtCase("StaticAssert")); + case STMT.CaseRange: mixin(visitStmtCase("CaseRange")); + case STMT.Synchronized: mixin(visitStmtCase("Synchronized")); + case STMT.Asm: mixin(visitStmtCase("Asm")); + case STMT.InlineAsm: mixin(visitStmtCase("InlineAsm")); + case STMT.GccAsm: mixin(visitStmtCase("GccAsm")); + case STMT.Import: mixin(visitStmtCase("Import")); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitStmtCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto sx = s.is"~handler~"Statement(); + static if (__traits(compiles, visit"~handler~"(sx))) + { + static if (isVoid) + { + visit"~handler~"(sx); + return; + } + else + { + if (Result r = visit"~handler~"(sx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(s))) + { + static if (isVoid) + { + visitDefaultCase(sx); + return; + } + else + { + if (Result r = visitDefaultCase(s)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} From 34d35ba5efc6ba0da9bd96ead9d9d4ee5d4dde71 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 01:36:56 -0700 Subject: [PATCH 056/301] split off InlineScanVisitorDsymbol (dlang/dmd!15021) --- dmd/inline.d | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/dmd/inline.d b/dmd/inline.d index a84ab1826a5..954e58f7935 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -69,12 +69,17 @@ public void inlineScanModule(Module m) Dsymbol s = (*m.members)[i]; //if (global.params.verbose) // message("inline scan symbol %s", s.toChars()); - scope InlineScanVisitor v = new InlineScanVisitor(); - s.accept(v); + inlineScanDsymbol(s); } m.semanticRun = PASS.inlinedone; } +private void inlineScanDsymbol(Dsymbol s) +{ + scope InlineScanVisitorDsymbol v = new InlineScanVisitorDsymbol(); + s.accept(v); +} + /*********************************************************** * Perform the "inline copying" of a default argument for a function parameter. * @@ -1216,7 +1221,7 @@ public: } else { - s.accept(this); + inlineScanDsymbol(s); } } @@ -1526,6 +1531,20 @@ public: eresult = null; } } +} + +/*********************************************************** + * Walk the trees, looking for functions to inline. + * Inline any that can be. + */ +private extern (C++) final class InlineScanVisitorDsymbol : Visitor +{ + alias visit = Visitor.visit; +public: + + extern (D) this() scope + { + } /************************************* * Look for function inlining possibilities. @@ -1547,20 +1566,20 @@ public: return; if (fd.fbody && !fd.isNaked()) { - auto againsave = again; - auto parentsave = parent; - parent = fd; - do + while (1) { - again = false; fd.inlineNest++; fd.inlineScanned = true; - inlineScan(fd.fbody); + + scope InlineScanVisitor v = new InlineScanVisitor(); + v.parent = fd; + v.inlineScan(fd.fbody); + bool again = v.again; + fd.inlineNest--; + if (!again) + break; } - while (again); - again = againsave; - parent = parentsave; } } @@ -1806,8 +1825,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat else fd.inlineStatusExp = ILS.yes; - scope InlineScanVisitor v = new InlineScanVisitor(); - fd.accept(v); // Don't scan recursively for header content scan + inlineScanDsymbol(fd); // Don't scan recursively for header content scan if (fd.inlineStatusExp == ILS.uninitialized) { From 7ced704388008fbd66936501fd2cff2a4924c95d Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 01:40:32 -0700 Subject: [PATCH 057/301] blockexit.d: replace Visitor with mixin (dlang/dmd!15020) --- dmd/blockexit.d | 97 ++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/dmd/blockexit.d b/dmd/blockexit.d index 1d37ebfbf6f..db738b4076c 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -63,34 +63,21 @@ enum BE : int */ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) { - extern (C++) final class BlockExit : Visitor - { - alias visit = Visitor.visit; - public: - FuncDeclaration func; - bool mustNotThrow; - int result; - - extern (D) this(FuncDeclaration func, bool mustNotThrow) scope - { - this.func = func; - this.mustNotThrow = mustNotThrow; - result = BE.none; - } + int result = BE.none; - override void visit(Statement s) + void visitDefaultCase(Statement s) { printf("Statement::blockExit(%p)\n", s); printf("%s\n", s.toChars()); assert(0); } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = BE.none; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { result = BE.fallthru; if (s.exp) @@ -115,13 +102,18 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(MixinStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.length, result); result = BE.fallthru; @@ -175,7 +167,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { result = BE.fallthru; foreach (s; *uls.statements) @@ -190,19 +182,19 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { //printf("ScopeStatement::blockExit(%p)\n", s.statement); result = blockExit(s.statement, func, mustNotThrow); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(DoStatement s) + void visitDo(DoStatement s) { if (s._body) { @@ -227,7 +219,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result &= ~(BE.break_ | BE.continue_); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { result = BE.fallthru; if (s._init) @@ -259,7 +251,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= canThrow(s.increment, func, mustNotThrow); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { result = BE.fallthru; result |= canThrow(s.aggr, func, mustNotThrow); @@ -268,13 +260,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(IfStatement s) + void visitIf(IfStatement s) { //printf("IfStatement::blockExit(%p)\n", s); result = BE.none; @@ -297,24 +289,24 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) //printf("IfStatement::blockExit(%p) = x%x\n", s, result); } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { result = blockExit(s.ifbody, func, mustNotThrow); if (s.elsebody) result |= blockExit(s.elsebody, func, mustNotThrow); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { result = BE.fallthru; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { result = BE.fallthru; } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { result = BE.none; result |= canThrow(s.condition, func, mustNotThrow); @@ -332,63 +324,63 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { result = BE.goto_; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { result = BE.goto_; } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { // Switch errors are non-recoverable result = BE.halt; } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { result = BE.return_; if (s.exp) result |= canThrow(s.exp, func, mustNotThrow); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_); result = s.ident ? BE.goto_ : BE.break_; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_; } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { result = blockExit(s._body, func, mustNotThrow); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { result = BE.none; result |= canThrow(s.exp, func, mustNotThrow); result |= blockExit(s._body, func, mustNotThrow); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { assert(s._body); result = blockExit(s._body, func, false); @@ -428,7 +420,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= catchresult; } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { result = BE.fallthru; if (s._body) @@ -470,13 +462,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= finalresult & ~BE.fallthru; } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { // At this point, this statement is just an empty placeholder result = BE.fallthru; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { if (s.internalThrow) { @@ -489,13 +481,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result = checkThrow(s.loc, s.exp, mustNotThrow, func); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { //printf("GotoStatement::blockExit(%p)\n", s); result = BE.goto_; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { //printf("LabelStatement::blockExit(%p)\n", s); result = blockExit(s.statement, func, mustNotThrow); @@ -503,7 +495,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CompoundAsmStatement s) + void visitCompoundAsm(CompoundAsmStatement s) { // Assume the worst result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt; @@ -518,17 +510,16 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(ImportStatement s) + void visitImport(ImportStatement s) { result = BE.fallthru; } - } if (!s) return BE.fallthru; - scope BlockExit be = new BlockExit(func, mustNotThrow); - s.accept(be); - return be.result; + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; } /++ From 8e3c9517ea996f34ae0d0be1944ed902b2a5a8cb Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 01:42:05 -0700 Subject: [PATCH 058/301] hdrgen.d: replace StatementVisitor with mixin (dlang/dmd!15019) --- dmd/hdrgen.d | 223 ++++++++++++++++++++++++--------------------------- 1 file changed, 106 insertions(+), 117 deletions(-) diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index c0fbf1ebe48..1f3b11282d7 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -134,37 +134,19 @@ void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs) private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - s.accept(v); -} - -private extern (C++) final class StatementPrettyPrintVisitor : Visitor -{ - alias visit = Visitor.visit; -public: - OutBuffer* buf; - HdrGenState* hgs; - - extern (D) this(OutBuffer* buf, HdrGenState* hgs) scope + void visitDefaultCase(Statement s) { - this.buf = buf; - this.hgs = hgs; - } - - override void visit(Statement s) - { - buf.writestring("Statement::toCBuffer()"); - buf.writenl(); + s.error("visitDefaultCase() %d for %s", s.stmt, s.toChars()); assert(0); } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { buf.writestring("__error__"); buf.writenl(); } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { if (s.exp && s.exp.op == EXP.declaration && (cast(DeclarationExp)s.exp).declaration) @@ -180,7 +162,12 @@ public: buf.writenl(); } - override void visit(MixinStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { buf.writestring("mixin("); argsToBuffer(s.exps, buf, hgs, null); @@ -189,16 +176,21 @@ public: buf.writenl(); } - override void visit(CompoundStatement s) + void visitCompound(CompoundStatement s) { foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } } - override void visit(CompoundDeclarationStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitCompoundDeclaration(CompoundDeclarationStatement s) { bool anywritten = false; foreach (sx; *s.statements) @@ -223,7 +215,7 @@ public: buf.writenl(); } - override void visit(UnrolledLoopStatement s) + void visitUnrolledLoop(UnrolledLoopStatement s) { buf.writestring("/*unrolled*/ {"); buf.writenl(); @@ -231,26 +223,26 @@ public: foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { buf.writeByte('{'); buf.writenl(); buf.level++; if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { buf.writestring("while ("); if (auto p = s.param) @@ -271,28 +263,28 @@ public: buf.writeByte(')'); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(DoStatement s) + void visitDo(DoStatement s) { buf.writestring("do"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.writestring("while ("); s.condition.expressionToBuffer(buf, hgs); buf.writestring(");"); buf.writenl(); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { buf.writestring("for ("); if (s._init) { hgs.forStmtInit++; - s._init.accept(this); + s._init.statementToBuffer(buf, hgs); hgs.forStmtInit--; } else @@ -314,13 +306,13 @@ public: buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachWithoutBody(ForeachStatement s) + void foreachWithoutBody(ForeachStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -341,20 +333,20 @@ public: buf.writenl(); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { foreachWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachRangeWithoutBody(ForeachRangeStatement s) + void foreachRangeWithoutBody(ForeachRangeStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -370,39 +362,39 @@ public: buf.writenl(); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { foreachRangeWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(StaticForeachStatement s) + void visitStaticForeach(StaticForeachStatement s) { buf.writestring("static "); if (s.sfe.aggrfe) { - visit(s.sfe.aggrfe); + visitForeach(s.sfe.aggrfe); } else { assert(s.sfe.rangefe); - visit(s.sfe.rangefe); + visitForeachRange(s.sfe.rangefe); } } - override void visit(ForwardingStatement s) + void visitForwarding(ForwardingStatement s) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(IfStatement s) + void visitIf(IfStatement s) { buf.writestring("if ("); if (Parameter p = s.prm) @@ -423,12 +415,12 @@ public: buf.writenl(); if (s.ifbody.isScopeStatement()) { - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; } if (s.elsebody) @@ -444,18 +436,18 @@ public: } if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement()) { - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); } else { buf.level++; - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; } } } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { s.condition.conditionToBuffer(buf, hgs); buf.writenl(); @@ -463,7 +455,7 @@ public: buf.writenl(); buf.level++; if (s.ifbody) - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -474,14 +466,14 @@ public: buf.writeByte('{'); buf.level++; buf.writenl(); - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); } buf.writenl(); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { buf.writestring("pragma ("); buf.writestring(s.ident.toString()); @@ -497,7 +489,7 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -509,12 +501,12 @@ public: } } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.dsymbolToBuffer(buf, hgs); } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); s.condition.expressionToBuffer(buf, hgs); @@ -527,28 +519,28 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } else { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { buf.writestring("case "); s.exp.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(CaseRangeStatement s) + void visitCaseRange(CaseRangeStatement s) { buf.writestring("case "); s.first.expressionToBuffer(buf, hgs); @@ -556,23 +548,23 @@ public: s.last.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { buf.writestring("default:"); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { buf.writestring("goto default;"); buf.writenl(); } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { buf.writestring("goto case"); if (s.exp) @@ -584,13 +576,13 @@ public: buf.writenl(); } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { buf.writestring("SwitchErrorStatement::toCBuffer()"); buf.writenl(); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { buf.writestring("return "); if (s.exp) @@ -599,7 +591,7 @@ public: buf.writenl(); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { buf.writestring("break"); if (s.ident) @@ -611,7 +603,7 @@ public: buf.writenl(); } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { buf.writestring("continue"); if (s.ident) @@ -623,7 +615,7 @@ public: buf.writenl(); } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { buf.writestring("synchronized"); if (s.exp) @@ -635,21 +627,21 @@ public: if (s._body) { buf.writeByte(' '); - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } - override void visit(WithStatement s) + void visitWith(WithStatement s) { buf.writestring("with ("); s.exp.expressionToBuffer(buf, hgs); buf.writestring(")"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { buf.writestring("try"); buf.writenl(); @@ -657,29 +649,44 @@ public: { if (s._body.isScopeStatement()) { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } else { buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; } } foreach (c; *s.catches) { - visit(c); + buf.writestring("catch"); + if (c.type) + { + buf.writeByte('('); + typeToBuffer(c.type, c.ident, buf, hgs); + buf.writeByte(')'); + } + buf.writenl(); + buf.writeByte('{'); + buf.writenl(); + buf.level++; + if (c.handler) + c.handler.statementToBuffer(buf, hgs); + buf.level--; + buf.writeByte('}'); + buf.writenl(); } } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { buf.writestring("try"); buf.writenl(); buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -687,25 +694,25 @@ public: buf.writenl(); if (s.finalbody.isScopeStatement()) { - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); buf.level--; } } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { buf.writestring(Token.toString(s.tok)); buf.writeByte(' '); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { buf.writestring("throw "); s.exp.expressionToBuffer(buf, hgs); @@ -713,15 +720,15 @@ public: buf.writenl(); } - override void visit(DebugStatement s) + void visitDebug(DebugStatement s) { if (s.statement) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { buf.writestring("goto "); buf.writestring(s.ident.toString()); @@ -729,16 +736,16 @@ public: buf.writenl(); } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { buf.writestring(s.ident.toString()); buf.writeByte(':'); buf.writenl(); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { buf.writestring("asm { "); Token* t = s.tokens; @@ -764,7 +771,7 @@ public: buf.writenl(); } - override void visit(ImportStatement s) + void visitImport(ImportStatement s) { foreach (imp; *s.imports) { @@ -772,25 +779,8 @@ public: } } - void visit(Catch c) - { - buf.writestring("catch"); - if (c.type) - { - buf.writeByte('('); - typeToBuffer(c.type, c.ident, buf, hgs); - buf.writeByte(')'); - } - buf.writenl(); - buf.writeByte('{'); - buf.writenl(); - buf.level++; - if (c.handler) - c.handler.accept(this); - buf.level--; - buf.writeByte('}'); - buf.writenl(); - } + mixin VisitStatement!void visit; + visit.VisitStatement(s); } private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs) @@ -2879,8 +2869,7 @@ public: void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - (cast() s).accept(v); + (cast()s).statementToBuffer(buf, hgs); } void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs) From a0b7cdee3c0f08e60a44f93690401f3f999e70a2 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 00:53:17 -0700 Subject: [PATCH 059/301] move some functions out of Interpreter --- dmd/dinterpret.d | 257 ++++++++++++++++++++++++----------------------- 1 file changed, 131 insertions(+), 126 deletions(-) diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index 141574ff2e5..8288d740d9b 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -767,19 +767,6 @@ public: return false; } - static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) - { - if (exps is original) - { - if (!original) - exps = new Expressions(); - else - exps = original.copy(); - ++ctfeGlobals.numArrayAllocs; - } - return exps; - } - /******************************** Statement ***************************/ override void visit(Statement s) @@ -936,74 +923,6 @@ public: result = interpret(pue, s.statement, istate); } - /** - Given an expression e which is about to be returned from the current - function, generate an error if it contains pointers to local variables. - - Only checks expressions passed by value (pointers to local variables - may already be stored in members of classes, arrays, or AAs which - were passed as mutable function parameters). - Returns: - true if it is safe to return, false if an error was generated. - */ - static bool stopPointersEscaping(const ref Loc loc, Expression e) - { - if (!e.type.hasPointers()) - return true; - if (isPointer(e.type)) - { - Expression x = e; - if (auto eaddr = e.isAddrExp()) - x = eaddr.e1; - VarDeclaration v; - while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) - { - if (v.storage_class & STC.ref_) - { - x = getValue(v); - if (auto eaddr = e.isAddrExp()) - eaddr.e1 = x; - continue; - } - if (ctfeGlobals.stack.isInCurrentFrame(v)) - { - error(loc, "returning a pointer to a local stack variable"); - return false; - } - else - break; - } - // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not - // pointing to a local struct or static array. - } - if (auto se = e.isStructLiteralExp()) - { - return stopPointersEscapingFromArray(loc, se.elements); - } - if (auto ale = e.isArrayLiteralExp()) - { - return stopPointersEscapingFromArray(loc, ale.elements); - } - if (auto aae = e.isAssocArrayLiteralExp()) - { - if (!stopPointersEscapingFromArray(loc, aae.keys)) - return false; - return stopPointersEscapingFromArray(loc, aae.values); - } - return true; - } - - // Check all elements of an array for escaping local variables. Return false if error - static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) - { - foreach (e; *elems) - { - if (e && !stopPointersEscaping(loc, e)) - return false; - } - return true; - } - override void visit(ReturnStatement s) { debug (LOG) @@ -1082,19 +1001,6 @@ public: result = e; } - static Statement findGotoTarget(InterState* istate, Identifier ident) - { - Statement target = null; - if (ident) - { - LabelDsymbol label = istate.fd.searchLabel(ident); - assert(label && label.statement); - LabelStatement ls = label.statement; - target = ls.gotoTarget ? ls.gotoTarget : ls.statement; - } - return target; - } - override void visit(BreakStatement s) { debug (LOG) @@ -1522,38 +1428,6 @@ public: result = e; } - static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) - { - debug (LOG) - { - printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); - } - // Little sanity check to make sure it's really a Throwable - ClassReferenceExp boss = oldest.thrown; - const next = 5; // index of Throwable.next - assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next - ClassReferenceExp collateral = newest.thrown; - if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) - { - /* Find the index of the Error.bypassException field - */ - auto bypass = next + 1; - if ((*collateral.value.elements)[bypass].type.ty == Tuns32) - bypass += 1; // skip over _refcount field - assert((*collateral.value.elements)[bypass].type.ty == Tclass); - - // The new exception bypass the existing chain - (*collateral.value.elements)[bypass] = boss; - return newest; - } - while ((*boss.value.elements)[next].op == EXP.classReference) - { - boss = (*boss.value.elements)[next].isClassReferenceExp(); - } - (*boss.value.elements)[next] = collateral; - return oldest; - } - override void visit(TryFinallyStatement s) { debug (LOG) @@ -6638,6 +6512,137 @@ Expression interpret(Statement s, InterState* istate) return result; } +private +Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) +{ + if (exps is original) + { + if (!original) + exps = new Expressions(); + else + exps = original.copy(); + ++ctfeGlobals.numArrayAllocs; + } + return exps; +} + +/** + Given an expression e which is about to be returned from the current + function, generate an error if it contains pointers to local variables. + + Only checks expressions passed by value (pointers to local variables + may already be stored in members of classes, arrays, or AAs which + were passed as mutable function parameters). + Returns: + true if it is safe to return, false if an error was generated. + */ +private +bool stopPointersEscaping(const ref Loc loc, Expression e) +{ + if (!e.type.hasPointers()) + return true; + if (isPointer(e.type)) + { + Expression x = e; + if (auto eaddr = e.isAddrExp()) + x = eaddr.e1; + VarDeclaration v; + while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) + { + if (v.storage_class & STC.ref_) + { + x = getValue(v); + if (auto eaddr = e.isAddrExp()) + eaddr.e1 = x; + continue; + } + if (ctfeGlobals.stack.isInCurrentFrame(v)) + { + error(loc, "returning a pointer to a local stack variable"); + return false; + } + else + break; + } + // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not + // pointing to a local struct or static array. + } + if (auto se = e.isStructLiteralExp()) + { + return stopPointersEscapingFromArray(loc, se.elements); + } + if (auto ale = e.isArrayLiteralExp()) + { + return stopPointersEscapingFromArray(loc, ale.elements); + } + if (auto aae = e.isAssocArrayLiteralExp()) + { + if (!stopPointersEscapingFromArray(loc, aae.keys)) + return false; + return stopPointersEscapingFromArray(loc, aae.values); + } + return true; +} + +// Check all elements of an array for escaping local variables. Return false if error +private +bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) +{ + foreach (e; *elems) + { + if (e && !stopPointersEscaping(loc, e)) + return false; + } + return true; +} + +private +Statement findGotoTarget(InterState* istate, Identifier ident) +{ + Statement target = null; + if (ident) + { + LabelDsymbol label = istate.fd.searchLabel(ident); + assert(label && label.statement); + LabelStatement ls = label.statement; + target = ls.gotoTarget ? ls.gotoTarget : ls.statement; + } + return target; +} + +private +ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) +{ + debug (LOG) + { + printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); + } + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp boss = oldest.thrown; + const next = 5; // index of Throwable.next + assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next + ClassReferenceExp collateral = newest.thrown; + if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) + { + /* Find the index of the Error.bypassException field + */ + auto bypass = next + 1; + if ((*collateral.value.elements)[bypass].type.ty == Tuns32) + bypass += 1; // skip over _refcount field + assert((*collateral.value.elements)[bypass].type.ty == Tclass); + + // The new exception bypass the existing chain + (*collateral.value.elements)[bypass] = boss; + return newest; + } + while ((*boss.value.elements)[next].op == EXP.classReference) + { + boss = (*boss.value.elements)[next].isClassReferenceExp(); + } + (*boss.value.elements)[next] = collateral; + return oldest; +} + /** * All results destined for use outside of CTFE need to have their CTFE-specific * features removed. From 87124c4dc724dfbfba26d12c76cbe349f061c231 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 08:36:56 -0700 Subject: [PATCH 060/301] emit runtime assert error for gcc inline asm (dlang/dmd!15012) --- dmd/cparse.d | 11 +++++++++++ tests/dmd/compilable/testcstuff2.c | 17 +++++++++++++++++ tests/dmd/fail_compilation/gccasm1.c | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/dmd/fail_compilation/gccasm1.c diff --git a/dmd/cparse.d b/dmd/cparse.d index ebc2b73c10c..d9fbe47e8e8 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3195,15 +3195,26 @@ final class CParser(AST) : Parser!AST nextToken(); check(TOK.leftParenthesis); + if (token.value != TOK.string_) + error("string literal expected for Assembler Template, not `%s`", token.toChars()); Token* toklist = null; Token** ptoklist = &toklist; //Identifier label = null; auto statements = new AST.Statements(); + + int parens; while (1) { switch (token.value) { + case TOK.leftParenthesis: + ++parens; + goto default; + case TOK.rightParenthesis: + --parens; + if (parens >= 0) + goto default; break; case TOK.semicolon: diff --git a/tests/dmd/compilable/testcstuff2.c b/tests/dmd/compilable/testcstuff2.c index 44e7d1c18c0..5886257f786 100644 --- a/tests/dmd/compilable/testcstuff2.c +++ b/tests/dmd/compilable/testcstuff2.c @@ -713,3 +713,20 @@ enum E2 { m1, m2 = m1 }; + +/************************************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23725 + +#define __fldcw(addr) asm volatile("fldcw %0" : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void test23725() +{ + __fnldcw(1, 2); +} diff --git a/tests/dmd/fail_compilation/gccasm1.c b/tests/dmd/fail_compilation/gccasm1.c new file mode 100644 index 00000000000..3ba12bba057 --- /dev/null +++ b/tests/dmd/fail_compilation/gccasm1.c @@ -0,0 +1,18 @@ +/* TEST_OUTPUT: +--- +fail_compilation/gccasm1.c(12): Error: string literal expected for Assembler Template, not `%` +--- + */ + +#define __fldcw(addr) asm volatile(%0 : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void main() +{ + __fnldcw(1, 2); +} From 6d739096513829bb2b0e474b0ed904906ead6130 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Tue, 21 Mar 2023 22:35:20 +0100 Subject: [PATCH 061/301] Fix issue 14891 - profilgc to stdout not working Improves error message for diagnostics and tests it --- runtime/druntime/src/rt/profilegc.d | 11 +++++++++-- tests/dmd/runnable/profilegc_stdout.d | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/runnable/profilegc_stdout.d diff --git a/runtime/druntime/src/rt/profilegc.d b/runtime/druntime/src/rt/profilegc.d index 45e0d51b711..b97a5c5437b 100644 --- a/runtime/druntime/src/rt/profilegc.d +++ b/runtime/druntime/src/rt/profilegc.d @@ -15,6 +15,7 @@ module rt.profilegc; private: +import core.stdc.errno; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; @@ -151,7 +152,7 @@ shared static ~this() { qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp); - FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w"); + FILE* fp = logfilename == "\0" ? stdout : fopen((logfilename).ptr, "w"); if (fp) { fprintf(fp, "bytes allocated, allocations, type, function, file:line\n"); @@ -165,6 +166,12 @@ shared static ~this() fclose(fp); } else - fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr); + { + const err = errno; + fprintf(stderr, "cannot write profilegc log file '%.*s' (errno=%d)", + cast(int) logfilename.length, + logfilename.ptr, + cast(int) err); + } } } diff --git a/tests/dmd/runnable/profilegc_stdout.d b/tests/dmd/runnable/profilegc_stdout.d new file mode 100644 index 00000000000..31f23ce1177 --- /dev/null +++ b/tests/dmd/runnable/profilegc_stdout.d @@ -0,0 +1,18 @@ +/* +REQUIRED_ARGS: -profile=gc +RUN_OUTPUT: +--- +bytes allocated, allocations, type, function, file:line + 96 1 ubyte[] D main runnable/profilegc_stdout.d:17 +--- +*/ + +import core.runtime; + +void main() +{ + // test that stdout output works + profilegc_setlogfilename(""); + + ubyte[] arr = new ubyte[64]; +} From 665451b2844858aa7c1417b30a635c5b26e14102 Mon Sep 17 00:00:00 2001 From: drpriver Date: Wed, 22 Mar 2023 16:36:54 -0700 Subject: [PATCH 062/301] Fix Issue 23802 - ImportC: __volatile__ is yet another alias for volatile Some system headers on macOS use this GCC pre-ansi spelling of volatile. --- runtime/druntime/src/importc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 1ada6f0ea50..29b9389d6b2 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -29,6 +29,7 @@ #define __asm asm #define __inline__ inline #define __inline inline +#define __volatile__ volatile /******************** * Clang nullability extension used by macOS headers. From fb94807befcf2a78a491fecba9793d699cfba127 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 22 Mar 2023 23:56:48 -0700 Subject: [PATCH 063/301] complex.h should compile with FreeBSD now (dlang/dmd!15003) --- tests/dmd/compilable/stdcheaders.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 90c0339a841..72d6bf4be8e 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -5,10 +5,8 @@ #include #ifndef __DMC__ // D:\a\1\s\tools\dm\include\complex.h(105): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -#ifndef __FreeBSD__ // defines _COMPLEX_I with use of `i` postfix #include #endif -#endif #include #include From d161538fcfa15e89e2d08b619462de394d57eb17 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 23 Mar 2023 22:18:10 -0700 Subject: [PATCH 064/301] fix Issue 23784 - ImportC: __ptr32, __ptr64 --- runtime/druntime/src/importc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 29b9389d6b2..effed96e4d5 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -107,6 +107,8 @@ #if _MSC_VER //#undef _Post_writable_size //#define _Post_writable_size(x) // consider #include +#define __ptr32 +#define __ptr64 #endif /**************************** From a0c165058d2309a6eb71b5722bb34e5cabdd6267 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 24 Mar 2023 00:32:21 -0700 Subject: [PATCH 065/301] fix Issue 23795 - Cannot cast _Complex\!double to _Complex\!float (dlang/dmd!15032) --- tests/dmd/compilable/testcomplex.i | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i index 9946f26d562..3cc0021fa0e 100644 --- a/tests/dmd/compilable/testcomplex.i +++ b/tests/dmd/compilable/testcomplex.i @@ -8,6 +8,12 @@ _Complex float testf() return x; } +_Complex float testf2() +{ + _Complex float x = (float _Complex)1.0i; + return x; +} + _Complex double testd() { _Complex double x = 1.0i; @@ -25,3 +31,5 @@ _Complex float testcast() _Complex double y = 1.0i; return (_Complex float)y; } + +_Static_assert((float _Complex)1.0i == 1.0i, "1"); From e1fd61e46b895bec35f463bef7049ed27a9d7add Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 24 Mar 2023 01:23:20 -0700 Subject: [PATCH 066/301] dinterpret.d replace Statement Visitor with mixin part 1 (dlang/dmd!15031) Merging as to unblock other work. --- dmd/dinterpret.d | 111 ++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index 8288d740d9b..17b15bf0bfd 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -642,7 +642,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta e = CTFEExp.cantexp; break; } - e = interpret(pue, fd.fbody, &istatex); + e = interpretStatement(pue, fd.fbody, &istatex); if (CTFEExp.isCantExp(e)) { debug (LOG) @@ -819,7 +819,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - result = interpret(pue, sx, istate); + result = interpretStatement(pue, sx, istate); if (result) break; } @@ -842,7 +842,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - Expression e = interpret(pue, sx, istate); + Expression e = interpretStatement(pue, sx, istate); if (!e) // succeeds to interpret, or goto target was not found continue; if (exceptionOrCant(e)) @@ -887,9 +887,9 @@ public: if (istate.start) { Expression e = null; - e = interpret(s.ifbody, istate); + e = interpretStatement(s.ifbody, istate); if (!e && istate.start) - e = interpret(s.elsebody, istate); + e = interpretStatement(s.elsebody, istate); result = e; return; } @@ -901,9 +901,9 @@ public: return; if (isTrueBool(e)) - result = interpret(pue, s.ifbody, istate); + result = interpretStatement(pue, s.ifbody, istate); else if (e.toBool().hasValue(false)) - result = interpret(pue, s.elsebody, istate); + result = interpretStatement(pue, s.elsebody, istate); else { // no error, or assert(0)? @@ -920,7 +920,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } override void visit(ReturnStatement s) @@ -1057,7 +1057,7 @@ public: while (1) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1117,7 +1117,7 @@ public: istate.start = null; UnionExp ueinit = void; - Expression ei = interpret(&ueinit, s._init, istate); + Expression ei = interpretStatement(&ueinit, s._init, istate); if (exceptionOrCant(ei)) return; assert(!ei); // s.init never returns from function, or jumps out from it @@ -1136,7 +1136,7 @@ public: assert(isTrueBool(e)); } - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1200,7 +1200,7 @@ public: istate.start = null; if (istate.start) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (istate.start) // goto target was not found return; if (exceptionOrCant(e)) @@ -1250,7 +1250,7 @@ public: /* Jump to scase */ istate.start = scase; - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); assert(!istate.start); // jump must not fail if (e && e.op == EXP.break_) { @@ -1275,7 +1275,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } override void visit(DefaultStatement s) @@ -1288,7 +1288,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } override void visit(GotoStatement s) @@ -1357,7 +1357,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } override void visit(TryCatchStatement s) @@ -1371,18 +1371,18 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); foreach (ca; *s.catches) { if (e || !istate.start) // goto target was found break; - e = interpret(pue, ca.handler, istate); + e = interpretStatement(pue, ca.handler, istate); } result = e; return; } - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); // An exception was thrown if (e && e.isThrownExceptionExp()) @@ -1403,7 +1403,7 @@ public: ctfeGlobals.stack.push(ca.var); setValue(ca.var, ex.thrown); } - e = interpret(ca.handler, istate); + e = interpretStatement(ca.handler, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1415,7 +1415,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression eh = interpret(ca.handler, &istatex); + Expression eh = interpretStatement(ca.handler, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -1439,14 +1439,14 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); // Jump into/out from finalbody is disabled in semantic analysis. // and jump inside will be handled by the ScopeStatement == finalbody. result = e; return; } - Expression ex = interpret(s._body, istate); + Expression ex = interpretStatement(s._body, istate); if (CTFEExp.isCantExp(ex)) { result = ex; @@ -1459,7 +1459,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression bex = interpret(s._body, &istatex); + Expression bex = interpretStatement(s._body, &istatex); if (istatex.start) { // The goto target is outside the current scope. @@ -1475,7 +1475,7 @@ public: ex = bex; } - Expression ey = interpret(s.finalbody, istate); + Expression ey = interpretStatement(s.finalbody, istate); if (CTFEExp.isCantExp(ey)) { result = ey; @@ -1505,27 +1505,7 @@ public: istate.start = null; } - interpretThrow(s.exp, s.loc); - } - - /// Interpret `throw ` found at the specified location `loc` - private void interpretThrow(Expression exp, const ref Loc loc) - { - incUsageCtfe(istate, loc); - - Expression e = interpretRegion(exp, istate); - if (exceptionOrCant(e)) - return; - - if (e.op == EXP.classReference) - { - result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); - } - else - { - exp.error("to be thrown `%s` must be non-null", exp.toChars()); - result = ErrorExp.get(); - } + interpretThrow(result, s.exp, s.loc, istate); } override void visit(ScopeGuardStatement s) @@ -1543,14 +1523,14 @@ public: istate.start = null; if (istate.start) { - result = s._body ? interpret(s._body, istate) : null; + result = s._body ? interpretStatement(s._body, istate) : null; return; } // If it is with(Enum) {...}, just execute the body. if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type) { - result = interpret(pue, s._body, istate); + result = interpretStatement(pue, s._body, istate); return; } @@ -1566,7 +1546,7 @@ public: } ctfeGlobals.stack.push(s.wthis); setValue(s.wthis, e); - e = interpret(s._body, istate); + e = interpretStatement(s._body, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1578,7 +1558,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression ex = interpret(s._body, &istatex); + Expression ex = interpretStatement(s._body, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -6082,7 +6062,7 @@ public: { printf("%s ThrowExpression::interpret()\n", te.loc.toChars()); } - interpretThrow(te.e1, te.loc); + interpretThrow(result, te.e1, te.loc, istate); } override void visit(PtrExp e) @@ -6417,6 +6397,29 @@ public: } } +/// Interpret `throw ` found at the specified location `loc` +private void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +{ + incUsageCtfe(istate, loc); + + Expression e = interpretRegion(exp, istate); + if (exceptionOrCantInterpret(e)) + { + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + } + else if (e.op == EXP.classReference) + { + result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); + } + else + { + exp.error("to be thrown `%s` must be non-null", exp.toChars()); + result = ErrorExp.get(); + } +} + + /******************************************** * Interpret the expression. * Params: @@ -6493,7 +6496,7 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF * EXP.cantExpression cannot interpret statement at compile time * !NULL expression from return statement, or thrown exception */ -Expression interpret(UnionExp* pue, Statement s, InterState* istate) +Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) { if (!s) return null; @@ -6503,10 +6506,10 @@ Expression interpret(UnionExp* pue, Statement s, InterState* istate) } /// -Expression interpret(Statement s, InterState* istate) +Expression interpretStatement(Statement s, InterState* istate) { UnionExp ue = void; - auto result = interpret(&ue, s, istate); + auto result = interpretStatement(&ue, s, istate); if (result == ue.exp()) result = ue.copy(); return result; From 9cb9c91413299416755797564cdcedced40127b6 Mon Sep 17 00:00:00 2001 From: Temtaime Date: Fri, 24 Mar 2023 20:47:27 +0300 Subject: [PATCH 067/301] Fix Issue 23808 - #include is not working with importc --- dmd/cparse.d | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dmd/cparse.d b/dmd/cparse.d index d9fbe47e8e8..72e41ddbd56 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3143,6 +3143,8 @@ final class CParser(AST) : Parser!AST cparseParens(); } } + else if (token.value == Id.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. + nextToken(); else { error("extended-decl-modifier expected"); From aff98680d1e6da92b8a08f296a4c96b870a3d279 Mon Sep 17 00:00:00 2001 From: Temtaime Date: Fri, 24 Mar 2023 20:49:49 +0300 Subject: [PATCH 068/301] Update cparse.d --- dmd/cparse.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 72e41ddbd56..d9998ebaf85 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3143,7 +3143,7 @@ final class CParser(AST) : Parser!AST cparseParens(); } } - else if (token.value == Id.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. + else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. nextToken(); else { From 2e087b258580e91cc120bbb329f54e32e9c1b3e4 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 24 Mar 2023 14:26:29 -0700 Subject: [PATCH 069/301] add missing cases to statementToBuffer() (dlang/dmd!15035) --- dmd/hdrgen.d | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 1f3b11282d7..8fe2d9f597a 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -136,8 +136,8 @@ private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) { void visitDefaultCase(Statement s) { - s.error("visitDefaultCase() %d for %s", s.stmt, s.toChars()); - assert(0); + printf("Statement::toCBuffer() %d\n", s.stmt); + assert(0, "unrecognized statement in statementToBuffer()"); } void visitError(ErrorStatement s) @@ -771,6 +771,16 @@ private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) buf.writenl(); } + void visitInlineAsm(InlineAsmStatement s) + { + visitAsm(s); + } + + void visitGccAsm(GccAsmStatement s) + { + visitAsm(s); + } + void visitImport(ImportStatement s) { foreach (imp; *s.imports) From eb8b6c94a75112802d47bcc060370c75b5409a5c Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 24 Mar 2023 18:58:23 -0700 Subject: [PATCH 070/301] fix Issue 23787 - ImportC: __unaligned --- runtime/druntime/src/importc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index effed96e4d5..413a47680b7 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -109,6 +109,7 @@ //#define _Post_writable_size(x) // consider #include #define __ptr32 #define __ptr64 +#define __unaligned #endif /**************************** From 65e922af42e4fc554081a53f51adbf68b1c4ae71 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 24 Mar 2023 18:53:18 -0700 Subject: [PATCH 071/301] __FreeBSD__ compiles fenv.h --- tests/dmd/compilable/stdcheaders.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 72d6bf4be8e..eaf18820779 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -12,10 +12,8 @@ #include #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\fenv.h(68): Error: variable `stdcheaders._Fenv1` extern symbols cannot have initializers -#ifndef __FreeBSD__ // cannot turn off __GNUCLIKE_ASM in machine/ieeefp.h #include #endif -#endif #include #include From e168b5d4b1026280436761fc2bc1e25ac90fcaa3 Mon Sep 17 00:00:00 2001 From: Temtaime Date: Sat, 25 Mar 2023 21:05:19 +0300 Subject: [PATCH 072/301] Add tests --- tests/dmd/compilable/testcstuff1.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/dmd/compilable/testcstuff1.c b/tests/dmd/compilable/testcstuff1.c index 19db4f29174..2e37e49be74 100644 --- a/tests/dmd/compilable/testcstuff1.c +++ b/tests/dmd/compilable/testcstuff1.c @@ -201,6 +201,13 @@ _Static_assert(testexpinit() == 1 + 2 + 3, "ok"); /********************************/ +__declspec(restrict) void* testrestrictdeclspec() +{ + return 0; +} + +/********************************/ + // Character literals _Static_assert(sizeof('a') == 4, "ok"); _Static_assert(sizeof(u'a') == 4, "ok"); From fe277a49c9ea03fcb582c77a6217277c251560f5 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 25 Mar 2023 21:49:00 -0700 Subject: [PATCH 073/301] Importc: FreeBSD compiles stdatomic.h (dlang/dmd!15041) --- dmd/cparse.d | 3 ++- runtime/druntime/src/importc.h | 2 ++ tests/dmd/compilable/stdcheaders.c | 2 -- tests/dmd/compilable/testcstuff2.c | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index d9998ebaf85..471fa82ae32 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2307,9 +2307,9 @@ final class CParser(AST) : Parser!AST tk = peek(tk); if (isTypeName(tk) && tk.value == TOK.rightParenthesis) { + nextToken(); nextToken(); t = cparseTypeName(); - // TODO - implement the "atomic" part of t tkwx = TKW.x_Atomic; break; } @@ -2578,6 +2578,7 @@ final class CParser(AST) : Parser!AST } case TKW.xtag: + case TKW.x_Atomic: // no atomics for you break; // t is already set default: diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 413a47680b7..d19e0be82b4 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -102,6 +102,8 @@ #if __FreeBSD__ #define __volatile volatile +#define __sync_synchronize() +#define __sync_swap(A, B) 1 #endif #if _MSC_VER diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index eaf18820779..404402db6f1 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -41,13 +41,11 @@ #ifndef __linux__ #ifndef _MSC_VER #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.0/include/stdatomic.h(80): Error: type-specifier is missing -#ifndef __FreeBSD__ // /stdatomic.h(162): Error: found `volatile` when expecting `{` #include #endif #endif #endif #endif -#endif #include #include diff --git a/tests/dmd/compilable/testcstuff2.c b/tests/dmd/compilable/testcstuff2.c index 5886257f786..e80f6a53244 100644 --- a/tests/dmd/compilable/testcstuff2.c +++ b/tests/dmd/compilable/testcstuff2.c @@ -388,6 +388,9 @@ __attribute__((static, unsigned, long, const, extern, register, typedef, short, _Thread_local, int, char, float, double, void, _Bool, _Atomic)) int test22196(); +_Atomic(_Bool) atomicbool; + + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=22245 From 8bc2fdf8d18fa4a93cad0a6ce919830dfb0cae70 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 25 Mar 2023 23:16:54 -0700 Subject: [PATCH 074/301] ImportC: #include on FreeBSD --- tests/dmd/compilable/stdcheaders.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 404402db6f1..ea193c36e40 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -65,12 +65,10 @@ #ifndef __DMC__ // no tgmath.h #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` -#ifndef __FreeBSD__ // #includes complex.h #include #endif #endif #endif -#endif #ifndef __DMC__ #ifndef __linux__ From d7d04d76822658db9298c43f82ce6ecea65f99f9 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 26 Mar 2023 14:50:26 -0700 Subject: [PATCH 075/301] fix Issue 23789 - ImportC: __declspec(align(n)) (dlang/dmd!15036) * fix Issue 23789 - ImportC: __declspec(align(n)) * fix Issue 23789 - ImportC: __declspec(align(n)) --- dmd/cparse.d | 37 ++++++++++++++++++++++++++ dmd/frontend.h | 1 + dmd/id.d | 1 + tests/dmd/compilable/imports/c23789.i | 15 +++++++++++ tests/dmd/compilable/test23789.d | 5 ++++ tests/dmd/fail_compilation/test23789.c | 14 ++++++++++ 6 files changed, 73 insertions(+) create mode 100644 tests/dmd/compilable/imports/c23789.i create mode 100644 tests/dmd/compilable/test23789.d create mode 100644 tests/dmd/fail_compilation/test23789.c diff --git a/dmd/cparse.d b/dmd/cparse.d index 471fa82ae32..62468cff480 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2048,6 +2048,10 @@ final class CParser(AST) : Parser!AST error("storage class and type are not allowed in identifier-list"); foreach (s; (*symbols)[]) // yes, quadratic { + auto ad = s.isAttribDeclaration(); + if (ad) + s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration + auto d = s.isDeclaration(); if (d && p.ident == d.ident && d.type) { @@ -2287,6 +2291,9 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.__attribute__) cparseGnuAttributes(specifier); + if (token.value == TOK.__declspec) + cparseDeclspec(specifier); + t = cparseStruct(sloc, structOrUnion, symbols); tkwx = TKW.xtag; break; @@ -3095,6 +3102,7 @@ final class CParser(AST) : Parser!AST * extended-decl-modifier extended-decl-modifier-seq * * extended-decl-modifier: + * align(number) * dllimport * dllexport * noreturn @@ -3137,6 +3145,28 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._align) + { + // Microsoft spec is very imprecise as to how this actually works + nextToken(); + check(TOK.leftParenthesis); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || ushort.max < n) + error("__decspec(align(%lld)) must be an integer positive power of 2", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } else { nextToken(); @@ -4100,6 +4130,13 @@ final class CParser(AST) : Parser!AST case TOK.union_: case TOK.enum_: t = peek(t); + if (t.value == TOK.__attribute__ || + t.value == TOK.__declspec) + { + t = peek(t); + if (!skipParens(t, &t)) + return false; + } if (t.value == TOK.identifier) { t = peek(t); diff --git a/dmd/frontend.h b/dmd/frontend.h index a6d353e4030..d83c4264ead 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8838,6 +8838,7 @@ struct Id final static Identifier* dllexport; static Identifier* vector_size; static Identifier* noreturn; + static Identifier* _align; static Identifier* builtins; static Identifier* builtin_va_list; static Identifier* builtin_va_arg; diff --git a/dmd/id.d b/dmd/id.d index 9ccbc025910..7a720eaab0c 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -526,6 +526,7 @@ immutable Msgtable[] msgtable = { "vector_size" }, { "__func__" }, { "noreturn" }, + { "_align", "align" }, { "__pragma", "pragma" }, { "builtins", "__builtins" }, { "builtin_va_list", "__builtin_va_list" }, diff --git a/tests/dmd/compilable/imports/c23789.i b/tests/dmd/compilable/imports/c23789.i new file mode 100644 index 00000000000..e219f05ffaa --- /dev/null +++ b/tests/dmd/compilable/imports/c23789.i @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +struct __declspec(align(64)) M128A { + char c; +}; + +typedef struct __declspec(align(16)) _M128B { + int x; +} M128B, *PM128A; + + +void testpl(p) +struct __declspec(align(2)) S *p; +{ +} diff --git a/tests/dmd/compilable/test23789.d b/tests/dmd/compilable/test23789.d new file mode 100644 index 00000000000..b97f17e291a --- /dev/null +++ b/tests/dmd/compilable/test23789.d @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +import imports.c23789; + +static assert(M128A.alignof == 64); diff --git a/tests/dmd/fail_compilation/test23789.c b/tests/dmd/fail_compilation/test23789.c new file mode 100644 index 00000000000..1c813da6b12 --- /dev/null +++ b/tests/dmd/fail_compilation/test23789.c @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23789.c(101): Error: __decspec(align(3)) must be an integer positive power of 2 +fail_compilation/test23789.c(103): Error: alignment value expected, not `"a"` +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=23789 + +#line 100 + +struct __declspec(align(3)) S { int a; }; + +struct __declspec(align("a")) T { int a; }; From c792a3386e2f382f8e8cc96b187fe01c953f8d18 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 26 Mar 2023 14:51:45 -0700 Subject: [PATCH 076/301] statementsem.d: replace Visitor with mixin (dlang/dmd!15027) --- dmd/expressionsem.d | 2 +- dmd/statementsem.d | 829 ++++++++++++++++++++++---------------------- 2 files changed, 417 insertions(+), 414 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index e6caab64974..636807edddc 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6671,7 +6671,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; - if (StatementSemanticVisitor.throwSemantic(te.loc, te.e1, sc)) + if (throwSemantic(te.loc, te.e1, sc)) result = te; else setError(); diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 6c0f3aad1af..9d09ed1a76d 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -145,43 +145,35 @@ extern(C++) Statement statementSemantic(Statement s, Scope* sc) version (CallbackAPI) Compiler.onStatementSemanticStart(s, sc); - scope v = new StatementSemanticVisitor(sc); - s.accept(v); + Statement result = statementSemanticVisit(s, sc); version (CallbackAPI) Compiler.onStatementSemanticDone(s, sc); - return v.result; + return result; } -package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor +package (dmd) +Statement statementSemanticVisit(Statement s, Scope* sc) { - alias visit = Visitor.visit; - Statement result; - Scope* sc; - - this(Scope* sc) scope - { - this.sc = sc; - } - private void setError() + void setError() { result = new ErrorStatement(); } - override void visit(Statement s) + void visitDefaultCase(Statement s) { result = s; } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = s; } - override void visit(PeelStatement s) + void visitPeel(PeelStatement s) { /* "peel" off this wrapper, and don't run semantic() * on the result. @@ -189,7 +181,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.s; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { /* https://dlang.org/spec/statement.html#expression-statement */ @@ -226,7 +218,12 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(MixinStatement cs) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement cs) { /* https://dlang.org/spec/statement.html#mixin-statement */ @@ -239,7 +236,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.statementSemantic(sc); } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc); version (none) @@ -431,7 +428,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cs; } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc); Scope* scd = sc.push(); @@ -454,7 +451,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = serror ? serror : uls; } - override void visit(ScopeStatement ss) + void visitScope(ScopeStatement ss) { //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (!ss.statement) @@ -501,7 +498,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss; } - override void visit(ForwardingStatement ss) + void visitForwarding(ForwardingStatement ss) { assert(ss.sym); for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing) @@ -517,7 +514,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss.statement; } - override void visit(WhileStatement ws) + void visitWhile(WhileStatement ws) { /* Rewrite as a for(;condition;) loop * https://dlang.org/spec/statement.html#while-statement @@ -544,7 +541,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(DoStatement ds) + void visitDo(DoStatement ds) { /* https://dlang.org/spec/statement.html#do-statement */ @@ -580,7 +577,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ds; } - override void visit(ForStatement fs) + void visitFor(ForStatement fs) { /* https://dlang.org/spec/statement.html#for-statement */ @@ -674,7 +671,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = fs; } - override void visit(ForeachStatement fs) + void visitForeach(ForeachStatement fs) { /* https://dlang.org/spec/statement.html#foreach-statement */ @@ -1399,312 +1396,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } } - private static extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Dsymbol sapply) - { - version (none) - { - if (global.params.useDIP1000 == FeatureState.enabled) - { - message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); - } - (cast(FuncExp)flde).fd.tookAddressOf = 1; - } - else - { - if (global.params.useDIP1000 == FeatureState.enabled) - ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' - } - assert(tab.ty == Tstruct || tab.ty == Tclass); - assert(sapply); - /* Call: - * aggr.apply(flde) - */ - Expression ec; - ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); - ec = new CallExp(fs.loc, ec, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2) - { - Expression ec; - /* Call: - * aggr(flde) - */ - if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && - !(cast(DelegateExp)fs.aggr).func.needThis()) - { - // https://issues.dlang.org/show_bug.cgi?id=3560 - fs.aggr = (cast(DelegateExp)fs.aggr).e1; - } - ec = new CallExp(fs.loc, fs.aggr, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Type tn, Type tnv) - { - Expression ec; - const dim = fs.parameters.length; - const loc = fs.loc; - /* Call: - * _aApply(aggr, flde) - */ - static immutable fntab = - [ - "cc", "cw", "cd", - "wc", "cc", "wd", - "dc", "dw", "dd" - ]; - - const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; - char[BUFFER_LEN] fdname; - int flag; - - switch (tn.ty) - { - case Tchar: flag = 0; break; - case Twchar: flag = 3; break; - case Tdchar: flag = 6; break; - default: - assert(0); - } - switch (tnv.ty) - { - case Tchar: flag += 0; break; - case Twchar: flag += 1; break; - case Tdchar: flag += 2; break; - default: - assert(0); - } - const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; - int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); - assert(j < BUFFER_LEN); - - FuncDeclaration fdapply; - TypeDelegate dgty; - auto params = new Parameters(); - params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, dgty, null, null, null)); - fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); - - if (tab.isTypeSArray()) - fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!dgty.equals(flde.type)) - { - fexp = new CastExp(loc, flde, flde.type); - fexp.type = dgty; - } - ec = new VarExp(Loc.initial, fdapply, false); - ec = new CallExp(loc, ec, fs.aggr, fexp); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) - { - auto taa = tab.isTypeAArray(); - Expression ec; - const dim = fs.parameters.length; - // Check types - Parameter p = (*fs.parameters)[0]; - bool isRef = (p.storageClass & STC.ref_) != 0; - Type ta = p.type; - if (dim == 2) - { - Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); - if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) - { - fs.error("`foreach`: index must be type `%s`, not `%s`", - ti.toChars(), ta.toChars()); - return null; - } - p = (*fs.parameters)[1]; - isRef = (p.storageClass & STC.ref_) != 0; - ta = p.type; - } - Type taav = taa.nextOf(); - if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) - { - fs.error("`foreach`: value must be type `%s`, not `%s`", - taav.toChars(), ta.toChars()); - return null; - } - - /* Call: - * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) - * _aaApply(aggr, keysize, flde) - * - * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) - * _aaApply2(aggr, keysize, flde) - */ - __gshared FuncDeclaration* fdapply = [null, null]; - __gshared TypeDelegate* fldeTy = [null, null]; - ubyte i = (dim == 2 ? 1 : 0); - if (!fdapply[i]) - { - auto params = new Parameters(); - params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); - params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, fldeTy[i], null, null, null)); - fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); - } - - auto exps = new Expressions(); - exps.push(fs.aggr); - auto keysize = taa.index.size(); - if (keysize == SIZE_INVALID) - return null; - assert(keysize < keysize.max - target.ptrsize); - keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!fldeTy[i].equals(flde.type)) - { - fexp = new CastExp(fs.loc, flde, flde.type); - fexp.type = fldeTy[i]; - } - exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); - exps.push(fexp); - ec = new VarExp(Loc.initial, fdapply[i], false); - ec = new CallExp(fs.loc, ec, exps); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) - { - if (!cases.length) - { - // Easy case, a clean exit from the loop - e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 - return new ExpStatement(loc, e); - } - // Construct a switch statement around the return value - // of the apply function. - Statement s; - auto a = new Statements(); - - // default: break; takes care of cases 0 and 1 - s = new BreakStatement(Loc.initial, null); - s = new DefaultStatement(Loc.initial, s); - a.push(s); - - // cases 2... - foreach (i, c; *cases) - { - s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); - a.push(s); - } - - s = new CompoundStatement(loc, a); - return new SwitchStatement(loc, e, s, false); - } - /************************************* - * Turn foreach body into the function literal: - * int delegate(ref T param) { body } - * Params: - * sc = context - * fs = ForeachStatement - * tfld = type of function literal to be created (type of opApply() function if any), can be null - * Returns: - * Function literal created, as an expression - * null if error. - */ - static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld) - { - auto params = new Parameters(); - foreach (i, p; *fs.parameters) - { - StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); - Identifier id; - - p.type = p.type.typeSemantic(fs.loc, sc); - p.type = p.type.addStorageClass(p.storageClass); - if (tfld) - { - Parameter prm = tfld.parameterList[i]; - //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); - stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); - if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) - { - if (!(prm.storageClass & STC.ref_)) - { - fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); - return null; - } - goto LcopyArg; - } - id = p.ident; // argument copy is not need. - } - else if (p.storageClass & STC.ref_) - { - // default delegate parameters are marked as ref, then - // argument copy is not need. - id = p.ident; - } - else - { - // Make a copy of the ref argument so it isn't - // a reference. - LcopyArg: - id = Identifier.generateId("__applyArg", cast(int)i); - - Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); - auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); - v.storage_class |= STC.temp | (stc & STC.scope_); - Statement s = new ExpStatement(fs.loc, v); - fs._body = new CompoundStatement(fs.loc, s, fs._body); - } - params.push(new Parameter(stc, p.type, id, null, null)); - } - // https://issues.dlang.org/show_bug.cgi?id=13840 - // Throwable nested function inside nothrow function is acceptable. - StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); - auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); - fs.cases = new Statements(); - fs.gotos = new ScopeStatements(); - auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); - fld.fbody = fs._body; - Expression flde = new FuncExp(fs.loc, fld); - flde = flde.expressionSemantic(sc); - fld.tookAddressOf = 0; - if (flde.op == EXP.error) - return null; - return cast(FuncExp)flde; - } - - override void visit(ForeachRangeStatement fs) + void visitForeachRange(ForeachRangeStatement fs) { /* https://dlang.org/spec/statement.html#foreach-range-statement */ @@ -1890,7 +1582,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.statementSemantic(sc); } - override void visit(IfStatement ifs) + void visitIf(IfStatement ifs) { /* https://dlang.org/spec/statement.html#IfStatement */ @@ -2006,7 +1698,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ifs; } - override void visit(ConditionalStatement cs) + void visitConditional(ConditionalStatement cs) { //printf("ConditionalStatement::semantic()\n"); @@ -2035,7 +1727,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } } - override void visit(PragmaStatement ps) + void visitPragma(PragmaStatement ps) { /* https://dlang.org/spec/statement.html#pragma-statement */ @@ -2119,14 +1811,14 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ps._body; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.semantic2(sc); if (s.sa.errors) return setError(); } - override void visit(SwitchStatement ss) + void visitSwitch(SwitchStatement ss) { /* https://dlang.org/spec/statement.html#switch-statement */ @@ -2440,7 +2132,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss; } - override void visit(CaseStatement cs) + void visitCase(CaseStatement cs) { SwitchStatement sw = sc.sw; bool errors = false; @@ -2586,7 +2278,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cs; } - override void visit(CaseRangeStatement crs) + void visitCaseRange(CaseRangeStatement crs) { SwitchStatement sw = sc.sw; if (sw is null) @@ -2669,7 +2361,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(DefaultStatement ds) + void visitDefault(DefaultStatement ds) { //printf("DefaultStatement::semantic()\n"); bool errors = false; @@ -2713,7 +2405,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ds; } - override void visit(GotoDefaultStatement gds) + void visitGotoDefault(GotoDefaultStatement gds) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2732,7 +2424,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = gds; } - override void visit(GotoCaseStatement gcs) + void visitGotoCase(GotoCaseStatement gcs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2756,7 +2448,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = gcs; } - override void visit(ReturnStatement rs) + void visitReturn(ReturnStatement rs) { /* https://dlang.org/spec/statement.html#return-statement */ @@ -3149,7 +2841,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = rs; } - override void visit(BreakStatement bs) + void visitBreak(BreakStatement bs) { /* https://dlang.org/spec/statement.html#break-statement */ @@ -3227,7 +2919,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = bs; } - override void visit(ContinueStatement cs) + void visitContinue(ContinueStatement cs) { /* https://dlang.org/spec/statement.html#continue-statement */ @@ -3314,7 +3006,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cs; } - override void visit(SynchronizedStatement ss) + void visitSynchronized(SynchronizedStatement ss) { /* https://dlang.org/spec/statement.html#synchronized-statement */ @@ -3436,7 +3128,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } } - override void visit(WithStatement ws) + void visitWith(WithStatement ws) { /* https://dlang.org/spec/statement.html#with-statement */ @@ -3551,7 +3243,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } // https://dlang.org/spec/statement.html#TryStatement - override void visit(TryCatchStatement tcs) + void visitTryCatch(TryCatchStatement tcs) { //printf("TryCatchStatement.semantic()\n"); @@ -3655,7 +3347,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = tcs; } - override void visit(TryFinallyStatement tfs) + void visitTryFinally(TryFinallyStatement tfs) { //printf("TryFinallyStatement::semantic()\n"); tfs.tryBody = sc.tryBody; // chain on in-flight tryBody @@ -3695,7 +3387,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = tfs; } - override void visit(ScopeGuardStatement oss) + void visitScopeGuard(ScopeGuardStatement oss) { /* https://dlang.org/spec/statement.html#scope-guard-statement */ @@ -3745,7 +3437,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = oss; } - override void visit(ThrowStatement ts) + void visitThrow(ThrowStatement ts) { /* https://dlang.org/spec/statement.html#throw-statement */ @@ -3758,63 +3450,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } - /** - * Run semantic on `throw `. - * - * Params: - * loc = location of the `throw` - * exp = value to be thrown - * sc = enclosing scope - * - * Returns: true if the `throw` is valid, or false if an error was found - */ - extern(D) static bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) - { - if (!global.params.useExceptions) - { - loc.error("cannot use `throw` statements with -betterC"); - return false; - } - - if (!ClassDeclaration.throwable) - { - loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); - return false; - } - - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - - if (exp.op == EXP.new_) - { - NewExp ne = cast(NewExp) exp; - ne.thrownew = true; - } - - exp = exp.expressionSemantic(sc); - exp = resolveProperties(sc, exp); - exp = checkGC(sc, exp); - if (exp.op == EXP.error) - return false; - if (!exp.type.isNaked()) - { - // @@@DEPRECATED_2.112@@@ - // Deprecated in 2.102, change into an error & return false in 2.112 - exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); - //return false; - } - checkThrowEscape(sc, exp, false); - - ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); - if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) - { - loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); - return false; - } - return true; - } - - override void visit(DebugStatement ds) + void visitDebug(DebugStatement ds) { if (ds.statement) { @@ -3826,7 +3462,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ds.statement; } - override void visit(GotoStatement gs) + void visitGoto(GotoStatement gs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -3870,7 +3506,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = gs; } - override void visit(LabelStatement ls) + void visitLabel(LabelStatement ls) { //printf("LabelStatement::semantic()\n"); FuncDeclaration fd = sc.parent.isFuncDeclaration(); @@ -3904,7 +3540,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ls; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { /* https://dlang.org/spec/statement.html#asm */ @@ -3913,7 +3549,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = asmSemantic(s, sc); } - override void visit(CompoundAsmStatement cas) + void visitCompoundAsm(CompoundAsmStatement cas) { //printf("CompoundAsmStatement()::semantic()\n"); // Apply postfix attributes of the asm block to each statement. @@ -3954,7 +3590,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cas; } - override void visit(ImportStatement imps) + void visitImport(ImportStatement imps) { /* https://dlang.org/spec/module.html#ImportDeclaration */ @@ -3993,8 +3629,375 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } result = imps; } + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/** + * Run semantic on `throw `. + * + * Params: + * loc = location of the `throw` + * exp = value to be thrown + * sc = enclosing scope + * + * Returns: true if the `throw` is valid, or false if an error was found + */ +public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) +{ + if (!global.params.useExceptions) + { + loc.error("cannot use `throw` statements with -betterC"); + return false; + } + + if (!ClassDeclaration.throwable) + { + loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); + return false; + } + + if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) + fd.hasReturnExp |= 2; + + if (exp.op == EXP.new_) + { + NewExp ne = cast(NewExp) exp; + ne.thrownew = true; + } + + exp = exp.expressionSemantic(sc); + exp = resolveProperties(sc, exp); + exp = checkGC(sc, exp); + if (exp.op == EXP.error) + return false; + if (!exp.type.isNaked()) + { + // @@@DEPRECATED_2.112@@@ + // Deprecated in 2.102, change into an error & return false in 2.112 + exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); + //return false; + } + checkThrowEscape(sc, exp, false); + + ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); + if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) + { + loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); + return false; + } + return true; +} + +private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Dsymbol sapply) +{ + version (none) + { + if (global.params.useDIP1000 == FeatureState.enabled) + { + message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); + } + (cast(FuncExp)flde).fd.tookAddressOf = 1; + } + else + { + if (global.params.useDIP1000 == FeatureState.enabled) + ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' + } + assert(tab.ty == Tstruct || tab.ty == Tclass); + assert(sapply); + /* Call: + * aggr.apply(flde) + */ + Expression ec; + ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); + ec = new CallExp(fs.loc, ec, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; +} + +private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2) +{ + Expression ec; + /* Call: + * aggr(flde) + */ + if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && + !(cast(DelegateExp)fs.aggr).func.needThis()) + { + // https://issues.dlang.org/show_bug.cgi?id=3560 + fs.aggr = (cast(DelegateExp)fs.aggr).e1; + } + ec = new CallExp(fs.loc, fs.aggr, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; } +private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Type tn, Type tnv) +{ + Expression ec; + const dim = fs.parameters.length; + const loc = fs.loc; + /* Call: + * _aApply(aggr, flde) + */ + static immutable fntab = + [ + "cc", "cw", "cd", + "wc", "cc", "wd", + "dc", "dw", "dd" + ]; + + const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; + char[BUFFER_LEN] fdname; + int flag; + + switch (tn.ty) + { + case Tchar: flag = 0; break; + case Twchar: flag = 3; break; + case Tdchar: flag = 6; break; + default: + assert(0); + } + switch (tnv.ty) + { + case Tchar: flag += 0; break; + case Twchar: flag += 1; break; + case Tdchar: flag += 2; break; + default: + assert(0); + } + const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; + int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); + assert(j < BUFFER_LEN); + + FuncDeclaration fdapply; + TypeDelegate dgty; + auto params = new Parameters(); + params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, dgty, null, null, null)); + fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); + + if (tab.isTypeSArray()) + fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!dgty.equals(flde.type)) + { + fexp = new CastExp(loc, flde, flde.type); + fexp.type = dgty; + } + ec = new VarExp(Loc.initial, fdapply, false); + ec = new CallExp(loc, ec, fs.aggr, fexp); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) +{ + auto taa = tab.isTypeAArray(); + Expression ec; + const dim = fs.parameters.length; + // Check types + Parameter p = (*fs.parameters)[0]; + bool isRef = (p.storageClass & STC.ref_) != 0; + Type ta = p.type; + if (dim == 2) + { + Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); + if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) + { + fs.error("`foreach`: index must be type `%s`, not `%s`", + ti.toChars(), ta.toChars()); + return null; + } + p = (*fs.parameters)[1]; + isRef = (p.storageClass & STC.ref_) != 0; + ta = p.type; + } + Type taav = taa.nextOf(); + if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) + { + fs.error("`foreach`: value must be type `%s`, not `%s`", + taav.toChars(), ta.toChars()); + return null; + } + + /* Call: + * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) + * _aaApply(aggr, keysize, flde) + * + * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) + * _aaApply2(aggr, keysize, flde) + */ + __gshared FuncDeclaration* fdapply = [null, null]; + __gshared TypeDelegate* fldeTy = [null, null]; + ubyte i = (dim == 2 ? 1 : 0); + if (!fdapply[i]) + { + auto params = new Parameters(); + params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, fldeTy[i], null, null, null)); + fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); + } + + auto exps = new Expressions(); + exps.push(fs.aggr); + auto keysize = taa.index.size(); + if (keysize == SIZE_INVALID) + return null; + assert(keysize < keysize.max - target.ptrsize); + keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!fldeTy[i].equals(flde.type)) + { + fexp = new CastExp(fs.loc, flde, flde.type); + fexp.type = fldeTy[i]; + } + exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); + exps.push(fexp); + ec = new VarExp(Loc.initial, fdapply[i], false); + ec = new CallExp(fs.loc, ec, exps); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) +{ + if (!cases.length) + { + // Easy case, a clean exit from the loop + e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 + return new ExpStatement(loc, e); + } + // Construct a switch statement around the return value + // of the apply function. + Statement s; + auto a = new Statements(); + + // default: break; takes care of cases 0 and 1 + s = new BreakStatement(Loc.initial, null); + s = new DefaultStatement(Loc.initial, s); + a.push(s); + + // cases 2... + foreach (i, c; *cases) + { + s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); + a.push(s); + } + + s = new CompoundStatement(loc, a); + return new SwitchStatement(loc, e, s, false); +} + +/************************************* + * Turn foreach body into the function literal: + * int delegate(ref T param) { body } + * Params: + * sc = context + * fs = ForeachStatement + * tfld = type of function literal to be created (type of opApply() function if any), can be null + * Returns: + * Function literal created, as an expression + * null if error. + */ +private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld) +{ + auto params = new Parameters(); + foreach (i, p; *fs.parameters) + { + StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); + Identifier id; + + p.type = p.type.typeSemantic(fs.loc, sc); + p.type = p.type.addStorageClass(p.storageClass); + if (tfld) + { + Parameter prm = tfld.parameterList[i]; + //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); + stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); + if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) + { + if (!(prm.storageClass & STC.ref_)) + { + fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); + return null; + } + goto LcopyArg; + } + id = p.ident; // argument copy is not need. + } + else if (p.storageClass & STC.ref_) + { + // default delegate parameters are marked as ref, then + // argument copy is not need. + id = p.ident; + } + else + { + // Make a copy of the ref argument so it isn't + // a reference. + LcopyArg: + id = Identifier.generateId("__applyArg", cast(int)i); + + Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); + auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); + v.storage_class |= STC.temp | (stc & STC.scope_); + Statement s = new ExpStatement(fs.loc, v); + fs._body = new CompoundStatement(fs.loc, s, fs._body); + } + params.push(new Parameter(stc, p.type, id, null, null)); + } + // https://issues.dlang.org/show_bug.cgi?id=13840 + // Throwable nested function inside nothrow function is acceptable. + StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); + auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); + fs.cases = new Statements(); + fs.gotos = new ScopeStatements(); + auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); + fld.fbody = fs._body; + Expression flde = new FuncExp(fs.loc, fld); + flde = flde.expressionSemantic(sc); + fld.tookAddressOf = 0; + if (flde.op == EXP.error) + return null; + return cast(FuncExp)flde; +} + + void catchSemantic(Catch c, Scope* sc) { //printf("Catch::semantic(%s)\n", ident.toChars()); From e47fc7f08841a04d51aa93a99c0ef8369f2d0682 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 26 Mar 2023 23:41:57 -0700 Subject: [PATCH 077/301] dinterpret.d: replace Statement Visitor with mixin (dlang/dmd!15026) --- dmd/dinterpret.d | 232 +++++++++++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 97 deletions(-) diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index 17b15bf0bfd..c3e95cd404f 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -738,21 +738,30 @@ void incUsageCtfe(InterState* istate, const ref Loc loc) } } -private extern (C++) final class Interpreter : Visitor +/*********************************** + * Interpret the statement. + * Params: + * s = Statement to interpret + * istate = context + * Returns: + * NULL continue to next statement + * EXP.cantExpression cannot interpret statement at compile time + * !NULL expression from return statement, or thrown exception + */ + +Expression interpretStatement(Statement s, InterState* istate) { - alias visit = Visitor.visit; -public: - InterState* istate; - CTFEGoal goal; - Expression result; - UnionExp* pue; // storage for `result` + UnionExp ue = void; + auto result = interpretStatement(&ue, s, istate); + if (result == ue.exp()) + result = ue.copy(); + return result; +} - extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope - { - this.pue = pue; - this.istate = istate; - this.goal = goal; - } +/// +Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) +{ + Expression result; // If e is EXP.throw_exception or EXP.cantExpression, // set it to 'result' and returns true. @@ -769,11 +778,11 @@ public: /******************************** Statement ***************************/ - override void visit(Statement s) + void visitDefaultCase(Statement s) { debug (LOG) { - printf("%s Statement::interpret()\n", s.loc.toChars()); + printf("%s Statement::interpret() %s\n", s.loc.toChars(), s.toChars()); } if (istate.start) { @@ -786,7 +795,7 @@ public: result = CTFEExp.cantexp; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { debug (LOG) { @@ -806,7 +815,12 @@ public: return; } - override void visit(CompoundStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitCompound(CompoundStatement s) { debug (LOG) { @@ -829,7 +843,12 @@ public: } } - override void visit(UnrolledLoopStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitUnrolledLoop(UnrolledLoopStatement s) { debug (LOG) { @@ -875,7 +894,7 @@ public: } } - override void visit(IfStatement s) + void visitIf(IfStatement s) { debug (LOG) { @@ -911,7 +930,7 @@ public: } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { debug (LOG) { @@ -923,7 +942,7 @@ public: result = interpretStatement(pue, s.statement, istate); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { debug (LOG) { @@ -980,7 +999,7 @@ public: if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace)) { auto rs = new ReturnStatement(s.loc, e); - rs.accept(this); + visitReturn(rs); return; } @@ -1001,7 +1020,7 @@ public: result = e; } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { debug (LOG) { @@ -1019,7 +1038,7 @@ public: result = CTFEExp.breakexp; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { debug (LOG) { @@ -1037,7 +1056,7 @@ public: result = CTFEExp.continueexp; } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { debug (LOG) { @@ -1046,7 +1065,7 @@ public: assert(0); // rewritten to ForStatement } - override void visit(DoStatement s) + void visitDo(DoStatement s) { debug (LOG) { @@ -1107,7 +1126,7 @@ public: assert(result is null); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { debug (LOG) { @@ -1179,17 +1198,17 @@ public: assert(result is null); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { assert(0); // rewritten to ForStatement } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(0); // rewritten to ForStatement } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { debug (LOG) { @@ -1265,7 +1284,7 @@ public: result = e; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { debug (LOG) { @@ -1278,7 +1297,7 @@ public: result = interpretStatement(pue, s.statement, istate); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { debug (LOG) { @@ -1291,7 +1310,7 @@ public: result = interpretStatement(pue, s.statement, istate); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { debug (LOG) { @@ -1310,7 +1329,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { debug (LOG) { @@ -1329,7 +1348,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { debug (LOG) { @@ -1348,7 +1367,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { debug (LOG) { @@ -1360,7 +1379,7 @@ public: result = interpretStatement(pue, s.statement, istate); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { debug (LOG) { @@ -1428,7 +1447,7 @@ public: result = e; } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { debug (LOG) { @@ -1492,7 +1511,7 @@ public: result = ex; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { debug (LOG) { @@ -1508,12 +1527,12 @@ public: interpretThrow(result, s.exp, s.loc, istate); } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { assert(0); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { debug (LOG) { @@ -1569,7 +1588,7 @@ public: result = e; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { debug (LOG) { @@ -1585,7 +1604,17 @@ public: result = CTFEExp.cantexp; } - override void visit(ImportStatement s) + void visitInlineAsm(InlineAsmStatement s) + { + visitAsm(s); + } + + void visitGccAsm(GccAsmStatement s) + { + visitAsm(s); + } + + void visitImport(ImportStatement s) { debug (LOG) { @@ -1599,6 +1628,45 @@ public: } } + if (!s) + return null; + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/// + +private extern (C++) final class Interpreter : Visitor +{ + alias visit = Visitor.visit; +public: + InterState* istate; + CTFEGoal goal; + Expression result; + UnionExp* pue; // storage for `result` + + extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope + { + this.pue = pue; + this.istate = istate; + this.goal = goal; + } + + // If e is EXP.throw_exception or EXP.cantExpression, + // set it to 'result' and returns true. + bool exceptionOrCant(Expression e) + { + if (exceptionOrCantInterpret(e)) + { + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + return true; + } + return false; + } + /******************************** Expression ***************************/ override void visit(Expression e) @@ -6368,6 +6436,30 @@ public: { assert(0); // This should never be interpreted } +} + +/// Interpret `throw ` found at the specified location `loc` +private +void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +{ + incUsageCtfe(istate, loc); + + Expression e = interpretRegion(exp, istate); + if (exceptionOrCantInterpret(e)) + { + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + } + else if (e.op == EXP.classReference) + { + result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); + } + else + { + exp.error("to be thrown `%s` must be non-null", exp.toChars()); + result = ErrorExp.get(); + } +} /********************************************* * Checks if the given expresion is a call to the runtime hook `id`. @@ -6395,30 +6487,6 @@ public: return null; } -} - -/// Interpret `throw ` found at the specified location `loc` -private void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) -{ - incUsageCtfe(istate, loc); - - Expression e = interpretRegion(exp, istate); - if (exceptionOrCantInterpret(e)) - { - // Make sure e is not pointing to a stack temporary - result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; - } - else if (e.op == EXP.classReference) - { - result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); - } - else - { - exp.error("to be thrown `%s` must be non-null", exp.toChars()); - result = ErrorExp.get(); - } -} - /******************************************** * Interpret the expression. @@ -6485,36 +6553,6 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size); } -/*********************************** - * Interpret the statement. - * Params: - * pue = non-null pointer to temporary storage that can be used to store the return value - * s = Statement to interpret - * istate = context - * Returns: - * NULL continue to next statement - * EXP.cantExpression cannot interpret statement at compile time - * !NULL expression from return statement, or thrown exception - */ -Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) -{ - if (!s) - return null; - scope Interpreter v = new Interpreter(pue, istate, CTFEGoal.Nothing); - s.accept(v); - return v.result; -} - -/// -Expression interpretStatement(Statement s, InterState* istate) -{ - UnionExp ue = void; - auto result = interpretStatement(&ue, s, istate); - if (result == ue.exp()) - result = ue.copy(); - return result; -} - private Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) { From 2dfeb600a6995411707ee2c341b0ed571c164915 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 28 Mar 2023 02:35:18 -0700 Subject: [PATCH 078/301] fix Issue 23789 - ImportC: __declspec(align(n)) take 2 (dlang/dmd!15044) --- dmd/cparse.d | 21 +++++++++++++++------ dmd/dsymbolsem.d | 3 ++- dmd/frontend.h | 1 + dmd/mtype.d | 4 +++- dmd/typesem.d | 2 ++ tests/dmd/compilable/imports/c23789.i | 2 +- tests/dmd/compilable/test23789.d | 2 ++ 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 62468cff480..70688509e65 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -1670,6 +1670,12 @@ final class CParser(AST) : Parser!AST auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) : (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) : new AST.EnumDeclaration(tt.loc, tt.id, tt.base); + if (!tt.packalign.isUnknown()) + { + // saw `struct __declspec(align(N)) Tag ...` + auto st = stag.isStructDeclaration(); + st.alignment = tt.packalign; + } stag.members = tt.members; tt.members = null; if (!symbols) @@ -2283,18 +2289,20 @@ final class CParser(AST) : Parser!AST const sloc = token.loc; nextToken(); + Specifier tagSpecifier; + /* GNU Extensions * struct-or-union-specifier: * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt) * struct-or-union gnu-attribute (opt) identifier */ if (token.value == TOK.__attribute__) - cparseGnuAttributes(specifier); + cparseGnuAttributes(tagSpecifier); if (token.value == TOK.__declspec) - cparseDeclspec(specifier); + cparseDeclspec(tagSpecifier); - t = cparseStruct(sloc, structOrUnion, symbols); + t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols); tkwx = TKW.xtag; break; } @@ -3622,7 +3630,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, TOK.enum_, tag, base, members); + return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members); } /************************************* @@ -3644,11 +3652,12 @@ final class CParser(AST) : Parser!AST * Params: * loc = location of `struct` or `union` * structOrUnion = TOK.struct_ or TOK.union_ + * packalign = alignment to use for struct members * symbols = symbols to add struct-or-union declaration to * Returns: * type of the struct */ - private AST.Type cparseStruct(Loc loc, TOK structOrUnion, ref AST.Dsymbols* symbols) + private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols) { Identifier tag; @@ -3687,7 +3696,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, structOrUnion, tag, null, members); + return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members); } /************************************* diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 2f3e167865e..847ddc577dd 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -4666,7 +4666,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { sd.visibility = sc.visibility; - sd.alignment = sc.alignment(); + if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }` + sd.alignment = sc.alignment(); sd.storage_class |= sc.stc; if (sd.storage_class & STC.abstract_) diff --git a/dmd/frontend.h b/dmd/frontend.h index d83c4264ead..3e8654f5baa 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3957,6 +3957,7 @@ class TypeTag final : public Type public: Loc loc; TOK tok; + structalign_t packalign; Identifier* id; Type* base; Array* members; diff --git a/dmd/mtype.d b/dmd/mtype.d index 4f9ad991862..33ef556c9e8 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -6515,6 +6515,7 @@ extern (C++) final class TypeTag : Type { Loc loc; /// location of declaration TOK tok; /// TOK.struct_, TOK.union_, TOK.enum_ + structalign_t packalign; /// alignment of struct/union fields Identifier id; /// tag name identifier Type base; /// base type for enums otherwise null Dsymbols* members; /// members of struct, null if none @@ -6524,13 +6525,14 @@ extern (C++) final class TypeTag : Type /// struct S { int a; } s1, *s2; MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) - extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members) + extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) { //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); this.loc = loc; this.tok = tok; this.id = id; + this.packalign = packalign; this.base = base; this.members = members; this.mod = 0; diff --git a/dmd/typesem.d b/dmd/typesem.d index 9b80b210ec0..5617bbe6f5d 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1809,12 +1809,14 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) case TOK.struct_: auto sd = new StructDeclaration(mtype.loc, mtype.id, false); + sd.alignment = mtype.packalign; declare(sd); mtype.resolved = visitStruct(new TypeStruct(sd)); break; case TOK.union_: auto ud = new UnionDeclaration(mtype.loc, mtype.id); + ud.alignment = mtype.packalign; declare(ud); mtype.resolved = visitStruct(new TypeStruct(ud)); break; diff --git a/tests/dmd/compilable/imports/c23789.i b/tests/dmd/compilable/imports/c23789.i index e219f05ffaa..1c35d13c199 100644 --- a/tests/dmd/compilable/imports/c23789.i +++ b/tests/dmd/compilable/imports/c23789.i @@ -4,7 +4,7 @@ struct __declspec(align(64)) M128A { char c; }; -typedef struct __declspec(align(16)) _M128B { +typedef struct __declspec(align(32)) _M128B { int x; } M128B, *PM128A; diff --git a/tests/dmd/compilable/test23789.d b/tests/dmd/compilable/test23789.d index b97f17e291a..263d057e419 100644 --- a/tests/dmd/compilable/test23789.d +++ b/tests/dmd/compilable/test23789.d @@ -3,3 +3,5 @@ import imports.c23789; static assert(M128A.alignof == 64); +static assert(_M128B.alignof == 32); +static assert(M128B.alignof == 32); From f57109d0c6bb36feda2de62715970d7af47ad783 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 28 Mar 2023 03:31:14 -0700 Subject: [PATCH 079/301] fix Issue 23801 - ImportC: enumeration constant does not fit in an int (dlang/dmd!15039) --- dmd/cparse.d | 3 +- dmd/dsymbolsem.d | 58 +++++++++++++++++------- tests/dmd/compilable/enumbase.c | 11 +++++ tests/dmd/fail_compilation/enumtype.c | 12 ++--- tests/dmd/fail_compilation/failcstuff6.c | 5 -- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 70688509e65..0f4e932402f 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3550,7 +3550,8 @@ final class CParser(AST) : Parser!AST * https://en.cppreference.com/w/cpp/language/enum * enum Identifier : Type */ - AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + AST.Type base = null; // C23 says base type is determined by enum member values if (token.value == TOK.colon) { nextToken(); diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 847ddc577dd..d5914f65999 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -2256,32 +2256,39 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { /* C11 6.7.2.2 */ - assert(ed.memtype); - int nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 + Type commonType = ed.memtype; + if (!commonType) + commonType = Type.tint32; + ulong nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 // C11 6.7.2.2-2 value must be representable as an int. // The sizemask represents all values that int will fit into, // from 0..uint.max. We want to cover int.min..uint.max. - const mask = Type.tint32.sizemask(); - IntRange ir = IntRange(SignExtendedNumber(~(mask >> 1), true), - SignExtendedNumber(mask)); + IntRange ir = IntRange.fromType(commonType); - void emSemantic(EnumMember em, ref int nextValue) + void emSemantic(EnumMember em, ref ulong nextValue) { static void errorReturn(EnumMember em) { + em.value = ErrorExp.get(); em.errors = true; em.semanticRun = PASS.semanticdone; } em.semanticRun = PASS.semantic; - em.type = Type.tint32; + em.type = commonType; em._linkage = LINK.c; em.storage_class |= STC.manifest; if (em.value) { Expression e = em.value; assert(e.dyncast() == DYNCAST.expression); + + /* To merge the type of e with commonType, add 0 of type commonType + */ + if (!ed.memtype) + e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType)); + e = e.expressionSemantic(sc); e = resolveProperties(sc, e); e = e.integralPromotions(sc); @@ -2295,14 +2302,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars()); return errorReturn(em); } - if (!ir.contains(getIntRange(ie))) + if (ed.memtype && !ir.contains(getIntRange(ie))) { // C11 6.7.2.2-2 - em.error("enum member value `%s` does not fit in an `int`", e.toChars()); + em.error("enum member value `%s` does not fit in `%s`", e.toChars(), commonType.toChars()); return errorReturn(em); } - nextValue = cast(int)ie.toInteger(); - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + nextValue = ie.toInteger(); + if (!ed.memtype) + commonType = e.type; + em.value = new IntegerExp(em.loc, nextValue, commonType); } else { @@ -2310,17 +2319,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool first = (em == (*em.ed.members)[0]); if (!first) { - import core.checkedint : adds; - bool overflow; - nextValue = adds(nextValue, 1, overflow); - if (overflow) + Expression max = getProperty(commonType, null, em.loc, Id.max, 0); + if (nextValue == max.toInteger()) { - em.error("initialization with `%d+1` causes overflow for type `int`", nextValue - 1); + em.error("initialization with `%s+1` causes overflow for type `%s`", max.toChars(), commonType.toChars()); return errorReturn(em); } + nextValue += 1; } - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + em.value = new IntegerExp(em.loc, nextValue, commonType); } + em.type = commonType; em.semanticRun = PASS.semanticdone; } @@ -2329,6 +2338,21 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (EnumMember em = s.isEnumMember()) emSemantic(em, nextValue); }); + + if (!ed.memtype) + { + // cast all members to commonType + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + { + em.type = commonType; + em.value = em.value.castTo(sc, commonType); + } + }); + } + + ed.memtype = commonType; ed.semanticRun = PASS.semanticdone; return; } diff --git a/tests/dmd/compilable/enumbase.c b/tests/dmd/compilable/enumbase.c index f1fabfa2319..683a3ff04e5 100644 --- a/tests/dmd/compilable/enumbase.c +++ b/tests/dmd/compilable/enumbase.c @@ -23,3 +23,14 @@ enum U2: unsigned { enum U3: unsigned long { U3_A = 1, }; + +// https://issues.dlang.org/show_bug.cgi?id=23801 + +enum +{ + X = ~1ull, + Y, +}; + +_Static_assert(X == ~1ull, "3"); +_Static_assert(Y == ~1ull + 1, "4"); diff --git a/tests/dmd/fail_compilation/enumtype.c b/tests/dmd/fail_compilation/enumtype.c index f6aae337e22..d283595f177 100644 --- a/tests/dmd/fail_compilation/enumtype.c +++ b/tests/dmd/fail_compilation/enumtype.c @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in an `int` +fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in `int` --- */ @@ -11,9 +11,9 @@ enum E1 { A1 = 0, B1 = sizeof(A1), C1 = 1LL, D1 = sizeof(C1), F1 = -1U, G1 }; _Static_assert(A1 == 0, "in"); _Static_assert(B1 == 4, "in"); _Static_assert(C1 == 1, "in"); -_Static_assert(D1 == 4, "in"); -_Static_assert(F1 == -1, "in"); -_Static_assert(G1 == 0, "in"); -_Static_assert(sizeof(enum E1) == 4, "in"); +_Static_assert(D1 == 8, "in"); +_Static_assert(F1 == -1U, "in"); +_Static_assert(G1 == 0x100000000, "in"); +_Static_assert(sizeof(enum E1) == 8, "in"); -enum E2 { A2 = 0x8000000001LL }; +enum E2 : int { A2 = 0x8000000001LL }; diff --git a/tests/dmd/fail_compilation/failcstuff6.c b/tests/dmd/fail_compilation/failcstuff6.c index af486745444..88c541ca64f 100644 --- a/tests/dmd/fail_compilation/failcstuff6.c +++ b/tests/dmd/fail_compilation/failcstuff6.c @@ -2,11 +2,6 @@ /* TEST_OUTPUT: --- fail_compilation/failcstuff6.c(56): Error: enum member `failcstuff6.test_overflow.boom` initialization with `2147483647+1` causes overflow for type `int` -fail_compilation/failcstuff6.c(105): Error: enum member `failcstuff6.test_enum_fits.firstMinError` enum member value `-2147483649L` does not fit in an `int` -fail_compilation/failcstuff6.c(106): Error: enum member `failcstuff6.test_enum_fits.firstMaxError` enum member value `4294967296L` does not fit in an `int` -fail_compilation/failcstuff6.c(107): Error: enum member `failcstuff6.test_enum_fits.lastMaxError` enum member value `18446744071562067967LU` does not fit in an `int` -fail_compilation/failcstuff6.c(108): Error: enum member `failcstuff6.test_enum_fits.firstBlindSpot` enum member value `18446744071562067968LU` does not fit in an `int` -fail_compilation/failcstuff6.c(109): Error: enum member `failcstuff6.test_enum_fits.lastBlindSpot` enum member value `18446744073709551615LU` does not fit in an `int` --- */ From d70f5dbd23ca59defb67aa92016ec59e21849132 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 29 Mar 2023 00:43:02 -0700 Subject: [PATCH 080/301] ImportC: accept __declspec(thread) and __declspec(naked) (dlang/dmd!15045) --- dmd/cparse.d | 13 +++++++++++++ dmd/frontend.h | 2 ++ dmd/id.d | 2 ++ tests/dmd/compilable/testcstuff1.c | 1 + 4 files changed, 18 insertions(+) diff --git a/dmd/cparse.d b/dmd/cparse.d index 0f4e932402f..202b573f6e7 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3113,7 +3113,9 @@ final class CParser(AST) : Parser!AST * align(number) * dllimport * dllexport + * naked * noreturn + * thread * Params: * specifier = filled in with the attribute(s) */ @@ -3125,6 +3127,7 @@ final class CParser(AST) : Parser!AST */ bool dllimport; // TODO implement bool dllexport; // TODO implement + bool naked; // TODO implement nextToken(); // move past __declspec check(TOK.leftParenthesis); while (1) @@ -3148,11 +3151,21 @@ final class CParser(AST) : Parser!AST dllexport = true; nextToken(); } + else if (token.ident == Id.naked) + { + naked = true; + nextToken(); + } else if (token.ident == Id.noreturn) { specifier.noreturn = true; nextToken(); } + else if (token.ident == Id.thread) + { + specifier.scw |= SCW.x_Thread_local; + nextToken(); + } else if (token.ident == Id._align) { // Microsoft spec is very imprecise as to how this actually works diff --git a/dmd/frontend.h b/dmd/frontend.h index 3e8654f5baa..ef16eb58839 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8837,6 +8837,8 @@ struct Id final static Identifier* ImportC; static Identifier* dllimport; static Identifier* dllexport; + static Identifier* naked; + static Identifier* thread; static Identifier* vector_size; static Identifier* noreturn; static Identifier* _align; diff --git a/dmd/id.d b/dmd/id.d index 7a720eaab0c..9fb770fb6c2 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -523,6 +523,8 @@ immutable Msgtable[] msgtable = { "__tag" }, { "dllimport" }, { "dllexport" }, + { "naked" }, + { "thread" }, { "vector_size" }, { "__func__" }, { "noreturn" }, diff --git a/tests/dmd/compilable/testcstuff1.c b/tests/dmd/compilable/testcstuff1.c index 2e37e49be74..13b6d96fa96 100644 --- a/tests/dmd/compilable/testcstuff1.c +++ b/tests/dmd/compilable/testcstuff1.c @@ -278,6 +278,7 @@ void test2() //extern int ei; static int si; _Thread_local int tli; + int __declspec(thread) tlj; auto int ai; register int reg; const int ci; From 0c56f3654bf6e86fcac7f3d2e37c4366f1e9deeb Mon Sep 17 00:00:00 2001 From: Dennis Date: Thu, 30 Mar 2023 07:23:45 +0200 Subject: [PATCH 081/301] Parse UDAs like template arguments (dlang/dmd!14881) --- dmd/parse.d | 54 ++++++++++++++----- .../dmd/compilable/user_defined_attributes.d | 22 ++++++++ .../fail_compilation/named_arguments_parse.d | 4 +- 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 tests/dmd/compilable/user_defined_attributes.d diff --git a/dmd/parse.d b/dmd/parse.d index 9a2448fb57d..da257601a89 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -1316,12 +1316,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return 0; } + AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + { + switch (o.dyncast) + { + case DYNCAST.expression: + return cast(AST.Expression) o; + case DYNCAST.type: + return new AST.TypeExp(loc, cast(AST.Type)o); + default: + assert(0); + } + } + if (token.value == TOK.leftParenthesis) { // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing if (peekNext() == TOK.rightParenthesis) error("empty attribute list is not allowed"); - udas = AST.UserAttributeDeclaration.concat(udas, parseArguments()); + + if (udas is null) + udas = new AST.Expressions(); + auto args = parseTemplateArgumentList(); + foreach (arg; *args) + udas.push(templateArgToExp(arg, token.loc)); + return 0; + } + + if (auto o = parseTemplateSingleArgument()) + { + if (udas is null) + udas = new AST.Expressions(); + udas.push(templateArgToExp(o, token.loc)); return 0; } @@ -1778,7 +1804,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else { // ident!template_argument - tiargs = parseTemplateSingleArgument(); + RootObject o = parseTemplateSingleArgument(); + if (!o) + { + error("template argument expected following `!`"); + } + else + { + tiargs = new AST.Objects(); + tiargs.push(o); + } } if (token.value == TOK.not) { @@ -1844,11 +1879,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * foo!arg * Input: * current token is the arg + * Returns: An AST.Type, AST.Expression, or `null` on error */ - private AST.Objects* parseTemplateSingleArgument() + private RootObject parseTemplateSingleArgument() { //printf("parseTemplateSingleArgument()\n"); - auto tiargs = new AST.Objects(); AST.Type ta; switch (token.value) { @@ -1956,9 +1991,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer ta = AST.Type.tdchar; goto LabelX; LabelX: - tiargs.push(ta); nextToken(); - break; + return ta; case TOK.int32Literal: case TOK.uns32Literal: @@ -1988,15 +2022,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.this_: { // Template argument is an expression - AST.Expression ea = parsePrimaryExp(); - tiargs.push(ea); - break; + return parsePrimaryExp(); } default: - error("template argument expected following `!`"); - break; + return null; } - return tiargs; } /********************************** diff --git a/tests/dmd/compilable/user_defined_attributes.d b/tests/dmd/compilable/user_defined_attributes.d new file mode 100644 index 00000000000..169ca49eb0d --- /dev/null +++ b/tests/dmd/compilable/user_defined_attributes.d @@ -0,0 +1,22 @@ + +enum Test; + +@true @null @byte int x; +@(int) int y; +@"test" @`test2` @30 @'a' @__LINE__ void f(); + +@Test void h(); + +static assert( __traits(getAttributes, x)[0] == true); +static assert( __traits(getAttributes, x)[1] == null); +static assert(is(__traits(getAttributes, x)[2] == byte)); + +static assert(is(__traits(getAttributes, y)[0] == int)); + +static assert( __traits(getAttributes, f)[0] == "test"); +static assert( __traits(getAttributes, f)[1] == "test2"); +static assert( __traits(getAttributes, f)[2] == 30); +static assert( __traits(getAttributes, f)[3] == 'a'); +static assert( __traits(getAttributes, f)[4] == 6); + +static assert(is(__traits(getAttributes, h)[0] == enum)); diff --git a/tests/dmd/fail_compilation/named_arguments_parse.d b/tests/dmd/fail_compilation/named_arguments_parse.d index 19e230ee519..096c499790e 100644 --- a/tests/dmd/fail_compilation/named_arguments_parse.d +++ b/tests/dmd/fail_compilation/named_arguments_parse.d @@ -1,13 +1,13 @@ /** TEST_OUTPUT: --- -fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here --- */ -@(attribute: 3) + +// @(attribute: 3) Currently gives an ugly parse error, will be better when named template arguments are implemented void main() { mixin(thecode: "{}"); From ae7c56d05c07b5ba764895396b0ee94f1e23d1f6 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 30 Mar 2023 00:07:02 -0700 Subject: [PATCH 082/301] pull leaf functions out of VisualCPPMangler --- dmd/cparse.d | 2 +- dmd/cppmanglewin.d | 382 +++++++++++++++++++++++---------------------- 2 files changed, 196 insertions(+), 188 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 202b573f6e7..cc78317ddc6 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3564,7 +3564,7 @@ final class CParser(AST) : Parser!AST * enum Identifier : Type */ //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type - AST.Type base = null; // C23 says base type is determined by enum member values + AST.Type base = null; // C23 says base type is determined by enum member values if (token.value == TOK.colon) { nextToken(); diff --git a/dmd/cppmanglewin.d b/dmd/cppmanglewin.d index 1ae20bac368..165e8c7f18c 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/cppmanglewin.d @@ -522,22 +522,6 @@ public: private: extern(D): - void mangleVisibility(Declaration d, string privProtDef) - { - switch (d.visibility.kind) - { - case Visibility.Kind.private_: - buf.writeByte(privProtDef[0]); - break; - case Visibility.Kind.protected_: - buf.writeByte(privProtDef[1]); - break; - default: - buf.writeByte(privProtDef[2]); - break; - } - } - void mangleFunction(FuncDeclaration d) { // ? @@ -551,11 +535,11 @@ extern(D): //d.toChars(), d.isVirtualMethod(), d.isVirtual(), cast(int)d.vtblIndex, d.interfaceVirtual); if ((d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) || (d.isDtorDeclaration() && d.parent.isClassDeclaration() && !d.isFinal())) { - mangleVisibility(d, "EMU"); + mangleVisibility(buf, d, "EMU"); } else { - mangleVisibility(d, "AIQ"); + mangleVisibility(buf, d, "AIQ"); } if (target.isLP64) buf.writeByte('E'); @@ -571,7 +555,7 @@ extern(D): else if (d.isMember2()) // static function { // ::= - mangleVisibility(d, "CKS"); + mangleVisibility(buf, d, "CKS"); } else // top-level function { @@ -606,7 +590,7 @@ extern(D): } else { - mangleVisibility(d, "012"); + mangleVisibility(buf, d, "012"); } Type t = d.type; @@ -624,141 +608,6 @@ extern(D): buf.writeByte(cv_mod); } - /** - * Computes mangling for symbols with special mangling. - * Params: - * sym = symbol to mangle - * Returns: - * mangling for special symbols, - * null if not a special symbol - */ - static string mangleSpecialName(Dsymbol sym) - { - string mangle; - if (sym.isCtorDeclaration()) - mangle = "?0"; - else if (sym.isAggregateDtor()) - mangle = "?1"; - else if (!sym.ident) - return null; - else if (sym.ident == Id.assign) - mangle = "?4"; - else if (sym.ident == Id.eq) - mangle = "?8"; - else if (sym.ident == Id.index) - mangle = "?A"; - else if (sym.ident == Id.call) - mangle = "?R"; - else if (sym.ident == Id.cppdtor) - mangle = "?_G"; - else - return null; - - return mangle; - } - - /** - * Mangles an operator, if any - * - * Params: - * ti = associated template instance of the operator - * symName = symbol name - * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) - * Returns: - * true if sym has no further mangling needed - * false otherwise - */ - bool mangleOperator(TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) - { - auto whichOp = isCppOperator(ti.name); - final switch (whichOp) - { - case CppOperator.Unknown: - return false; - case CppOperator.Cast: - buf.writestring("?B"); - return true; - case CppOperator.Assign: - symName = "?4"; - return false; - case CppOperator.Eq: - symName = "?8"; - return false; - case CppOperator.Index: - symName = "?A"; - return false; - case CppOperator.Call: - symName = "?R"; - return false; - - case CppOperator.Unary: - case CppOperator.Binary: - case CppOperator.OpAssign: - TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); - assert(td); - assert(ti.tiargs.length >= 1); - TemplateParameter tp = (*td.parameters)[0]; - TemplateValueParameter tv = tp.isTemplateValueParameter(); - if (!tv || !tv.valType.isString()) - return false; // expecting a string argument to operators! - Expression exp = (*ti.tiargs)[0].isExpression(); - StringExp str = exp.toStringExp(); - switch (whichOp) - { - case CppOperator.Unary: - switch (str.peekString()) - { - case "*": symName = "?D"; goto continue_template; - case "++": symName = "?E"; goto continue_template; - case "--": symName = "?F"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "~": symName = "?S"; goto continue_template; - default: return false; - } - case CppOperator.Binary: - switch (str.peekString()) - { - case ">>": symName = "?5"; goto continue_template; - case "<<": symName = "?6"; goto continue_template; - case "*": symName = "?D"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "&": symName = "?I"; goto continue_template; - case "/": symName = "?K"; goto continue_template; - case "%": symName = "?L"; goto continue_template; - case "^": symName = "?T"; goto continue_template; - case "|": symName = "?U"; goto continue_template; - default: return false; - } - case CppOperator.OpAssign: - switch (str.peekString()) - { - case "*": symName = "?X"; goto continue_template; - case "+": symName = "?Y"; goto continue_template; - case "-": symName = "?Z"; goto continue_template; - case "/": symName = "?_0"; goto continue_template; - case "%": symName = "?_1"; goto continue_template; - case ">>": symName = "?_2"; goto continue_template; - case "<<": symName = "?_3"; goto continue_template; - case "&": symName = "?_4"; goto continue_template; - case "|": symName = "?_5"; goto continue_template; - case "^": symName = "?_6"; goto continue_template; - default: return false; - } - default: assert(0); - } - } - continue_template: - if (ti.tiargs.length == 1) - { - buf.writestring(symName); - return true; - } - firstTemplateArg = 1; - return false; - } - /** * Mangles a template value * @@ -781,13 +630,13 @@ extern(D): assert(e); if (tv.valType.isunsigned()) { - mangleNumber(e.toUInteger()); + mangleNumber(buf, e.toUInteger()); } else if (is_dmc_template) { // NOTE: DMC mangles everything based on // unsigned int - mangleNumber(e.toInteger()); + mangleNumber(buf, e.toInteger()); } else { @@ -797,7 +646,7 @@ extern(D): val = -val; buf.writeByte('?'); } - mangleNumber(val); + mangleNumber(buf, val); } } @@ -927,7 +776,7 @@ extern(D): int firstTemplateArg = 0; // test for special symbols - if (mangleOperator(ti,symName,firstTemplateArg)) + if (mangleOperator(buf, ti,symName,firstTemplateArg)) return; TemplateInstance actualti = ti; bool needNamespaces; @@ -1092,32 +941,6 @@ extern(D): buf.writeByte('@'); } - void mangleNumber(dinteger_t num) - { - if (!num) // 0 encoded as "A@" - { - buf.writeByte('A'); - buf.writeByte('@'); - return; - } - if (num <= 10) // 5 encoded as "4" - { - buf.writeByte(cast(char)(num - 1 + '0')); - return; - } - char[17] buff; - buff[16] = 0; - size_t i = 16; - while (num) - { - --i; - buff[i] = num % 16 + 'A'; - num /= 16; - } - buf.writestring(&buff[i]); - buf.writeByte('@'); - } - bool checkTypeSaved(Type type) { if (flags & IS_NOT_TOP_TYPE) @@ -1177,12 +1000,12 @@ extern(D): cur = cur.nextOf(); } buf.writeByte('Y'); - mangleNumber(i); // count of dimensions + mangleNumber(buf, i); // count of dimensions cur = type; while (cur && cur.ty == Tsarray) // sizes of dimensions { TypeSArray sa = cast(TypeSArray)cur; - mangleNumber(sa.dim ? sa.dim.toInteger() : 0); + mangleNumber(buf, sa.dim ? sa.dim.toInteger() : 0); cur = cur.nextOf(); } flags |= IGNORE_CONST; @@ -1301,3 +1124,188 @@ extern(D): return ret; } } + +private: +extern(D): + +/** + * Computes mangling for symbols with special mangling. + * Params: + * sym = symbol to mangle + * Returns: + * mangling for special symbols, + * null if not a special symbol + */ +string mangleSpecialName(Dsymbol sym) +{ + string mangle; + if (sym.isCtorDeclaration()) + mangle = "?0"; + else if (sym.isAggregateDtor()) + mangle = "?1"; + else if (!sym.ident) + return null; + else if (sym.ident == Id.assign) + mangle = "?4"; + else if (sym.ident == Id.eq) + mangle = "?8"; + else if (sym.ident == Id.index) + mangle = "?A"; + else if (sym.ident == Id.call) + mangle = "?R"; + else if (sym.ident == Id.cppdtor) + mangle = "?_G"; + else + return null; + + return mangle; +} + +/** + * Mangles an operator, if any + * + * Params: + * buf = buffer to write mangling to + * ti = associated template instance of the operator + * symName = symbol name + * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) + * Returns: + * true if sym has no further mangling needed + * false otherwise + */ +bool mangleOperator(ref OutBuffer buf, TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) +{ + auto whichOp = isCppOperator(ti.name); + final switch (whichOp) + { + case CppOperator.Unknown: + return false; + case CppOperator.Cast: + buf.writestring("?B"); + return true; + case CppOperator.Assign: + symName = "?4"; + return false; + case CppOperator.Eq: + symName = "?8"; + return false; + case CppOperator.Index: + symName = "?A"; + return false; + case CppOperator.Call: + symName = "?R"; + return false; + + case CppOperator.Unary: + case CppOperator.Binary: + case CppOperator.OpAssign: + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + assert(ti.tiargs.length >= 1); + TemplateParameter tp = (*td.parameters)[0]; + TemplateValueParameter tv = tp.isTemplateValueParameter(); + if (!tv || !tv.valType.isString()) + return false; // expecting a string argument to operators! + Expression exp = (*ti.tiargs)[0].isExpression(); + StringExp str = exp.toStringExp(); + switch (whichOp) + { + case CppOperator.Unary: + switch (str.peekString()) + { + case "*": symName = "?D"; goto continue_template; + case "++": symName = "?E"; goto continue_template; + case "--": symName = "?F"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "~": symName = "?S"; goto continue_template; + default: return false; + } + case CppOperator.Binary: + switch (str.peekString()) + { + case ">>": symName = "?5"; goto continue_template; + case "<<": symName = "?6"; goto continue_template; + case "*": symName = "?D"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "&": symName = "?I"; goto continue_template; + case "/": symName = "?K"; goto continue_template; + case "%": symName = "?L"; goto continue_template; + case "^": symName = "?T"; goto continue_template; + case "|": symName = "?U"; goto continue_template; + default: return false; + } + case CppOperator.OpAssign: + switch (str.peekString()) + { + case "*": symName = "?X"; goto continue_template; + case "+": symName = "?Y"; goto continue_template; + case "-": symName = "?Z"; goto continue_template; + case "/": symName = "?_0"; goto continue_template; + case "%": symName = "?_1"; goto continue_template; + case ">>": symName = "?_2"; goto continue_template; + case "<<": symName = "?_3"; goto continue_template; + case "&": symName = "?_4"; goto continue_template; + case "|": symName = "?_5"; goto continue_template; + case "^": symName = "?_6"; goto continue_template; + default: return false; + } + default: assert(0); + } + } + continue_template: + if (ti.tiargs.length == 1) + { + buf.writestring(symName); + return true; + } + firstTemplateArg = 1; + return false; +} + +/**********************************' + */ +void mangleNumber(ref OutBuffer buf, dinteger_t num) +{ + if (!num) // 0 encoded as "A@" + { + buf.writeByte('A'); + buf.writeByte('@'); + return; + } + if (num <= 10) // 5 encoded as "4" + { + buf.writeByte(cast(char)(num - 1 + '0')); + return; + } + char[17] buff = void; + buff[16] = 0; + size_t i = 16; + while (num) + { + --i; + buff[i] = num % 16 + 'A'; + num /= 16; + } + buf.writestring(&buff[i]); + buf.writeByte('@'); +} + +/************************************* + */ +void mangleVisibility(ref OutBuffer buf, Declaration d, string privProtDef) +{ + switch (d.visibility.kind) + { + case Visibility.Kind.private_: + buf.writeByte(privProtDef[0]); + break; + case Visibility.Kind.protected_: + buf.writeByte(privProtDef[1]); + break; + default: + buf.writeByte(privProtDef[2]); + break; + } +} From 1a5d7778830a9f402438f476951008b0b342cdbd Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 31 Mar 2023 01:21:06 -0700 Subject: [PATCH 083/301] sort the traits table (dlang/dmd!15054) --- dmd/traits.d | 90 ++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/dmd/traits.d b/dmd/traits.d index de0129b5b33..dbd05a3fefb 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -2246,65 +2246,65 @@ private void traitNotFound(TraitsExp e) // All possible traits __gshared Identifier*[59] idents = [ + &Id.allMembers, + &Id.child, + &Id.classInstanceAlignment, + &Id.classInstanceSize, + &Id.compiles, + &Id.derivedMembers, + &Id.fullyQualifiedName, + &Id.getAliasThis, + &Id.getAttributes, + &Id.getFunctionAttributes, + &Id.getFunctionVariadicStyle, + &Id.getLinkage, + &Id.getLocation, + &Id.getMember, + &Id.getOverloads, + &Id.getParameterStorageClasses, + &Id.getPointerBitmap, + &Id.getProtection, + &Id.getTargetInfo, + &Id.getUnitTests, + &Id.getVirtualFunctions, + &Id.getVirtualIndex, + &Id.getVirtualMethods, + &Id.getVisibility, + &Id.hasCopyConstructor, + &Id.hasMember, + &Id.hasPostblit, + &Id.identifier, &Id.isAbstractClass, + &Id.isAbstractFunction, &Id.isArithmetic, &Id.isAssociativeArray, - &Id.isDisabled, + &Id.isCopyable, &Id.isDeprecated, - &Id.isFuture, + &Id.isDisabled, &Id.isFinalClass, - &Id.isPOD, - &Id.isNested, + &Id.isFinalFunction, &Id.isFloating, + &Id.isFuture, &Id.isIntegral, - &Id.isScalar, - &Id.isStaticArray, - &Id.isUnsigned, - &Id.isVirtualFunction, - &Id.isVirtualMethod, - &Id.isAbstractFunction, - &Id.isFinalFunction, - &Id.isOverrideFunction, - &Id.isStaticFunction, + &Id.isLazy, &Id.isModule, + &Id.isNested, + &Id.isOut, + &Id.isOverrideFunction, &Id.isPackage, + &Id.isPOD, &Id.isRef, - &Id.isOut, - &Id.isLazy, &Id.isReturnOnStack, - &Id.hasMember, - &Id.identifier, - &Id.fullyQualifiedName, - &Id.getProtection, - &Id.getVisibility, - &Id.parent, - &Id.child, - &Id.getLinkage, - &Id.getMember, - &Id.getOverloads, - &Id.getVirtualFunctions, - &Id.getVirtualMethods, - &Id.classInstanceSize, - &Id.classInstanceAlignment, - &Id.allMembers, - &Id.derivedMembers, &Id.isSame, - &Id.compiles, - &Id.getAliasThis, - &Id.getAttributes, - &Id.getFunctionAttributes, - &Id.getFunctionVariadicStyle, - &Id.getParameterStorageClasses, - &Id.getUnitTests, - &Id.getVirtualIndex, - &Id.getPointerBitmap, + &Id.isScalar, + &Id.isStaticArray, + &Id.isStaticFunction, + &Id.isUnsigned, + &Id.isVirtualFunction, + &Id.isVirtualMethod, &Id.isZeroInit, - &Id.getTargetInfo, - &Id.getLocation, - &Id.hasPostblit, - &Id.hasCopyConstructor, - &Id.isCopyable, &Id.parameters, + &Id.parent, ]; StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable; From 058b47207dda2bdf0ce10256a5b0b471707a8277 Mon Sep 17 00:00:00 2001 From: Dennis Date: Fri, 31 Mar 2023 10:32:42 +0200 Subject: [PATCH 084/301] Fix 17374 - Improve inferred attribute error message (dlang/dmd!15051) --- .../dmd/fail_compilation/attributediagnostic_nogc.d | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/dmd/fail_compilation/attributediagnostic_nogc.d b/tests/dmd/fail_compilation/attributediagnostic_nogc.d index 558796c73b2..e3dbee899fc 100644 --- a/tests/dmd/fail_compilation/attributediagnostic_nogc.d +++ b/tests/dmd/fail_compilation/attributediagnostic_nogc.d @@ -12,9 +12,12 @@ fail_compilation/attributediagnostic_nogc.d(32): cannot use `new` in `@no fail_compilation/attributediagnostic_nogc.d(44): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc2` fail_compilation/attributediagnostic_nogc.d(38): which wasn't inferred `@nogc` because of: fail_compilation/attributediagnostic_nogc.d(38): `@nogc` function `attributediagnostic_nogc.gc2` cannot call non-@nogc `fgc` +fail_compilation/attributediagnostic_nogc.d(45): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gcClosure` +fail_compilation/attributediagnostic_nogc.d(48): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(48): function `attributediagnostic_nogc.gcClosure` is `@nogc` yet allocates closure for `gcClosure()` with the GC --- */ - +#line 18 // Issue 17374 - Improve inferred attribute error message // https://issues.dlang.org/show_bug.cgi?id=17374 @@ -42,4 +45,12 @@ void main() @nogc { gc1(); gc2(); + gcClosure(); +} + +auto gcClosure() +{ + int x; + int bar() { return x; } + return &bar; } From fc156c799f8490a81196171432814e8f7f4c1e66 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 31 Mar 2023 01:33:01 -0700 Subject: [PATCH 085/301] replace Visitor for Types with mixin (dlang/dmd!15056) --- dmd/astenums.d | 3 +- dmd/frontend.h | 2 +- dmd/mtype.d | 110 +++++++++++++++++++++++++++++++++++++++++ dmd/traits.d | 130 +++++++++++++++++-------------------------------- 4 files changed, 158 insertions(+), 87 deletions(-) diff --git a/dmd/astenums.d b/dmd/astenums.d index 97658d960d7..2c7788336bf 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -214,8 +214,8 @@ enum TY : ubyte Tmixin, Tnoreturn, Ttag, - TMAX } +enum TMAX = TY.max + 1; alias Tarray = TY.Tarray; alias Tsarray = TY.Tsarray; @@ -265,7 +265,6 @@ alias Ttraits = TY.Ttraits; alias Tmixin = TY.Tmixin; alias Tnoreturn = TY.Tnoreturn; alias Ttag = TY.Ttag; -alias TMAX = TY.TMAX; enum TFlags { diff --git a/dmd/frontend.h b/dmd/frontend.h index ef16eb58839..86cfe0ffca6 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -1339,7 +1339,6 @@ enum class TY : uint8_t Tmixin = 45u, Tnoreturn = 46u, Ttag = 47u, - TMAX = 48u, }; enum class Covariant @@ -5065,6 +5064,7 @@ struct ASTCodegen final using TypeTuple = ::TypeTuple; using TypeTypeof = ::TypeTypeof; using TypeVector = ::TypeVector; + using VisitType = ::VisitType; using Nspace = ::Nspace; using AsmStatement = ::AsmStatement; using BreakStatement = ::BreakStatement; diff --git a/dmd/mtype.d b/dmd/mtype.d index 33ef556c9e8..d97554ffddf 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -7583,3 +7583,113 @@ TypeVector toBooleanVector(TypeVector tv) return new TypeVector(new TypeSArray(telem, tsa.dim)); } + +/************************************************* + * Dispatch to function based on static type of Type. + */ +mixin template VisitType(Result) +{ + Result VisitType(Type t) + { + final switch (t.ty) + { + case TY.Tvoid: + case TY.Tint8: + case TY.Tuns8: + case TY.Tint16: + case TY.Tuns16: + case TY.Tint32: + case TY.Tuns32: + case TY.Tint64: + case TY.Tuns64: + case TY.Tfloat32: + case TY.Tfloat64: + case TY.Tfloat80: + case TY.Timaginary32: + case TY.Timaginary64: + case TY.Timaginary80: + case TY.Tcomplex32: + case TY.Tcomplex64: + case TY.Tcomplex80: + case TY.Tbool: + case TY.Tchar: + case TY.Twchar: + case TY.Tdchar: + case TY.Tint128: + case TY.Tuns128: mixin(visitTYCase("Basic")); + case TY.Tarray: mixin(visitTYCase("DArray")); + case TY.Tsarray: mixin(visitTYCase("SArray")); + case TY.Taarray: mixin(visitTYCase("AArray")); + case TY.Tpointer: mixin(visitTYCase("Pointer")); + case TY.Treference: mixin(visitTYCase("Reference")); + case TY.Tfunction: mixin(visitTYCase("Function")); + case TY.Tident: mixin(visitTYCase("Identifier")); + case TY.Tclass: mixin(visitTYCase("Class")); + case TY.Tstruct: mixin(visitTYCase("Struct")); + case TY.Tenum: mixin(visitTYCase("Enum")); + case TY.Tdelegate: mixin(visitTYCase("Delegate")); + case TY.Terror: mixin(visitTYCase("Error")); + case TY.Tinstance: mixin(visitTYCase("Instance")); + case TY.Ttypeof: mixin(visitTYCase("Typeof")); + case TY.Ttuple: mixin(visitTYCase("Tuple")); + case TY.Tslice: mixin(visitTYCase("Slice")); + case TY.Treturn: mixin(visitTYCase("Return")); + case TY.Tnull: mixin(visitTYCase("Null")); + case TY.Tvector: mixin(visitTYCase("Vector")); + case TY.Ttraits: mixin(visitTYCase("Traits")); + case TY.Tmixin: mixin(visitTYCase("Mixin")); + case TY.Tnoreturn: mixin(visitTYCase("Noreturn")); + case TY.Ttag: mixin(visitTYCase("Tag")); + case TY.Tnone: assert(0); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitTYCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto tx = t.isType"~handler~"(); + static if (__traits(compiles, visit"~handler~"(tx))) + { + static if (isVoid) + { + visit"~handler~"(tx); + return; + } + else + { + if (Result r = visit"~handler~"(tx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(t))) + { + static if (isVoid) + { + visitDefaultCase(tx); + return; + } + else + { + if (Result r = visitDefaultCase(t)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} diff --git a/dmd/traits.d b/dmd/traits.d index dbd05a3fefb..788e0fb6b82 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -118,55 +118,40 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) data.setDim(cast(size_t)cntdata); data.zero(); - extern (C++) final class PointerBitmapVisitor : Visitor - { - alias visit = Visitor.visit; - public: - extern (D) this(Array!(ulong)* _data, ulong _sz_size_t) scope - { - this.data = _data; - this.sz_size_t = _sz_size_t; - } + ulong offset; + bool error; + void visit(Type t) + { void setpointer(ulong off) { ulong ptroff = off / sz_size_t; (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); } - override void visit(Type t) + void visitType(Type t) { Type tb = t.toBasetype(); if (tb != t) - tb.accept(this); - } - - override void visit(TypeError t) - { - visit(cast(Type)t); + visit(tb); } - override void visit(TypeNext t) + void visitError(TypeError t) { - assert(0); + visitType(t); } - override void visit(TypeBasic t) + void visitBasic(TypeBasic t) { if (t.ty == Tvoid) setpointer(offset); } - override void visit(TypeVector t) - { - } - - override void visit(TypeArray t) + void visitVector(TypeVector t) { - assert(0); } - override void visit(TypeSArray t) + void visitSArray(TypeSArray t) { ulong arrayoff = offset; ulong nextsize = t.next.size(); @@ -176,95 +161,67 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) for (ulong i = 0; i < dim; i++) { offset = arrayoff + i * nextsize; - t.next.accept(this); + visit(t.next); } offset = arrayoff; } - override void visit(TypeDArray t) + void visitDArray(TypeDArray t) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr} - override void visit(TypeAArray t) + void visitAArray(TypeAArray t) { setpointer(offset); } - override void visit(TypePointer t) + void visitPointer(TypePointer t) { if (t.nextOf().ty != Tfunction) // don't mark function pointers setpointer(offset); } - override void visit(TypeReference t) + void visitReference(TypeReference t) { setpointer(offset); } - override void visit(TypeClass t) + void visitClass(TypeClass t) { setpointer(offset); } - override void visit(TypeFunction t) + void visitFunction(TypeFunction t) { } - override void visit(TypeDelegate t) + void visitDelegate(TypeDelegate t) { setpointer(offset); } - // delegate is {context, function} - override void visit(TypeQualified t) - { - assert(0); - } - - // assume resolved - override void visit(TypeIdentifier t) + void visitEnum(TypeEnum t) { - assert(0); + visitType(t); } - override void visit(TypeInstance t) + void visitTuple(TypeTuple t) { - assert(0); - } - - override void visit(TypeTypeof t) - { - assert(0); + visitType(t); } - override void visit(TypeReturn t) + void visitNull(TypeNull t) { - assert(0); - } - - override void visit(TypeEnum t) - { - visit(cast(Type)t); - } - - override void visit(TypeTuple t) - { - visit(cast(Type)t); - } - - override void visit(TypeSlice t) - { - assert(0); + // always a null pointer } - override void visit(TypeNull t) + void visitNoreturn(TypeNoreturn t) { - // always a null pointer } - override void visit(TypeStruct t) + void visitStruct(TypeStruct t) { ulong structoff = offset; foreach (v; t.sym.fields) @@ -273,38 +230,43 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) if (v.type.ty == Tclass) setpointer(offset); else - v.type.accept(this); + visit(v.type); } offset = structoff; } + void visitDefaultCase(Type t) + { + //printf("ty = %d\n", t.ty); + assert(0); + } + + mixin VisitType!void visit; + visit.VisitType(t); + } + + if (auto tc = t.isTypeClass()) + { // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references - void visitClass(TypeClass t) + void visitTopLevelClass(TypeClass t) { ulong classoff = offset; // skip vtable-ptr and monitor if (t.sym.baseClass) - visitClass(cast(TypeClass)t.sym.baseClass.type); + visitTopLevelClass(t.sym.baseClass.type.isTypeClass()); foreach (v; t.sym.fields) { offset = classoff + v.offset; - v.type.accept(this); + visit(v.type); } offset = classoff; } - Array!(ulong)* data; - ulong offset; - ulong sz_size_t; - bool error; + visitTopLevelClass(tc); } - - scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t); - if (t.ty == Tclass) - pbv.visitClass(cast(TypeClass)t); else - t.accept(pbv); - return pbv.error ? ulong.max : sz; + visit(t); + return error ? ulong.max : sz; } /** From 7f6ee3e5dbe2c03f2edc20404f67d34fa9964fe2 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 31 Mar 2023 01:44:20 -0700 Subject: [PATCH 086/301] redo interface to LeftoverVisitor (dlang/dmd!15049) --- dmd/cppmangle.d | 102 +++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index b015a642b90..dd4e48df2ec 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -342,14 +342,14 @@ private final class CppMangleVisitor : Visitor * * Params: * off = Offset to insert at - * fd = Type of the function to mangle the return type of + * tf = Type of the function to mangle the return type of */ void writeRemainingTags(size_t off, TypeFunction tf) { - scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written); - tf.next.accept(remainingVisitor); + Array!StringExp toWrite; + leftOver(tf, &this.abiTags.written, &toWrite); OutBuffer b2; - foreach (se; remainingVisitor.toWrite) + foreach (se; toWrite) { auto tag = se.peekString(); // We can only insert a slice, and each insert is a memmove, @@ -2522,58 +2522,70 @@ unittest assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match); } -/** +/*** * Visits the return type of a function and writes leftover ABI tags + * Params: + * tf = Type of the function to mangle the return type of + * previous = already written ones + * toWrite = where to put StringExp's to be written */ -extern(C++) private final class LeftoverVisitor : Visitor +private +void leftOver(TypeFunction tf, const(Array!StringExp)* previous, Array!StringExp* toWrite) { - /// List of tags to write - private Array!StringExp toWrite; - /// List of tags to ignore - private const(Array!StringExp)* ignore; - - /// - public this(const(Array!StringExp)* previous) + extern(C++) final class LeftoverVisitor : Visitor { - this.ignore = previous; - } + /// List of tags to write + private Array!StringExp* toWrite; + /// List of tags to ignore + private const(Array!StringExp)* ignore; - /// Reintroduce base class overloads - public alias visit = Visitor.visit; + /// + public this(const(Array!StringExp)* previous, Array!StringExp* toWrite) + { + this.ignore = previous; + this.toWrite = toWrite; + } - /// Least specialized overload of each direct child of `RootObject` - public override void visit(Dsymbol o) - { - auto ale = ABITagContainer.forSymbol(o); - if (!ale) return; + /// Reintroduce base class overloads + public alias visit = Visitor.visit; - bool match; - foreach (elem; *ale.elements) + /// Least specialized overload of each direct child of `RootObject` + public override void visit(Dsymbol o) { - auto se = elem.toStringExp(); - closestIndex((*this.ignore)[], se, match); - if (match) continue; - auto idx = closestIndex(this.toWrite[], se, match); - if (!match) - this.toWrite.insert(idx, se); + auto ale = ABITagContainer.forSymbol(o); + if (!ale) return; + + bool match; + foreach (elem; *ale.elements) + { + auto se = elem.toStringExp(); + closestIndex((*this.ignore)[], se, match); + if (match) continue; + auto idx = closestIndex((*this.toWrite)[], se, match); + if (!match) + (*this.toWrite).insert(idx, se); + } } - } - /// Ditto - public override void visit(Type o) - { - if (auto sym = o.toDsymbol(null)) - sym.accept(this); - } + /// Ditto + public override void visit(Type o) + { + if (auto sym = o.toDsymbol(null)) + sym.accept(this); + } - /// Composite type - public override void visit(TypePointer o) - { - o.next.accept(this); - } + /// Composite type + public override void visit(TypePointer o) + { + o.next.accept(this); + } - public override void visit(TypeReference o) - { - o.next.accept(this); + public override void visit(TypeReference o) + { + o.next.accept(this); + } } + + scope remainingVisitor = new LeftoverVisitor(previous, toWrite); + tf.next.accept(remainingVisitor); } From c99d2971d5fb047b134609c568179434faadf4dc Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Fri, 31 Mar 2023 16:44:48 +0800 Subject: [PATCH 087/301] Add a `lowering` field for AssignExp to store potential lowerings (dlang/dmd!14985) * Fix Issue 23773 - Add a lowering field for AssignExp to store potential lowerings and use it for array length expressions * Add LoweredAssignExp AST node --- dmd/dinterpret.d | 19 ----------------- dmd/expression.d | 28 ++++++++++++++++++++++++++ dmd/expression.h | 11 ++++++++++ dmd/expressionsem.d | 6 ++++-- dmd/frontend.h | 14 +++++++++++++ dmd/hdrgen.d | 12 +++++++++++ dmd/inline.d | 14 ++++++++----- dmd/sideeffect.d | 1 + dmd/tokens.d | 2 ++ dmd/visitor.d | 7 +++++++ tests/dmd/fail_compilation/fail23773.d | 15 ++++++++++++++ 11 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 tests/dmd/fail_compilation/fail23773.d diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index c3e95cd404f..255a8f2826e 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -4770,25 +4770,6 @@ public: result = CTFEExp.voidexp; return; } - else if (fd.ident == Id._d_arraysetlengthT) - { - // In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`. - // The following code will rewrite it back to `ea.length = eb` and then interpret that expression. - assert(e.arguments.length == 2); - - Expression ea = (*e.arguments)[0]; - Expression eb = (*e.arguments)[1]; - - auto ale = ctfeEmplaceExp!ArrayLengthExp(e.loc, ea); - ale.type = Type.tsize_t; - AssignExp ae = ctfeEmplaceExp!AssignExp(e.loc, ale, eb); - ae.type = ea.type; - - // if (global.params.verbose) - // message("interpret %s =>\n %s", e.toChars(), ae.toChars()); - result = interpretRegion(ae, istate); - return; - } else if (isArrayConstructionOrAssign(fd.ident)) { // In expressionsem.d, the following lowerings were performed: diff --git a/dmd/expression.d b/dmd/expression.d index 9316fc37dc3..c85d7798fda 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -1785,6 +1785,7 @@ extern (C++) abstract class Expression : ASTNode inout(PostExp) isPostExp() { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; } inout(PreExp) isPreExp() { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; } inout(AssignExp) isAssignExp() { return op == EXP.assign ? cast(typeof(return))this : null; } + inout(LoweredAssignExp) isLoweredAssignExp() { return op == EXP.loweredAssignExp ? cast(typeof(return))this : null; } inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; } inout(BlitExp) isBlitExp() { return op == EXP.blit ? cast(typeof(return))this : null; } inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; } @@ -6167,6 +6168,32 @@ extern (C++) class AssignExp : BinExp } } +/*********************************************************** + * When an assignment expression is lowered to a druntime call + * this class is used to store the lowering. + * It essentially behaves the same as an AssignExp, but it is + * used to not waste space for other AssignExp that are not + * lowered to anything. + */ +extern (C++) final class LoweredAssignExp : AssignExp +{ + Expression lowering; + extern (D) this(AssignExp exp, Expression lowering) + { + super(exp.loc, EXP.loweredAssignExp, exp.e1, exp.e2); + this.lowering = lowering; + } + + override const(char)* toChars() const + { + return lowering.toChars(); + } + override void accept(Visitor v) + { + v.visit(this); + } +} + /*********************************************************** */ extern (C++) final class ConstructExp : AssignExp @@ -7554,4 +7581,5 @@ private immutable ubyte[EXP.max+1] expSize = [ EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp), EXP._Generic: __traits(classInstanceSize, GenericExp), EXP.interval: __traits(classInstanceSize, IntervalExp), + EXP.loweredAssignExp : __traits(classInstanceSize, LoweredAssignExp), ]; diff --git a/dmd/expression.h b/dmd/expression.h index 80d8030b470..895291b5b37 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -38,6 +38,7 @@ class TemplateDeclaration; class ClassDeclaration; class OverloadSet; class StringExp; +class LoweredAssignExp; struct UnionExp; #ifdef IN_GCC typedef union tree_node Symbol; @@ -240,6 +241,7 @@ class Expression : public ASTNode UnaExp* isUnaExp(); BinExp* isBinExp(); BinAssignExp* isBinAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); void accept(Visitor *v) override { v->visit(this); } }; @@ -1082,6 +1084,15 @@ class ConstructExp final : public AssignExp void accept(Visitor *v) override { v->visit(this); } }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression *lowering; + + const char *toChars() const override; + void accept(Visitor *v) override { v->visit(this); } +}; + class BlitExp final : public AssignExp { public: diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 636807edddc..705cefc99f9 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -9828,10 +9828,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor arguments.push(ale.e1); arguments.push(exp.e2); - Expression ce = new CallExp(ale.loc, id, arguments); - auto res = ce.expressionSemantic(sc); + Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc); + auto res = new LoweredAssignExp(exp, ce); // if (global.params.verbose) // message("lowered %s =>\n %s", exp.toChars(), res.toChars()); + res.type = Type.tsize_t; return setResult(res); } else if (auto se = exp.e1.isSliceExp()) @@ -13939,6 +13940,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.assign: case EXP.construct: case EXP.blit: + case EXP.loweredAssignExp: if (sc.flags & SCOPE.Cfile) return exp; // Things like: diff --git a/dmd/frontend.h b/dmd/frontend.h index 86cfe0ffca6..085d94aaa37 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -181,6 +181,7 @@ class IndexExp; class PostExp; class PreExp; class AssignExp; +class LoweredAssignExp; class ConstructExp; class BlitExp; class AddAssignExp; @@ -924,6 +925,7 @@ enum class EXP : uint8_t compoundLiteral = 123u, _Generic = 124u, interval = 125u, + loweredAssignExp = 126u, }; typedef uint64_t dinteger_t; @@ -1069,6 +1071,7 @@ class Expression : public ASTNode PostExp* isPostExp(); PreExp* isPreExp(); AssignExp* isAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); ConstructExp* isConstructExp(); BlitExp* isBlitExp(); AddAssignExp* isAddAssignExp(); @@ -4937,6 +4940,7 @@ struct ASTCodegen final using IsExp = ::IsExp; using LineInitExp = ::LineInitExp; using LogicalExp = ::LogicalExp; + using LoweredAssignExp = ::LoweredAssignExp; using MemorySet = ::MemorySet; using MinAssignExp = ::MinAssignExp; using MinExp = ::MinExp; @@ -5200,6 +5204,7 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp* e); virtual void visit(VoidInitExp* e); virtual void visit(ThrownExceptionExp* e); + virtual void visit(LoweredAssignExp* e); }; class StoppableVisitor : public Visitor @@ -7722,6 +7727,14 @@ class AssignExp : public BinExp void accept(Visitor* v) override; }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression* lowering; + const char* toChars() const override; + void accept(Visitor* v) override; +}; + class ConstructExp final : public AssignExp { public: @@ -8403,6 +8416,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(DotExp* e) override; void visit(IndexExp* e) override; void visit(RemoveExp* e) override; + void visit(LoweredAssignExp* e) override; }; extern _d_real creall(complex_t x); diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 8fe2d9f597a..f38ae9aad5f 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -2318,6 +2318,16 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg expToBuffer(e.e1, precedence[e.op], buf, hgs); } + void visitLoweredAssignExp(LoweredAssignExp e) + { + if (global.params.vcg_ast) + { + expressionToBuffer(e.lowering, buf, hgs); + return; + } + + visit(cast(BinExp)e); + } void visitBin(BinExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); @@ -2691,6 +2701,7 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg case EXP.remove: return visitRemove(e.isRemoveExp()); case EXP.question: return visitCond(e.isCondExp()); case EXP.classReference: return visitClassReference(e.isClassReferenceExp()); + case EXP.loweredAssignExp: return visitLoweredAssignExp(e.isLoweredAssignExp()); } } @@ -4219,6 +4230,7 @@ string EXPtoString(EXP op) EXP.declaration : "declaration", EXP.interval : "interval", + EXP.loweredAssignExp : "=" ]; const p = strings[op]; if (!p) diff --git a/dmd/inline.d b/dmd/inline.d index 954e58f7935..aaa7c3174c7 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -766,12 +766,11 @@ public: override void visit(AssignExp e) { visit(cast(BinExp)e); + } - if (auto ale = e.e1.isArrayLengthExp()) - { - Type tn = ale.e1.type.toBasetype().nextOf(); - semanticTypeInfo(null, tn); - } + override void visit(LoweredAssignExp e) + { + result = doInlineAs!Expression(e.lowering, ids); } override void visit(EqualExp e) @@ -1286,6 +1285,11 @@ public: visit(cast(BinExp)e); } + override void visit(LoweredAssignExp e) + { + inlineScan(e.lowering); + } + override void visit(CallExp e) { //printf("CallExp.inlineScan() %s\n", e.toChars()); diff --git a/dmd/sideeffect.d b/dmd/sideeffect.d index 60a74cc2812..3f3e7e6377a 100644 --- a/dmd/sideeffect.d +++ b/dmd/sideeffect.d @@ -185,6 +185,7 @@ private bool lambdaHasSideEffect(Expression e, bool assumeImpureCalls = false) case EXP.delete_: case EXP.new_: case EXP.newAnonymousClass: + case EXP.loweredAssignExp: return true; case EXP.call: { diff --git a/dmd/tokens.d b/dmd/tokens.d index f2701fe42ee..0a698c5b30e 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -419,6 +419,8 @@ enum EXP : ubyte compoundLiteral, // ( type-name ) { initializer-list } _Generic, interval, + + loweredAssignExp, } enum FirstCKeyword = TOK.inline; diff --git a/dmd/visitor.d b/dmd/visitor.d index e8c77d47293..7b059a061fd 100644 --- a/dmd/visitor.d +++ b/dmd/visitor.d @@ -89,6 +89,7 @@ public: void visit(ASTCodegen.ClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.VoidInitExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.ThrownExceptionExp e) { visit(cast(ASTCodegen.Expression)e); } + void visit(ASTCodegen.LoweredAssignExp e) { visit(cast(ASTCodegen.AssignExp)e); } } /** @@ -240,6 +241,12 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor e.e1.accept(this); e.e2.accept(this); } + + override void visit(ASTCodegen.LoweredAssignExp e) + { + e.lowering.accept(this); + visit(cast(AssignExp)e); + } } extern (C++) class StoppableVisitor : Visitor diff --git a/tests/dmd/fail_compilation/fail23773.d b/tests/dmd/fail_compilation/fail23773.d new file mode 100644 index 00000000000..e6cdc3e0354 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23773.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23773 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23773.d(14): Error: assignment cannot be used as a condition, perhaps `==` was meant? +--- +*/ + +void main() +{ + int i; + int[] arr; + assert(arr.length = i); +} From 0bfa12f1ac54ba1aab3d61131b3fdeffdeae3fc5 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 31 Mar 2023 13:52:01 +0200 Subject: [PATCH 088/301] StructLiteralExp: Reduce class instance size --- dmd/expression.d | 12 +++++++++--- dmd/expression.h | 10 +++++++--- dmd/frontend.h | 9 ++++++--- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/dmd/expression.d b/dmd/expression.d index c85d7798fda..beb63006328 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -3293,7 +3293,15 @@ extern (C++) final class StructLiteralExp : Expression Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip Type stype; /// final type of result (can be different from sd's type) - Symbol* sym; /// back end symbol to initialize with literal + // `inlineCopy` is only used temporarily in the `inline.d` pass, + // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after + union + { + Symbol* sym; /// back end symbol to initialize with literal + + /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp inlinecopy; + } /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. @@ -3302,8 +3310,6 @@ extern (C++) final class StructLiteralExp : Expression */ StructLiteralExp origin; - /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. diff --git a/dmd/expression.h b/dmd/expression.h index 895291b5b37..4db147b8ebb 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -459,7 +459,13 @@ class StructLiteralExp final : public Expression Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip Type *stype; // final type of result (can be different from sd's type) - Symbol *sym; // back end symbol to initialize with literal + union + { + Symbol *sym; // back end symbol to initialize with literal + + // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp *inlinecopy; + }; /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. @@ -468,8 +474,6 @@ class StructLiteralExp final : public Expression */ StructLiteralExp *origin; - // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp *inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. diff --git a/dmd/frontend.h b/dmd/frontend.h index 085d94aaa37..5602e506a02 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -6978,7 +6978,7 @@ struct UnionExp final char stringexp[58LLU]; char arrayliteralexp[56LLU]; char assocarrayliteralexp[56LLU]; - char structliteralexp[92LLU]; + char structliteralexp[84LLU]; char compoundliteralexp[48LLU]; char nullexp[34LLU]; char dotvarexp[65LLU]; @@ -7203,9 +7203,12 @@ class StructLiteralExp final : public Expression StructDeclaration* sd; Array* elements; Type* stype; - Symbol* sym; + union + { + Symbol* sym; + StructLiteralExp* inlinecopy; + }; StructLiteralExp* origin; - StructLiteralExp* inlinecopy; uint8_t stageflags; bool useStaticInit; bool isOriginal; From bc46326650f3e85363c4fb693937f5293f2b0d2a Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 30 Mar 2023 21:41:32 -0700 Subject: [PATCH 089/301] change ESCAPE from flag to bool --- dmd/cppmanglewin.d | 207 ++++++++++++++++++++------------------------- 1 file changed, 94 insertions(+), 113 deletions(-) diff --git a/dmd/cppmanglewin.d b/dmd/cppmanglewin.d index 165e8c7f18c..2268bc7b33c 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/cppmanglewin.d @@ -88,57 +88,37 @@ private extern (D) bool checkImmutableShared(Type type, Loc loc) private final class VisualCPPMangler : Visitor { - enum VC_SAVED_TYPE_CNT = 10u; - enum VC_SAVED_IDENT_CNT = 10u; - alias visit = Visitor.visit; - Identifier[VC_SAVED_IDENT_CNT] saved_idents; - Type[VC_SAVED_TYPE_CNT] saved_types; - Loc loc; /// location for use in error messages - - // IS_NOT_TOP_TYPE: when we mangling one argument, we can call visit several times (for base types of arg type) - // but we must save only arg type: - // For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" - // This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. - // MANGLE_RETURN_TYPE: return type shouldn't be saved and substituted in arguments - // IGNORE_CONST: in some cases we should ignore CV-modifiers. - // ESCAPE: toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. - - enum Flags : int - { - IS_NOT_TOP_TYPE = 0x1, - MANGLE_RETURN_TYPE = 0x2, - IGNORE_CONST = 0x4, - IS_DMC = 0x8, - ESCAPE = 0x10, - } + Identifier[10] saved_idents; + Type[10] saved_types; + Loc loc; /// location for use in error messages + + bool isNotTopType; /** When mangling one argument, we can call visit several times (for base types of arg type) + * but must save only arg type: + * For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" + * This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. + */ + bool ignoreConst; /// in some cases we should ignore CV-modifiers. + bool escape; /// toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. + bool mangleReturnType; /// return type shouldn't be saved and substituted in arguments + bool isDmc; /// Digital Mars C++ name mangling - alias IS_NOT_TOP_TYPE = Flags.IS_NOT_TOP_TYPE; - alias MANGLE_RETURN_TYPE = Flags.MANGLE_RETURN_TYPE; - alias IGNORE_CONST = Flags.IGNORE_CONST; - alias IS_DMC = Flags.IS_DMC; - alias ESCAPE = Flags.ESCAPE; - - int flags; OutBuffer buf; extern (D) this(VisualCPPMangler rvl) scope { - flags |= (rvl.flags & IS_DMC); saved_idents[] = rvl.saved_idents[]; - saved_types[] = rvl.saved_types[]; - loc = rvl.loc; + saved_types[] = rvl.saved_types[]; + isDmc = rvl.isDmc; + loc = rvl.loc; } public: - extern (D) this(bool isdmc, Loc loc) scope + extern (D) this(bool isDmc, Loc loc) scope { - if (isdmc) - { - flags |= IS_DMC; - } saved_idents[] = null; saved_types[] = null; + this.isDmc = isDmc; this.loc = loc; } @@ -159,8 +139,8 @@ public: return; buf.writestring("$$T"); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeNoreturn type) @@ -171,17 +151,17 @@ public: return; buf.writeByte('X'); // yes, mangle it like `void` - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeBasic type) { - //printf("visit(TypeBasic); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeBasic); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -190,7 +170,7 @@ public: { return; } - if (!(flags & IS_DMC)) + if (!isDmc) { switch (type.ty) { @@ -251,7 +231,7 @@ public: buf.writeByte('N'); break; case Tfloat80: - if (flags & IS_DMC) + if (isDmc) buf.writestring("_Z"); // DigitalMars long double else buf.writestring("_T"); // Intel long double @@ -272,33 +252,33 @@ public: visit(cast(Type)type); return; } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeVector type) { - //printf("visit(TypeVector); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeVector); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; mangleModifier(type); buf.writestring("T__m128@@"); // may be better as __m128i or __m128d? - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeSArray type) { // This method can be called only for static variable type mangling. - //printf("visit(TypeSArray); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeSArray); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; // first dimension always mangled as const pointer - if (flags & IS_DMC) + if (isDmc) buf.writeByte('Q'); else buf.writeByte('P'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -314,7 +294,7 @@ public: // There is not way to map int C++ (*arr)[2][1] to D override void visit(TypePointer type) { - //printf("visit(TypePointer); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypePointer); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; @@ -334,8 +314,8 @@ public: buf.writeByte('P'); // mutable buf.writeByte('6'); // pointer to a function buf.writestring(arg); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; return; } else if (type.next.ty == Tsarray) @@ -343,13 +323,13 @@ public: if (checkTypeSaved(type)) return; mangleModifier(type); - if (type.isConst() || !(flags & IS_DMC)) + if (type.isConst() || !isDmc) buf.writeByte('Q'); // const else buf.writeByte('P'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleArray(cast(TypeSArray)type.next); return; } @@ -368,7 +348,7 @@ public: } if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; type.next.accept(this); } } @@ -385,7 +365,7 @@ public: buf.writeByte('A'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -400,7 +380,7 @@ public: override void visit(TypeFunction type) { const(char)* arg = mangleFunctionType(type); - if ((flags & IS_DMC)) + if (isDmc) { if (checkTypeSaved(type)) return; @@ -410,14 +390,15 @@ public: buf.writestring("$$A6"); } buf.writestring(arg); - flags &= ~(IS_NOT_TOP_TYPE | IGNORE_CONST); + isNotTopType = false; + ignoreConst = false; } override void visit(TypeStruct type) { if (checkTypeSaved(type)) return; - //printf("visit(TypeStruct); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeStruct); is_not_top_type = %d\n", isNotTopType); mangleModifier(type); const agg = type.sym.isStructDeclaration(); if (type.sym.isUnionDeclaration()) @@ -425,13 +406,13 @@ public: else buf.writeByte(agg.cppmangle == CPPMANGLE.asClass ? 'V' : 'U'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeEnum type) { - //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & isNotTopType)); const id = type.sym.ident; string c; if (id == Id.__c_long_double) @@ -448,7 +429,7 @@ public: c = "D"; // VC++ char else if (id == Id.__c_wchar_t) { - c = (flags & IS_DMC) ? "_Y" : "_W"; + c = isDmc ? "_Y" : "_W"; } if (c.length) @@ -456,7 +437,7 @@ public: if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -472,18 +453,18 @@ public: buf.writestring("W4"); mangleIdent(type.sym); } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } // D class mangled as pointer to C++ class // const(Object) mangled as Object const* const override void visit(TypeClass type) { - //printf("visit(TypeClass); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeClass); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) mangleModifier(type); if (type.isConst()) buf.writeByte('Q'); @@ -491,13 +472,13 @@ public: buf.writeByte('P'); if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleModifier(type); const cldecl = type.sym.isClassDeclaration(); buf.writeByte(cldecl.cppmangle == CPPMANGLE.asStruct ? 'U' : 'V'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } const(char)* mangleOf(Dsymbol s) @@ -670,7 +651,7 @@ extern(D): else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) { buf.writeByte('$'); - if (flags & IS_DMC) + if (isDmc) buf.writeByte('1'); else buf.writeByte('E'); @@ -679,7 +660,7 @@ extern(D): else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) { Dsymbol ds = d.isTemplateDeclaration().onemember; - if (flags & IS_DMC) + if (isDmc) { buf.writeByte('V'); } @@ -720,11 +701,11 @@ extern(D): */ void mangleTemplateType(RootObject o) { - flags |= ESCAPE; + escape = true; Type t = isType(o); assert(t); t.accept(this); - flags &= ~ESCAPE; + escape = false; } /** @@ -810,14 +791,14 @@ extern(D): } } - scope VisualCPPMangler tmp = new VisualCPPMangler((flags & IS_DMC) ? true : false, loc); + scope VisualCPPMangler tmp = new VisualCPPMangler(isDmc ? true : false, loc); tmp.buf.writeByte('?'); tmp.buf.writeByte('$'); tmp.buf.writestring(symName); tmp.saved_idents[0] = id; if (symName == id.toString()) tmp.buf.writeByte('@'); - if (flags & IS_DMC) + if (isDmc) { tmp.mangleIdent(sym.parent, true); is_dmc_template = true; @@ -873,16 +854,16 @@ extern(D): // returns true if name already saved bool checkAndSaveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (i, ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { - buf.writeByte(i + '0'); + buf.writeByte(cast(uint)i + '0'); return true; } } @@ -891,14 +872,14 @@ extern(D): void saveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { return; } @@ -943,22 +924,22 @@ extern(D): bool checkTypeSaved(Type type) { - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) return false; - if (flags & MANGLE_RETURN_TYPE) + if (mangleReturnType) return false; - for (uint i = 0; i < VC_SAVED_TYPE_CNT; i++) + foreach (i, ref ty; saved_types) { - if (!saved_types[i]) // no saved same type + if (!ty) // no saved same type { - saved_types[i] = type; + ty = type; return false; } - if (saved_types[i].equals(type)) // ok, we've found same type. use index instead of type + if (ty.equals(type)) // ok, we've found same type. use index instead of type { - buf.writeByte(i + '0'); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + buf.writeByte(cast(uint)i + '0'); + isNotTopType = false; + ignoreConst = false; return true; } } @@ -967,7 +948,7 @@ extern(D): void mangleModifier(Type type) { - if (flags & IGNORE_CONST) + if (ignoreConst) return; if (checkImmutableShared(type, loc)) return; @@ -976,17 +957,17 @@ extern(D): { // Template parameters that are not pointers and are const need an $$C escape // in addition to 'B' (const). - if ((flags & ESCAPE) && type.ty != Tpointer) + if (escape && type.ty != Tpointer) buf.writestring("$$CB"); - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('B'); // const - else if ((flags & IS_DMC) && type.ty != Tpointer) + else if (isDmc && type.ty != Tpointer) buf.writestring("_O"); } - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('A'); // mutable - flags &= ~ESCAPE; + escape = false; } void mangleArray(TypeSArray type) @@ -1008,7 +989,7 @@ extern(D): mangleNumber(buf, sa.dim ? sa.dim.toInteger() : 0); cur = cur.nextOf(); } - flags |= IGNORE_CONST; + ignoreConst = true; cur.accept(this); } @@ -1045,7 +1026,7 @@ extern(D): assert(0); } } - tmp.flags &= ~IS_NOT_TOP_TYPE; + tmp.isNotTopType = false; if (noreturn) { tmp.buf.writeByte('@'); @@ -1055,7 +1036,7 @@ extern(D): Type rettype = type.next; if (type.isref) rettype = rettype.referenceTo(); - flags &= ~IGNORE_CONST; + ignoreConst = false; if (rettype.ty == Tstruct) { tmp.buf.writeByte('?'); @@ -1070,9 +1051,9 @@ extern(D): tmp.buf.writeByte('A'); } } - tmp.flags |= MANGLE_RETURN_TYPE; + tmp.mangleReturnType = true; rettype.accept(tmp); - tmp.flags &= ~MANGLE_RETURN_TYPE; + tmp.mangleReturnType = false; } if (!type.parameterList.parameters || !type.parameterList.parameters.length) { @@ -1103,8 +1084,8 @@ extern(D): errorSupplemental(loc, "Use pointer instead."); assert(0); } - tmp.flags &= ~IS_NOT_TOP_TYPE; - tmp.flags &= ~IGNORE_CONST; + tmp.isNotTopType = false; + ignoreConst = false; t.accept(tmp); } From 73fa0a46c5e7a478a11474ae8f53cbe4175663f8 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 31 Mar 2023 15:50:23 +0200 Subject: [PATCH 090/301] Deduplicate function/template branches in aliasthisOf --- dmd/mtype.d | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/dmd/mtype.d b/dmd/mtype.d index d97554ffddf..704fc2e7a88 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -2044,9 +2044,11 @@ extern (C++) abstract class Type : ASTNode t = t.addMod(this.mod); return t; } - if (auto fd = s.isFuncDeclaration()) + Dsymbol callable = s.isFuncDeclaration(); + callable = callable ? callable : s.isTemplateDeclaration(); + if (callable) { - fd = resolveFuncCall(Loc.initial, null, fd, null, this, ArgumentList(), FuncResolveFlag.quiet); + auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet); if (!fd || fd.errors || !fd.functionSemantic()) return Type.terror; @@ -2065,19 +2067,6 @@ extern (C++) abstract class Type : ASTNode { return ed.type; } - if (auto td = s.isTemplateDeclaration()) - { - assert(td._scope); - auto fd = resolveFuncCall(Loc.initial, null, td, null, this, ArgumentList(), FuncResolveFlag.quiet); - if (!fd || fd.errors || !fd.functionSemantic()) - return Type.terror; - - auto t = fd.type.nextOf(); - if (!t) - return Type.terror; - t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod); - return t; - } //printf("%s\n", s.kind()); return null; From 65ba57ba7e7dc51ba1735ed8a0f7b647e73d2f81 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 1 Apr 2023 11:44:29 -0700 Subject: [PATCH 091/301] cppmangle.d: eliminate many unsafe casts --- dmd/cppmangle.d | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index dd4e48df2ec..55e5440aadf 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -211,7 +211,7 @@ private final class CppMangleVisitor : Visitor */ void mangleReturnType(TypeFunction preSemantic) { - auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + auto tf = this.context.res.asFuncDecl().type.isTypeFunction(); Type rt = preSemantic.nextOf(); if (tf.isref) rt = rt.referenceTo(); @@ -491,9 +491,9 @@ private final class CppMangleVisitor : Visitor mangle_function(d.isFuncDeclaration()); buf.writestring("EE"); } - else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) + else if (e && e.isVarExp() && e.isVarExp().var.isVarDeclaration()) { - VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration vd = e.isVarExp().var.isVarDeclaration(); buf.writeByte('L'); mangle_variable(vd, true); buf.writeByte('E'); @@ -748,9 +748,9 @@ private final class CppMangleVisitor : Visitor bool isIdent_char(Identifier ident, RootObject o) { Type t = isType(o); - if (!t || t.ty != Tstruct) + if (!t || !t.isTypeStruct()) return false; - Dsymbol s = (cast(TypeStruct)t).toDsymbol(null); + Dsymbol s = t.toDsymbol(null); if (s.ident != ident) return false; Dsymbol p = s.toParent(); @@ -1050,7 +1050,7 @@ private final class CppMangleVisitor : Visitor * ::= * ::= */ - TypeFunction tf = cast(TypeFunction)d.type; + TypeFunction tf = d.type.isTypeFunction(); if (TemplateDeclaration ftd = getFuncTemplateDecl(d)) { @@ -1164,7 +1164,7 @@ private final class CppMangleVisitor : Visitor this.context.ti = ti; this.context.fd = d; this.context.res = d; - TypeFunction preSemantic = cast(TypeFunction)d.originalType; + TypeFunction preSemantic = d.originalType.isTypeFunction(); auto nspace = ti.toParent(); if (nspace && nspace.isNspace()) this.writeChained(ti.toParent(), () => source_name(ti, true)); @@ -1338,7 +1338,7 @@ private final class CppMangleVisitor : Visitor auto prev = this.context.push({ TypeFunction tf; if (isDsymbol(this.context.res)) - tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + tf = this.context.res.asFuncDecl().type.isTypeFunction(); else tf = this.context.res.asType().isTypeFunction(); assert(tf); @@ -1382,9 +1382,9 @@ private final class CppMangleVisitor : Visitor */ void headOfType(Type t) { - if (t.ty == Tclass) + if (auto tc = t.isTypeClass()) { - mangleTypeClass(cast(TypeClass)t, true); + mangleTypeClass(tc, true); } else { @@ -1951,7 +1951,7 @@ extern(C++): */ override void visit(TypeIdentifier t) { - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); assert(decl.parameters !is null); auto idx = templateParamIndex(t.ident, decl.parameters); // If not found, default to the post-semantic type @@ -2010,7 +2010,7 @@ extern(C++): { // If the resolved AST has more args than the parse one, // we have default arguments - auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters; + auto oparams = analyzed_ti.tempdecl.isTemplateDeclaration().origParameters; foreach (idx, arg; (*oparams)[t.tiargs.length .. $]) { this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.length]; @@ -2035,7 +2035,7 @@ extern(C++): assert(t.tiargs !is null); bool needsTa; - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); // Attempt to substitute the template itself auto idx = templateParamIndex(t.name, decl.parameters); if (idx < decl.parameters.length) @@ -2116,12 +2116,13 @@ private void visitObject(V : Visitor)(RootObject o, V this_) /// Helper function to safely get a type out of a `RootObject` private Type asType(RootObject o) { - Type ta = isType(o); + if (Type ta = isType(o)) + return ta; + // When called with context.res as argument, it can be `FuncDeclaration` - if (!ta && o.asFuncDecl()) - ta = (cast(FuncDeclaration)o).type; - assert(ta !is null, o.toString()); - return ta; + if (auto fd = o.asFuncDecl()) + return fd.type; + assert(0); } /// Helper function to safely get a `FuncDeclaration` out of a `RootObject` @@ -2174,12 +2175,12 @@ private extern(C++) final class ComponentVisitor : Visitor case DYNCAST.type: auto t = cast(Type)base; - if (t.ty == Tpointer) - this.tpointer = cast(TypePointer)t; - else if (t.ty == Treference) - this.tref = cast(TypeReference)t; - else if (t.ty == Tident) - this.tident = cast(TypeIdentifier)t; + if (auto tp = t.isTypePointer()) + this.tpointer = tp; + else if (auto tr = t.isTypeReference()) + this.tref = tr; + else if (auto ti = t.isTypeIdentifier()) + this.tident = ti; else goto default; break; From 4cb8433f76ccc451dda24171503ba83a6e999c48 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 3 Apr 2023 10:01:05 +0200 Subject: [PATCH 092/301] Don't store recursive alias this type in `UnaExp` (dlang/dmd!15064) --- dmd/aliasthis.d | 20 +++++++++++++++++--- dmd/expression.d | 1 - dmd/expression.h | 1 - dmd/expressionsem.d | 6 ++++-- dmd/frontend.h | 9 ++++----- dmd/mtype.d | 6 ++++++ dmd/opover.d | 44 ++++++++++++++++++++++++++------------------ 7 files changed, 57 insertions(+), 30 deletions(-) diff --git a/dmd/aliasthis.d b/dmd/aliasthis.d index ef839fae536..bc3d919cc80 100644 --- a/dmd/aliasthis.d +++ b/dmd/aliasthis.d @@ -200,15 +200,29 @@ bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) /************************************** * Check and set 'att' if 't' is a recursive 'alias this' type + * + * The goal is to prevent endless loops when there is a cycle in the alias this chain. + * Since there is no multiple `alias this`, the chain either ends in a leaf, + * or it loops back on itself as some point. + * + * Example: S0 -> (S1 -> S2 -> S3 -> S1) + * + * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried. + * `S1` is a recursive alias this type, but since `att` is initialized to `null`, + * this still returns `false`, but `att1` is set to `S1`. + * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again, + * we notice `att == t`, so we're back at the start of the loop, and this returns `true`. + * * Params: - * att = type reference used to detect recursion - * t = 'alias this' type + * att = type reference used to detect recursion. Should be initialized to `null`. + * t = type of 'alias this' rewrite to attempt * * Returns: - * Whether the 'alias this' is recursive or not + * `false` if the rewrite is safe, `true` if it would loop back around */ bool isRecursiveAliasThis(ref Type att, Type t) { + //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars()); auto tb = t.toBasetype(); if (att && tb.equivalent(att)) return true; diff --git a/dmd/expression.d b/dmd/expression.d index beb63006328..4226709220e 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -4360,7 +4360,6 @@ extern (C++) final class IsExp : Expression extern (C++) abstract class UnaExp : Expression { Expression e1; - Type att1; // Save alias this type to detect recursion extern (D) this(const ref Loc loc, EXP op, Expression e1) scope { diff --git a/dmd/expression.h b/dmd/expression.h index 4db147b8ebb..1c38ec56fd4 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -699,7 +699,6 @@ class UnaExp : public Expression { public: Expression *e1; - Type *att1; // Save alias this type to detect recursion UnaExp *syntaxCopy() override; Expression *incompatibleTypes(); diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 8911b98b018..ed01c361c97 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -4565,6 +4565,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + Type att = null; Lagain: //printf("Lagain: %s\n", toChars()); exp.f = null; @@ -4775,7 +4776,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) { - if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type)) + if (sd.aliasthis && !isRecursiveAliasThis(att, exp.e1.type)) { exp.e1 = resolveAliasThis(sc, exp.e1); goto Lagain; @@ -8951,6 +8952,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor assert((*ae.arguments)[0].op == EXP.interval); ie = cast(IntervalExp)(*ae.arguments)[0]; } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -9025,7 +9027,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // No operator overloading member function found yet, but // there might be an alias this to try. - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op e2) as: * a.aliasthis[arguments] op e2 diff --git a/dmd/frontend.h b/dmd/frontend.h index 1406e91d3fd..8730ab91f22 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -6981,11 +6981,11 @@ struct UnionExp final char structliteralexp[84LLU]; char compoundliteralexp[48LLU]; char nullexp[34LLU]; - char dotvarexp[65LLU]; - char addrexp[56LLU]; + char dotvarexp[57LLU]; + char addrexp[48LLU]; char indexexp[82LLU]; - char sliceexp[81LLU]; - char vectorexp[69LLU]; + char sliceexp[73LLU]; + char vectorexp[61LLU]; }; #pragma pack(pop) @@ -7389,7 +7389,6 @@ class UnaExp : public Expression { public: Expression* e1; - Type* att1; UnaExp* syntaxCopy() override; Expression* incompatibleTypes(); void setNoderefOperand(); diff --git a/dmd/mtype.d b/dmd/mtype.d index 704fc2e7a88..95845a4f28b 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -2072,6 +2072,12 @@ extern (C++) abstract class Type : ASTNode return null; } + /** + * Check whether this type has endless `alias this` recursion. + * Returns: + * `true` if this type has an `alias this` that can be implicitly + * converted back to this type itself. + */ extern (D) final bool checkAliasThisRec() { Type tb = toBasetype(); diff --git a/dmd/opover.d b/dmd/opover.d index 8d202db69ea..8306004371f 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -293,6 +293,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -354,7 +355,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite op(a[arguments]) as: * op(a.aliasthis[arguments]) @@ -370,13 +371,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } e.e1 = e.e1.expressionSemantic(sc); e.e1 = resolveProperties(sc, e.e1); - if (e.e1.op == EXP.error) - { - return e.e1; - } - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + Type att = null; // first cyclic `alias this` type + while (1) { + if (e.e1.op == EXP.error) + { + return e.e1; + } + + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + Dsymbol fd = null; /* Rewrite as: * e1.opUnary!(op)() @@ -404,18 +410,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { /* Rewrite op(e1) as: * op(e1.aliasthis) */ //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident); - UnaExp ue = cast(UnaExp)e.copy(); - ue.e1 = e1; - result = ue.trySemantic(sc); - return result; + e.e1 = resolveAliasThis(sc, e.e1, true); + if (e.e1) + continue; + break; } + break; } return result; } @@ -433,6 +439,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) ie = (*ae.arguments)[0].isIntervalExp(); } Expression result; + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -526,7 +533,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { //printf("att arr e1 = %s\n", this.e1.type.toChars()); /* Rewrite op(a[arguments]) as: @@ -547,7 +554,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * This is mostly the same as UnaryExp::op_overload(), but has * a different rewrite. */ - Expression visitCast(CastExp e) + Expression visitCast(CastExp e, Type att = null) { //printf("CastExp::op_overload() (%s)\n", e.toChars()); Expression result; @@ -578,7 +585,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { /* Rewrite op(e1) as: * op(e1.aliasthis) @@ -587,7 +594,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { result = e.copy(); (cast(UnaExp)result).e1 = e1; - result = result.op_overload(sc); + result = visitCast(result.isCastExp(), att); return result; } } @@ -1114,6 +1121,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -1185,7 +1193,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op= e2) as: * a.aliasthis[arguments] op= e2 From cdc709e48419ed199a6b0360999b0cf3aa5a9630 Mon Sep 17 00:00:00 2001 From: lucica28 <57060141+lucica28@users.noreply.github.com> Date: Tue, 4 Apr 2023 12:36:08 +0300 Subject: [PATCH 093/301] move addDefaultVersionIdentifiers to target (dlang/dmd!15073) --- dmd/mars.d | 181 ----------------------------- dmd/target.d | 186 +++++++++++++++++++++++++++++- tests/dmd/unit/deinitialization.d | 2 +- 3 files changed, 186 insertions(+), 183 deletions(-) diff --git a/dmd/mars.d b/dmd/mars.d index 7027735109f..46ae32fd2db 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -1150,187 +1150,6 @@ private void setDefaultLibrary(ref Param params, const ref Target target) driverParams.debuglibname = driverParams.defaultlibname; } -/** - * Add default `version` identifier for dmd, and set the - * target platform in `params`. - * https://dlang.org/spec/version.html#predefined-versions - * - * Needs to be run after all arguments parsing (command line, DFLAGS environment - * variable and config file) in order to add final flags (such as `X86_64` or - * the `CRuntime` used). - * - * Params: - * params = which target to compile for (set by `setTarget()`) - * tgt = target - */ -public -void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) -{ - VersionCondition.addPredefinedGlobalIdent("DigitalMars"); - VersionCondition.addPredefinedGlobalIdent("LittleEndian"); - VersionCondition.addPredefinedGlobalIdent("D_Version2"); - VersionCondition.addPredefinedGlobalIdent("all"); - - addPredefinedGlobalIdentifiers(tgt); - - if (params.ddoc.doOutput) - VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); - if (params.cov) - VersionCondition.addPredefinedGlobalIdent("D_Coverage"); - if (driverParams.pic != PIC.fixed) - VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); - if (params.useUnitTests) - VersionCondition.addPredefinedGlobalIdent("unittest"); - if (params.useAssert == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("assert"); - if (params.useIn == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); - if (params.useOut == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); - if (params.useInvariants == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_Invariants"); - if (params.useArrayBounds == CHECKENABLE.off) - VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); - if (params.betterC) - { - VersionCondition.addPredefinedGlobalIdent("D_BetterC"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); - VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); - VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); - } - - VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); - - if (params.tracegc) - VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); - - if (driverParams.optimize) - VersionCondition.addPredefinedGlobalIdent("D_Optimized"); -} - -/** - * Add predefined global identifiers that are determied by the target - */ -private -void addPredefinedGlobalIdentifiers(const ref Target tgt) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - if (tgt.cpu >= CPU.sse2) - { - predef("D_SIMD"); - if (tgt.cpu >= CPU.avx) - predef("D_AVX"); - if (tgt.cpu >= CPU.avx2) - predef("D_AVX2"); - } - - with (Target) - { - if (tgt.os & OS.Posix) - predef("Posix"); - if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) - predef("ELFv1"); - switch (tgt.os) - { - case OS.none: { predef("FreeStanding"); break; } - case OS.linux: { predef("linux"); break; } - case OS.OpenBSD: { predef("OpenBSD"); break; } - case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } - case OS.Solaris: { predef("Solaris"); break; } - case OS.Windows: - { - predef("Windows"); - VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); - break; - } - case OS.OSX: - { - predef("OSX"); - // For legacy compatibility - predef("darwin"); - break; - } - case OS.FreeBSD: - { - predef("FreeBSD"); - switch (tgt.osMajor) - { - case 10: predef("FreeBSD_10"); break; - case 11: predef("FreeBSD_11"); break; - case 12: predef("FreeBSD_12"); break; - case 13: predef("FreeBSD_13"); break; - default: predef("FreeBSD_11"); break; - } - break; - } - default: assert(0); - } - } - - addCRuntimePredefinedGlobalIdent(tgt.c); - addCppRuntimePredefinedGlobalIdent(tgt.cpp); - - if (tgt.is64bit) - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); - VersionCondition.addPredefinedGlobalIdent("X86_64"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); - VersionCondition.addPredefinedGlobalIdent("X86"); - } - if (tgt.isLP64) - VersionCondition.addPredefinedGlobalIdent("D_LP64"); - else if (tgt.is64bit) - VersionCondition.addPredefinedGlobalIdent("X32"); -} - -private -void addCRuntimePredefinedGlobalIdent(const ref TargetC c) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetC.Runtime) switch (c.runtime) - { - default: - case Unspecified: return; - case Bionic: return predef("CRuntime_Bionic"); - case DigitalMars: return predef("CRuntime_DigitalMars"); - case Glibc: return predef("CRuntime_Glibc"); - case Microsoft: return predef("CRuntime_Microsoft"); - case Musl: return predef("CRuntime_Musl"); - case Newlib: return predef("CRuntime_Newlib"); - case UClibc: return predef("CRuntime_UClibc"); - case WASI: return predef("CRuntime_WASI"); - } -} - -private -void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetCPP.Runtime) switch (cpp.runtime) - { - default: - case Unspecified: return; - case Clang: return predef("CppRuntime_Clang"); - case DigitalMars: return predef("CppRuntime_DigitalMars"); - case Gcc: return predef("CppRuntime_Gcc"); - case Microsoft: return predef("CppRuntime_Microsoft"); - case Sun: return predef("CppRuntime_Sun"); - } -} - private void printPredefinedVersions(FILE* stream) { if (global.versionids) diff --git a/dmd/target.d b/dmd/target.d index 6741147befa..d5a8edec2ab 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -25,7 +25,7 @@ module dmd.target; -import dmd.globals : Param; +import dmd.globals : Param, CHECKENABLE; enum CPU : ubyte { @@ -85,6 +85,190 @@ ubyte defaultTargetOSMajor() return 0; } +/** + * Add default `version` identifier for dmd, and set the + * target platform in `params`. + * https://dlang.org/spec/version.html#predefined-versions + * + * Needs to be run after all arguments parsing (command line, DFLAGS environment + * variable and config file) in order to add final flags (such as `X86_64` or + * the `CRuntime` used). + * + * Params: + * params = which target to compile for (set by `setTarget()`) + * tgt = target + */ +public +void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) +{ + import dmd.cond : VersionCondition; + import dmd.dmdparams : driverParams, PIC; + + VersionCondition.addPredefinedGlobalIdent("DigitalMars"); + VersionCondition.addPredefinedGlobalIdent("LittleEndian"); + VersionCondition.addPredefinedGlobalIdent("D_Version2"); + VersionCondition.addPredefinedGlobalIdent("all"); + + addPredefinedGlobalIdentifiers(tgt); + + if (params.ddoc.doOutput) + VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); + if (params.cov) + VersionCondition.addPredefinedGlobalIdent("D_Coverage"); + if (driverParams.pic != PIC.fixed) + VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); + if (params.useUnitTests) + VersionCondition.addPredefinedGlobalIdent("unittest"); + if (params.useAssert == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("assert"); + if (params.useIn == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); + if (params.useOut == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); + if (params.useInvariants == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_Invariants"); + if (params.useArrayBounds == CHECKENABLE.off) + VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); + if (params.betterC) + { + VersionCondition.addPredefinedGlobalIdent("D_BetterC"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); + VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); + VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); + } + + VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); + + if (params.tracegc) + VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); + + if (driverParams.optimize) + VersionCondition.addPredefinedGlobalIdent("D_Optimized"); +} + +// /** +// * Add predefined global identifiers that are determied by the target +// */ +private +void addPredefinedGlobalIdentifiers(const ref Target tgt) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + if (tgt.cpu >= CPU.sse2) + { + predef("D_SIMD"); + if (tgt.cpu >= CPU.avx) + predef("D_AVX"); + if (tgt.cpu >= CPU.avx2) + predef("D_AVX2"); + } + + with (Target) + { + if (tgt.os & OS.Posix) + predef("Posix"); + if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) + predef("ELFv1"); + switch (tgt.os) + { + case OS.none: { predef("FreeStanding"); break; } + case OS.linux: { predef("linux"); break; } + case OS.OpenBSD: { predef("OpenBSD"); break; } + case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } + case OS.Solaris: { predef("Solaris"); break; } + case OS.Windows: + { + predef("Windows"); + VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); + break; + } + case OS.OSX: + { + predef("OSX"); + // For legacy compatibility + predef("darwin"); + break; + } + case OS.FreeBSD: + { + predef("FreeBSD"); + switch (tgt.osMajor) + { + case 10: predef("FreeBSD_10"); break; + case 11: predef("FreeBSD_11"); break; + case 12: predef("FreeBSD_12"); break; + case 13: predef("FreeBSD_13"); break; + default: predef("FreeBSD_11"); break; + } + break; + } + default: assert(0); + } + } + + addCRuntimePredefinedGlobalIdent(tgt.c); + addCppRuntimePredefinedGlobalIdent(tgt.cpp); + + if (tgt.is64bit) + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition.addPredefinedGlobalIdent("X86_64"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition.addPredefinedGlobalIdent("X86"); + } + if (tgt.isLP64) + VersionCondition.addPredefinedGlobalIdent("D_LP64"); + else if (tgt.is64bit) + VersionCondition.addPredefinedGlobalIdent("X32"); +} + +private +void addCRuntimePredefinedGlobalIdent(const ref TargetC c) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetC.Runtime) switch (c.runtime) + { + default: + case Unspecified: return; + case Bionic: return predef("CRuntime_Bionic"); + case DigitalMars: return predef("CRuntime_DigitalMars"); + case Glibc: return predef("CRuntime_Glibc"); + case Microsoft: return predef("CRuntime_Microsoft"); + case Musl: return predef("CRuntime_Musl"); + case Newlib: return predef("CRuntime_Newlib"); + case UClibc: return predef("CRuntime_UClibc"); + case WASI: return predef("CRuntime_WASI"); + } +} + +private +void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetCPP.Runtime) switch (cpp.runtime) + { + default: + case Unspecified: return; + case Clang: return predef("CppRuntime_Clang"); + case DigitalMars: return predef("CppRuntime_DigitalMars"); + case Gcc: return predef("CppRuntime_Gcc"); + case Microsoft: return predef("CppRuntime_Microsoft"); + case Sun: return predef("CppRuntime_Sun"); + } +} + //////////////////////////////////////////////////////////////////////////////// /** * Describes a back-end target. At present it is incomplete, but in the future diff --git a/tests/dmd/unit/deinitialization.d b/tests/dmd/unit/deinitialization.d index b4cb73a945f..d3458802af9 100644 --- a/tests/dmd/unit/deinitialization.d +++ b/tests/dmd/unit/deinitialization.d @@ -33,7 +33,7 @@ unittest @("Type.deinitialize") unittest { - import dmd.mars : addDefaultVersionIdentifiers; + import dmd.target : addDefaultVersionIdentifiers; import dmd.mtype : Type; import dmd.globals : global; From 02374696fbb960b0c63cc711c6ad539b88567245 Mon Sep 17 00:00:00 2001 From: Dennis Date: Tue, 4 Apr 2023 12:08:45 +0200 Subject: [PATCH 094/301] Remove redundant `opEquals` with `alias this` checks (dlang/dmd!15071) --- dmd/opover.d | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/dmd/opover.d b/dmd/opover.d index 8306004371f..d7b90d7635f 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -1023,12 +1023,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * also compare the parent class's equality. Otherwise, compares * the identity of parent context through void*. */ - if (e.att1 && t1.equivalent(e.att1)) return null; - if (e.att2 && t2.equivalent(e.att2)) return null; - e = e.copy().isEqualExp(); - if (!e.att1) e.att1 = t1; - if (!e.att2) e.att2 = t2; e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); @@ -1036,18 +1031,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) sc2.flags |= SCOPE.noaccesscheck; Expression r = e.expressionSemantic(sc2); sc2.pop(); - - /* https://issues.dlang.org/show_bug.cgi?id=15292 - * if the rewrite result is same with the original, - * the equality is unresolvable because it has recursive definition. - */ - if (r.op == e.op && - r.isEqualExp().e1.type.toBasetype() == t1) - { - e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition", - t1.toChars()); - return ErrorExp.get(); - } return r; } @@ -1078,8 +1061,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) auto ex1 = (*tup1.exps)[i]; auto ex2 = (*tup2.exps)[i]; auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - eeq.att1 = e.att1; - eeq.att2 = e.att2; if (!result) result = eeq; From 28b6852c2ce55f29a15a49faba276599aff67645 Mon Sep 17 00:00:00 2001 From: Dennis Date: Tue, 4 Apr 2023 12:13:44 +0200 Subject: [PATCH 095/301] Turn semicolon as empty statement into an error (dlang/dmd!15072) --- dmd/cparse.d | 12 ++++++------ dmd/parse.d | 20 ++++++++------------ dmd/statementsem.d | 2 +- tests/dmd/fail_compilation/fail4559.d | 8 ++++---- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index cc78317ddc6..dc21af6e102 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -199,7 +199,7 @@ final class CParser(AST) : Parser!AST else if (token.value == TOK.leftCurly) s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); else - s = cparseStatement(ParseStatementFlags.semiOk); + s = cparseStatement(0); s = new AST.LabelStatement(loc, ident, s); break; } @@ -373,7 +373,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -514,7 +514,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = cparseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -529,7 +529,7 @@ final class CParser(AST) : Parser!AST } else { - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.CaseStatement(loc, exp, s); @@ -546,12 +546,12 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; diff --git a/dmd/parse.d b/dmd/parse.d index da257601a89..3055e25880c 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -5198,7 +5198,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.leftCurly: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - f.fbody = parseStatement(ParseStatementFlags.semi); + f.fbody = parseStatement(0); f.endloc = endloc; break; @@ -6034,7 +6034,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -6067,10 +6067,7 @@ LagainStc: case TOK.semicolon: if (!(flags & ParseStatementFlags.semiOk)) { - if (flags & ParseStatementFlags.semi) - deprecation("use `{ }` for an empty statement, not `;`"); - else - error("use `{ }` for an empty statement, not `;`"); + error("use `{ }` for an empty statement, not `;`"); } nextToken(); s = new AST.ExpStatement(loc, cast(AST.Expression)null); @@ -6287,7 +6284,7 @@ LagainStc: _body = null; } else - _body = parseStatement(ParseStatementFlags.semi); + _body = parseStatement(0); s = new AST.PragmaStatement(loc, ident, args, _body); break; } @@ -6340,7 +6337,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = parseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -6355,7 +6352,7 @@ LagainStc: } else { - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); @@ -6384,12 +6381,12 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; @@ -9686,7 +9683,6 @@ immutable PREC[EXP.max + 1] precedence = enum ParseStatementFlags : int { - semi = 1, // empty ';' statements are allowed, but deprecated scope_ = 2, // start a new scope curly = 4, // { } statement is required curlyScope = 8, // { } starts a new scope diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 2004d8f8968..9c56ce6cdff 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -4788,7 +4788,7 @@ private Statements* flatten(Statement statement, Scope* sc) auto a = new Statements(); while (p.token.value != TOK.endOfFile) { - Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + Statement s = p.parseStatement(ParseStatementFlags.curlyScope); if (!s || global.errors != errors) return errorStatements(); a.push(s); diff --git a/tests/dmd/fail_compilation/fail4559.d b/tests/dmd/fail_compilation/fail4559.d index 0101ae98bf1..657c184d787 100644 --- a/tests/dmd/fail_compilation/fail4559.d +++ b/tests/dmd/fail_compilation/fail4559.d @@ -1,10 +1,10 @@ /* -REQUIRED_ARGS: -o- -de +REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/fail4559.d(13): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(19): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(21): Deprecation: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(13): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(19): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(21): Error: use `{ }` for an empty statement, not `;` --- */ From 1337bb497a0db8828804a6927d30bd7a05b9538e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 4 Apr 2023 03:15:58 -0700 Subject: [PATCH 096/301] fix Issue 23014 - importC: static thread-locals do not work (dlang/dmd!15069) --- dmd/cparse.d | 5 +++++ dmd/frontend.h | 7 ++++--- dmd/tokens.d | 5 ++++- dmd/tokens.h | 1 + tests/dmd/runnable/imports/imp23014.i | 1 + tests/dmd/runnable/test23014.i | 7 +++++++ tests/dmd/unit/lexer/location_offset.d | 1 + 7 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/runnable/imports/imp23014.i create mode 100644 tests/dmd/runnable/test23014.i diff --git a/dmd/cparse.d b/dmd/cparse.d index dc21af6e102..4fd923438c2 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -304,6 +304,7 @@ final class CParser(AST) : Parser!AST case TOK.extern_: case TOK.static_: case TOK._Thread_local: + case TOK.__thread: case TOK.auto_: case TOK.register: @@ -2254,6 +2255,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: scwx = SCW.xtypedef; break; case TOK.inline: scwx = SCW.xinline; break; case TOK._Noreturn: scwx = SCW.x_Noreturn; break; + case TOK.__thread: case TOK._Thread_local: scwx = SCW.x_Thread_local; break; // Type qualifiers @@ -4183,6 +4185,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: case TOK.extern_: case TOK.static_: + case TOK.__thread: case TOK._Thread_local: case TOK.auto_: case TOK.register: @@ -4826,6 +4829,8 @@ final class CParser(AST) : Parser!AST { if (specifier.scw & SCW.xextern) stc = AST.STC.extern_; + else if (specifier.scw & SCW.xstatic) + stc = AST.STC.static_; } else if (level == LVL.local) { diff --git a/dmd/frontend.h b/dmd/frontend.h index 8730ab91f22..18e006856bd 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -1810,9 +1810,10 @@ enum class TOK : uint8_t __cdecl_ = 217u, __declspec_ = 218u, __stdcall_ = 219u, - __pragma_ = 220u, - __int128_ = 221u, - __attribute___ = 222u, + __thread_ = 220u, + __pragma_ = 221u, + __int128_ = 222u, + __attribute___ = 223u, }; enum class MemorySet diff --git a/dmd/tokens.d b/dmd/tokens.d index 0a698c5b30e..352c89ef164 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -274,6 +274,7 @@ enum TOK : ubyte __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, @@ -579,6 +580,7 @@ private immutable TOK[] keywords = TOK.__cdecl, TOK.__declspec, TOK.__stdcall, + TOK.__thread, TOK.__pragma, TOK.__int128, TOK.__attribute__, @@ -610,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords = union_, unsigned, void_, volatile, while_, asm_, typeof_, _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local, - _import, __cdecl, __declspec, __stdcall, __pragma, __int128, __attribute__, + _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__, _assert ]; foreach (kw; Ckwds) @@ -882,6 +884,7 @@ extern (C++) struct Token TOK.__cdecl : "__cdecl", TOK.__declspec : "__declspec", TOK.__stdcall : "__stdcall", + TOK.__thread : "__thread", TOK.__pragma : "__pragma", TOK.__int128 : "__int128", TOK.__attribute__ : "__attribute__", diff --git a/dmd/tokens.h b/dmd/tokens.h index 404c6330675..6c1b9792dbb 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -283,6 +283,7 @@ enum class TOK : unsigned char cdecl_, declspec, stdcall, + thread, pragma, int128_, attribute__, diff --git a/tests/dmd/runnable/imports/imp23014.i b/tests/dmd/runnable/imports/imp23014.i new file mode 100644 index 00000000000..ea51a781765 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23014.i @@ -0,0 +1 @@ +static _Thread_local int tmp; diff --git a/tests/dmd/runnable/test23014.i b/tests/dmd/runnable/test23014.i new file mode 100644 index 00000000000..12716caf29b --- /dev/null +++ b/tests/dmd/runnable/test23014.i @@ -0,0 +1,7 @@ +/* EXTRA_SOURCES: imports/imp23014.i + * DISABLED: win32mscoff win64 + */ + +static _Thread_local int tmp; + +int main() { return tmp; } diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index 50c2ad8e2f6..873d9812382 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -542,6 +542,7 @@ enum ignoreTokens __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, From 404afa89f362be1051d874085059823a82ff2bae Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 4 Apr 2023 03:17:09 -0700 Subject: [PATCH 097/301] fix Issue 22559 - ImportC: support gnu case ranges (dlang/dmd!15068) --- dmd/cparse.d | 13 ++++++++++++- tests/dmd/compilable/test22559.i | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/compilable/test22559.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 4fd923438c2..ba5bbf44007 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -508,6 +508,14 @@ final class CParser(AST) : Parser!AST nextToken(); auto exp = cparseAssignExp(); + AST.Expression expHigh; + if (token.value == TOK.dotDotDot) + { + /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + */ + nextToken(); + expHigh = cparseAssignExp(); + } check(TOK.colon); if (flags & ParseStatementFlags.curlyScope) @@ -533,7 +541,10 @@ final class CParser(AST) : Parser!AST s = cparseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); - s = new AST.CaseStatement(loc, exp, s); + if (expHigh) + s = new AST.CaseRangeStatement(loc, exp, expHigh, s); + else + s = new AST.CaseStatement(loc, exp, s); break; } diff --git a/tests/dmd/compilable/test22559.i b/tests/dmd/compilable/test22559.i new file mode 100644 index 00000000000..d8e5cc8847e --- /dev/null +++ b/tests/dmd/compilable/test22559.i @@ -0,0 +1,17 @@ +// https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + +int alpha(char c) +{ + switch (c) + { + case 'A' ... 'Z': return 1; + case 'a' ... 'z': return 1; + default: return 0; + } +} + +_Static_assert(alpha('A') == 1, "1"); +_Static_assert(alpha('B') == 1, "2"); +_Static_assert(alpha('z') == 1, "3"); +_Static_assert(alpha('z' + 1) == 0, "3"); +_Static_assert(alpha('0') == 0, "4"); From e0eac03cc7db4bd7baebf3067cb3b34c978b806a Mon Sep 17 00:00:00 2001 From: FeepingCreature <540727+FeepingCreature@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:35:25 +0200 Subject: [PATCH 098/301] Fix issue 23822: Check for deprecation before resolving alias on struct type member access. (dlang/dmd!15077) --- dmd/typesem.d | 3 +++ tests/dmd/fail_compilation/fail23822.d | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/dmd/fail_compilation/fail23822.d diff --git a/dmd/typesem.d b/dmd/typesem.d index 5617bbe6f5d..c923c6d7cfa 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -3758,6 +3758,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) { return noMember(mt, sc, e, ident, flag); } + // check before alias resolution; the alias itself might be deprecated! + if (s.isAliasDeclaration) + e.checkDeprecated(sc, s); s = s.toAlias(); if (auto em = s.isEnumMember()) diff --git a/tests/dmd/fail_compilation/fail23822.d b/tests/dmd/fail_compilation/fail23822.d new file mode 100644 index 00000000000..5cdd1fe0503 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23822.d @@ -0,0 +1,22 @@ +// https://issues.dlang.org/show_bug.cgi?id=23822 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23822.d(21): Deprecation: alias `fail23822.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +struct S +{ + deprecated alias value = Alias!5; +} + +void main() +{ + auto a = S.value; +} From 896ea1d8018fd77ce71b2dfb0c895099df5d6dd0 Mon Sep 17 00:00:00 2001 From: Dennis Date: Thu, 6 Apr 2023 08:53:32 +0200 Subject: [PATCH 099/301] Remove unsafe Expression casts in dtemplate.d (dlang/dmd!15080) --- dmd/dtemplate.d | 117 +++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index ad3a6d4dd54..011fb3d7a7b 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -1858,19 +1858,16 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol Type taai; if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) { - if (farg.op == EXP.string_) + if (StringExp se = farg.isStringExp()) { - StringExp se = cast(StringExp)farg; argtype = se.type.nextOf().sarrayOf(se.len); } - else if (farg.op == EXP.arrayLiteral) + else if (ArrayLiteralExp ae = farg.isArrayLiteralExp()) { - ArrayLiteralExp ae = cast(ArrayLiteralExp)farg; argtype = ae.type.nextOf().sarrayOf(ae.elements.length); } - else if (farg.op == EXP.slice) + else if (SliceExp se = farg.isSliceExp()) { - SliceExp se = cast(SliceExp)farg; if (Type tsa = toStaticArrayType(se)) argtype = tsa; } @@ -2283,18 +2280,23 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol Declaration d; VarDeclaration v = null; - if (ea && ea.op == EXP.type) - ta = ea.type; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; - else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.function_) + if (ea) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; - else - sa = (cast(FuncExp)ea).fd; + if (ea.op == EXP.type) + ta = ea.type; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + else if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto fe = ea.isFuncExp()) + { + if (fe.td) + sa = fe.td; + else + sa = fe.fd; + } } if (ta) @@ -3856,9 +3858,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param if (tparam.ty == Tsarray) { TypeSArray tsa = cast(TypeSArray)tparam; - if (tsa.dim.op == EXP.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter) + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - Identifier id = (cast(VarExp)tsa.dim).var.ident; + Identifier id = tsa.dim.isVarExp().var.ident; i = templateIdentifierLookup(id, parameters); assert(i != IDX_NOTFOUND); tp = (*parameters)[i]; @@ -4250,12 +4252,12 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param /* If it is one of the template parameters for this template, * we should not attempt to interpret it. It already has a value. */ - if (e2.op == EXP.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter)) + if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter)) { /* * (T:Number!(e2), int e2) */ - j = templateIdentifierLookup((cast(VarExp)e2).var.ident, parameters); + j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters); if (j != IDX_NOTFOUND) goto L1; // The template parameter was not from this template @@ -5236,7 +5238,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(DotTemplateInstanceExp e) { //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.ti.tiargs) { foreach (oa; *e.ti.tiargs) @@ -5254,7 +5256,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CallExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5269,7 +5271,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CastExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); // e.to can be null for cast() with no type if (!result && e.to) result = e.to.reliesOnTemplateParameters(tparams); @@ -5278,7 +5280,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(SliceExp e) { //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.lwr) e.lwr.accept(this); if (!result && e.upr) @@ -5296,7 +5298,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(ArrayExp e) { //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5317,7 +5319,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); e.econd.accept(this); if (!result) - visit(cast(BinExp)e); + visit(e.isBinExp()); } } @@ -6727,7 +6729,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol ea = ea.expressionSemantic(sc); // must not interpret the args, excepting template parameters - if (ea.op != EXP.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter)) + if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter)) { ea = ea.optimize(WANTvalue); } @@ -6738,13 +6740,13 @@ extern (C++) class TemplateInstance : ScopeDsymbol ea = ea.expressionSemantic(sc); sc = sc.endCTFE(); - if (ea.op == EXP.variable) + if (auto varExp = ea.isVarExp()) { /* If the parameter is a function that is not called * explicitly, i.e. `foo!func` as opposed to `foo!func()`, * then it is a dsymbol, not the return value of `func()` */ - Declaration vd = (cast(VarExp)ea).var; + Declaration vd = varExp.var; if (auto fd = vd.isFuncDeclaration()) { sa = fd; @@ -6767,10 +6769,9 @@ extern (C++) class TemplateInstance : ScopeDsymbol } } //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); - if (ea.op == EXP.tuple) + if (TupleExp te = ea.isTupleExp()) { // Expand tuple - TupleExp te = cast(TupleExp)ea; size_t dim = te.exps.length; tiargs.remove(j); if (dim) @@ -6796,12 +6797,11 @@ extern (C++) class TemplateInstance : ScopeDsymbol } if (ea.op == EXP.scope_) { - sa = (cast(ScopeExp)ea).sds; + sa = ea.isScopeExp().sds; goto Ldsym; } - if (ea.op == EXP.function_) + if (FuncExp fe = ea.isFuncExp()) { - FuncExp fe = cast(FuncExp)ea; /* A function literal, that is passed to template and * already semanticed as function pointer, never requires * outer frame. So convert it to global function is valid. @@ -6823,23 +6823,23 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (ea.op == EXP.dotVariable && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotVarExp)ea).var; + sa = ea.isDotVarExp().var; goto Ldsym; } - if (ea.op == EXP.template_) + if (auto te = ea.isTemplateExp()) { - sa = (cast(TemplateExp)ea).td; + sa = te.td; goto Ldsym; } if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotTemplateExp)ea).td; + sa = ea.isDotTemplateExp().td; goto Ldsym; } - if (ea.op == EXP.dot) + if (auto de = ea.isDotExp()) { - if (auto se = (cast(DotExp)ea).e2.isScopeExp()) + if (auto se = de.e2.isScopeExp()) { sa = se.sds; goto Ldsym; @@ -7340,22 +7340,22 @@ extern (C++) class TemplateInstance : ScopeDsymbol Tuple va = isTuple(o); if (ea) { - if (ea.op == EXP.variable) + if (auto ve = ea.isVarExp()) { - sa = (cast(VarExp)ea).var; + sa = ve.var; goto Lsa; } - if (ea.op == EXP.this_) + if (auto te = ea.isThisExp()) { - sa = (cast(ThisExp)ea).var; + sa = te.var; goto Lsa; } - if (ea.op == EXP.function_) + if (auto fe = ea.isFuncExp()) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; + if (fe.td) + sa = fe.td; else - sa = (cast(FuncExp)ea).fd; + sa = fe.fd; goto Lsa; } // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent. @@ -7727,13 +7727,13 @@ bool definitelyValueParameter(Expression e) */ // x.y.f cannot be a value - FuncDeclaration f = (cast(DotVarExp)e).var.isFuncDeclaration(); + FuncDeclaration f = e.isDotVarExp().var.isFuncDeclaration(); if (f) return false; while (e.op == EXP.dotVariable) { - e = (cast(DotVarExp)e).e1; + e = e.isDotVarExp().e1; } // this.x.y and super.x.y couldn't possibly be valid values. if (e.op == EXP.this_ || e.op == EXP.super_) @@ -7747,7 +7747,7 @@ bool definitelyValueParameter(Expression e) if (e.op != EXP.variable) return true; - VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration v = e.isVarExp().var.isVarDeclaration(); // func.x.y is not an alias if (!v) return true; @@ -8242,10 +8242,15 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ Type ta = isType(oarg); RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg); Expression ea = isExpression(oarg); - if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; + if (ea) + { + if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + } if (sa) { if ((cast(Dsymbol)sa).isAggregateDeclaration()) From 2a98a8ecf8ebf0a68a920b1b6dce3d3256921f70 Mon Sep 17 00:00:00 2001 From: Mathias LANG Date: Thu, 6 Apr 2023 09:41:30 +0200 Subject: [PATCH 100/301] expressionsem: Remove support for RTL evaluation order (dlang/dmd!15082) It isn't needed since PR 7032 was merged, in August 2017. --- dmd/expressionsem.d | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index ed01c361c97..9880487a00c 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2348,30 +2348,18 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } /* Remaining problems: - * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is - * implemented by calling a function) we'll defer this for now. - * 2. value structs (or static arrays of them) that need to be copy constructed - * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the + * 1. value structs (or static arrays of them) that need to be copy constructed + * 2. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the * function gets called. - * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. - * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned + * 3. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. + * Those are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned * up properly. Pushing arguments on the stack then cannot fail. */ { - /* TODO: tackle problem 1) - */ - const bool leftToRight = true; // TODO: Any cases that need rightToLeft? - if (!leftToRight) - assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity - - /* Does Problem (4) apply? + /* Does Problem (3) apply? */ const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf); - const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1); - const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1); - const ptrdiff_t step = (leftToRight ? 1 : -1); - /* Compute indices of last throwing argument and first arg needing destruction. * Used to not set up destructors unless an arg needs destruction on a throw * in a later argument. @@ -2379,7 +2367,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, ptrdiff_t lastthrow = -1; // last argument that may throw ptrdiff_t firstdtor = -1; // first argument that needs destruction ptrdiff_t lastdtor = -1; // last argument that needs destruction - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; if (canThrow(arg, sc.func, false)) @@ -2396,12 +2384,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } } - /* Do we need 'eprefix' for problems 3 or 4? + /* Do we need 'eprefix' for problems 2 or 3? */ const bool needsPrefix = callerDestroysArgs ? firstdtor >= 0 // true if any argument needs destruction : firstdtor >= 0 && lastthrow >= 0 && - (lastthrow - firstdtor) * step > 0; // last throw after first destruction + (lastthrow - firstdtor) > 0; // last throw after first destruction const ptrdiff_t lastPrefix = callerDestroysArgs ? lastdtor // up to last argument requiring destruction : lastthrow; // up to last potentially throwing argument @@ -2421,7 +2409,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, eprefix = ae.expressionSemantic(sc); } - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; //printf("arg[%d]: %s\n", cast(int)i, arg.toChars()); @@ -2437,12 +2425,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Do we have 'eprefix' and aren't past 'lastPrefix' yet? * Then declare a temporary variable for this arg and append that declaration - * to 'eprefix', which will implicitly take care of potential problem 2) for + * to 'eprefix', which will implicitly take care of potential problem 1) for * this arg. * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix', * excluding all lazy parameters. */ - if (needsPrefix && (lastPrefix - i) * step >= 0) + if (needsPrefix && (lastPrefix - i) >= 0) { const bool needsDtor = !isRef && arg.type.needsDestruction() && // Problem 3: last throwing arg doesn't require dtor patching @@ -2465,7 +2453,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else { - /* Problem 3: Modify the destructor so it only runs if gate==false, + /* Problem 2: Modify the destructor so it only runs if gate==false, * i.e., only if there was a throw while constructing the args */ if (!needsDtor) @@ -2500,7 +2488,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, arg = arg.expressionSemantic(sc); } - /* Problem 3: Last throwing arg? + /* Problem 2: Last throwing arg? * Then finalize eprefix => (eprefix, gate = true), i.e., disable the * dtors right after constructing the last throwing arg. * From now on, the callee will take care of destructing the args because @@ -2514,7 +2502,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else // not part of 'eprefix' { - /* Handle problem 2) by calling the copy constructor for value structs + /* Handle problem 1) by calling the copy constructor for value structs * (or static arrays of them) if appropriate. */ Type tv = arg.type.baseElemOf(); From 0b74c54d83229eae57f266eec52fe97937ddf8b3 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Thu, 6 Apr 2023 10:34:09 +0200 Subject: [PATCH 101/301] CI: Disable tests so Azure OMF targets pass --- tests/dmd/fail_compilation/bug9631.d | 2 +- tests/dmd/fail_compilation/fail19948.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/dmd/fail_compilation/bug9631.d b/tests/dmd/fail_compilation/bug9631.d index f456454fe87..c980d76a73d 100644 --- a/tests/dmd/fail_compilation/bug9631.d +++ b/tests/dmd/fail_compilation/bug9631.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/bug9631.d(20): Error: cannot implicitly convert expression `F()` of type `bug9631.T1!().F` to `bug9631.T2!().F` --- */ - +// DISABLED: win32 template T1() { struct F { } diff --git a/tests/dmd/fail_compilation/fail19948.d b/tests/dmd/fail_compilation/fail19948.d index 6122e418339..09453ed2e02 100644 --- a/tests/dmd/fail_compilation/fail19948.d +++ b/tests/dmd/fail_compilation/fail19948.d @@ -1,5 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=19948 - +// DISABLED: win32 /* TEST_OUTPUT: --- From bd1440ecd9601297bad905258ca13491eb016471 Mon Sep 17 00:00:00 2001 From: Mathias LANG Date: Thu, 6 Apr 2023 16:00:34 +0200 Subject: [PATCH 102/301] expressionsem: Remove redundant dependency to dmd.root.file (dlang/dmd!15083) FileManager.lookup will already attempt to read the file if it is not cached, and will insert it in the list of files if it manages to read it, so only the 'if (!readResult.success)' branch was reachable. This in turn allows us to remove expressionsem's dependency to dmd.root.file. --- dmd/expressionsem.d | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 9880487a00c..c0a050beaa9 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -65,7 +65,6 @@ import dmd.parse; import dmd.printast; import dmd.root.array; import dmd.root.ctfloat; -import dmd.root.file; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rootobject; @@ -6318,19 +6317,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - auto readResult = File.read(resolvedNamez); - if (!readResult.success) - { - e.error("cannot read file `%s`", resolvedNamez.ptr); - return setError(); - } - else - { - // take ownership of buffer (probably leaking) - auto data = readResult.extractSlice(); - se = new StringExp(e.loc, data); - global.fileManager.add(fileName, data); - } + e.error("cannot read file `%s`", resolvedNamez.ptr); + return setError(); } } result = se.expressionSemantic(sc); From c434007af5ef3bac3ae1b4fba31103a310e64dda Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Fri, 7 Apr 2023 04:50:13 -0400 Subject: [PATCH 103/301] Fix issue 11989 -- deprecate TickDuration, it's no longer used anywhere. (dlang/dmd!15024) --- runtime/druntime/src/core/time.d | 223 +++++++++++++++++++------------ tests/dmd/runnable/testpdb.d | 8 +- 2 files changed, 142 insertions(+), 89 deletions(-) diff --git a/runtime/druntime/src/core/time.d b/runtime/druntime/src/core/time.d index 8d508755c7d..be941e2abcd 100644 --- a/runtime/druntime/src/core/time.d +++ b/runtime/druntime/src/core/time.d @@ -18,7 +18,7 @@ $(LEADINGROW Types) $(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).)) - $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in + $(TR $(TDNW $(LREF TickDuration)) $(TD $(RED DEPRECATED) Represents a duration of time in system clock ticks, using the highest precision that the system provides.)) $(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in system clock ticks, using the highest precision that the system provides.)) @@ -682,21 +682,21 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - Duration opBinary(string op, D)(D rhs) const nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + Duration opBinary(string op)(const Duration rhs) const nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); - else - return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); + return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); + } + + deprecated Duration opBinary(string op)(const TickDuration rhs) const nothrow @nogc + if (op == "+" || op == "-") + { + return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); } version (CoreUnittest) unittest @@ -733,7 +733,13 @@ public: assert((cast(D)Duration(-7)) - (cast(E)Duration(-5)) == Duration(-2)); assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2)); } + } + } + version (CoreUnittest) deprecated unittest + { + foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) + { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -761,6 +767,8 @@ public: /++ + $(RED TickDuration is Deprecated) + Adds or subtracts two durations. The legal types of arithmetic for $(D Duration) using this operator are @@ -774,14 +782,14 @@ public: lhs = The $(D TickDuration) to add to this $(D Duration) or to subtract this $(D Duration) from. +/ - Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc + deprecated Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc if ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)) { return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs")); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -821,21 +829,22 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + ref Duration opOpAssign(string op)(const Duration rhs) nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); - else - mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); + mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); + return this; + } + + deprecated ref Duration opOpAssign(string op)(const TickDuration rhs) nothrow @nogc + if (op == "+" || op == "-") + { + mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); return this; } @@ -850,13 +859,6 @@ public: throw new AssertError("op assign failed", __FILE__, line); } - static void test2(string op, E) - (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) - { - assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); - assertApprox(actual, lower, upper, "op assign failed", line); - } - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) { test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12)); @@ -888,6 +890,26 @@ public: test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2)); } + foreach (D; AliasSeq!(const Duration, immutable Duration)) + { + foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) + { + D lhs = D(120); + E rhs = E(120); + static assert(!__traits(compiles, lhs += rhs), D.stringof ~ " " ~ E.stringof); + } + } + } + + version (CoreUnittest) deprecated unittest + { + static void test2(string op, E) + (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) + { + assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); + assertApprox(actual, lower, upper, "op assign failed", line); + } + foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -913,8 +935,7 @@ public: foreach (D; AliasSeq!(const Duration, immutable Duration)) { - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration, - TickDuration, const TickDuration, immutable TickDuration)) + foreach (E; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { D lhs = D(120); E rhs = E(120); @@ -1170,19 +1191,21 @@ public: /++ + $(RED TickDuration is Deprecated) + Returns a $(LREF TickDuration) with the same number of hnsecs as this $(D Duration). Note that the conventional way to convert between $(D Duration) and $(D TickDuration) is using $(REF to, std,conv), e.g.: $(D duration.to!TickDuration()) +/ - TickDuration opCast(T)() const nothrow @nogc + deprecated TickDuration opCast(T)() const nothrow @nogc if (is(immutable T == immutable TickDuration)) { return TickDuration.from!"hnsecs"(_hnsecs); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -1762,6 +1785,8 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest } /++ + $(RED TickDuration is DEPRECATED) + Converts a $(D TickDuration) to the given units as either an integral value or a floating point value. @@ -1773,6 +1798,7 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest td = The TickDuration to convert +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") T to(string units, T, D)(D td) @safe pure nothrow @nogc if (is(immutable D == immutable TickDuration) && (units == "seconds" || @@ -1804,7 +1830,7 @@ T to(string units, T, D)(D td) @safe pure nothrow @nogc } /// -unittest +deprecated unittest { auto t = TickDuration.from!"seconds"(1000); @@ -1816,7 +1842,7 @@ unittest assert(fabs(td - 1000) < 0.001); } -unittest +deprecated unittest { void testFun(string U)() { auto t1v = 1000; @@ -2756,22 +2782,24 @@ unittest /++ - $(RED Warning: TickDuration will be deprecated in the near future (once all - uses of it in Phobos have been deprecated). Please use + $(RED Warning: TickDuration is deprecated. Please use $(LREF MonoTime) for the cases where a monotonic timestamp is needed and $(LREF Duration) when a duration is needed, rather than using - TickDuration. It has been decided that TickDuration is too confusing - (e.g. it conflates a monotonic timestamp and a duration in monotonic - clock ticks) and that having multiple duration types is too awkward - and confusing.) + TickDuration.) Represents a duration of time in system clock ticks. The system clock ticks are the ticks of the system clock at the highest precision that the system provides. +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") struct TickDuration { +deprecated: + private static TickDuration TDRvalueOf(TickDuration td) + { + return td; + } /++ The number of ticks that the system clock has in one second. @@ -2811,14 +2839,14 @@ struct TickDuration version (CoreUnittest) unittest { - assert(zero == TickDuration(0)); - assert(TickDuration.max == TickDuration(long.max)); - assert(TickDuration.min == TickDuration(long.min)); - assert(TickDuration.min < TickDuration.zero); - assert(TickDuration.zero < TickDuration.max); - assert(TickDuration.min < TickDuration.max); - assert(TickDuration.min - TickDuration(1) == TickDuration.max); - assert(TickDuration.max + TickDuration(1) == TickDuration.min); + assert((zero == TickDuration(0)) == true); + assert((TickDuration.max == TickDuration(long.max)) == true); + assert((TickDuration.min == TickDuration(long.min)) == true); + assert((TickDuration.min < TickDuration.zero) == true); + assert((TickDuration.zero < TickDuration.max) == true); + assert((TickDuration.min < TickDuration.max) == true); + assert((TickDuration.min - TickDuration(1) == TickDuration.max) == true); + assert((TickDuration.max + TickDuration(1) == TickDuration.min) == true); } @@ -3040,12 +3068,12 @@ struct TickDuration { auto a = TickDuration.currSystemTick; auto result = a += cast(T)TickDuration.currSystemTick; - assert(a == result); + assert((a == result) == true); assert(a.to!("seconds", real)() >= 0); auto b = TickDuration.currSystemTick; result = b -= cast(T)TickDuration.currSystemTick; - assert(b == result); + assert((b == result) == true); assert(b.to!("seconds", real)() <= 0); foreach (U; AliasSeq!(const TickDuration, immutable TickDuration)) @@ -3104,11 +3132,11 @@ struct TickDuration { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { - assert(-(cast(T)TickDuration(7)) == TickDuration(-7)); - assert(-(cast(T)TickDuration(5)) == TickDuration(-5)); - assert(-(cast(T)TickDuration(-7)) == TickDuration(7)); - assert(-(cast(T)TickDuration(-5)) == TickDuration(5)); - assert(-(cast(T)TickDuration(0)) == TickDuration(0)); + assert((-(cast(T)TickDuration(7)) == TickDuration(-7)) == true); + assert((-(cast(T)TickDuration(5)) == TickDuration(-5)) == true); + assert((-(cast(T)TickDuration(-7)) == TickDuration(7)) == true); + assert((-(cast(T)TickDuration(-5)) == TickDuration(5)) == true); + assert((-(cast(T)TickDuration(0)) == TickDuration(0)) == true); } } @@ -3130,9 +3158,9 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t; - assert(t == u); - assert(rvalueOf(t) == u); - assert(t == rvalueOf(u)); + assert((t == u) == true); + assert((TDRvalueOf(t) == u) == true); + assert((t == TDRvalueOf(u)) == true); } } @@ -3142,20 +3170,20 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t + t; - assert(t < u); - assert(t <= t); - assert(u > t); - assert(u >= u); - - assert(rvalueOf(t) < u); - assert(rvalueOf(t) <= t); - assert(rvalueOf(u) > t); - assert(rvalueOf(u) >= u); - - assert(t < rvalueOf(u)); - assert(t <= rvalueOf(t)); - assert(u > rvalueOf(t)); - assert(u >= rvalueOf(u)); + assert((t < u) == true); + assert((t <= t) == true); + assert((u > t) == true); + assert((u >= u) == true); + + assert((TDRvalueOf(t) < u) == true); + assert((TDRvalueOf(t) <= t) == true); + assert((TDRvalueOf(u) > t) == true); + assert((TDRvalueOf(u) >= u) == true); + + assert((t < TDRvalueOf(u)) == true); + assert((t <= TDRvalueOf(t)) == true); + assert((u > TDRvalueOf(t)) == true); + assert((u >= TDRvalueOf(u)) == true); } } } @@ -3186,7 +3214,7 @@ struct TickDuration TickDuration t1 = curr; immutable t2 = curr + curr; t1 *= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t1 = curr; t1 *= 2.0; @@ -3195,7 +3223,7 @@ struct TickDuration t1 = curr; t1 *= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); foreach (T; AliasSeq!(const TickDuration, immutable TickDuration)) { @@ -3237,7 +3265,7 @@ struct TickDuration immutable t1 = curr; TickDuration t2 = curr + curr; t2 /= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t2 = curr + curr; t2 /= 2.0; @@ -3246,7 +3274,7 @@ struct TickDuration t2 = curr + curr; t2 /= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); _assertThrown!TimeException(t2 /= 0); @@ -3284,10 +3312,10 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t1 * 2 == t2); + assert((t1 * 2 == t2) == true); immutable tol = TickDuration(cast(long)(_abs(t1.length) * double.epsilon * 2.0)); assertApprox(t1 * 2.0, t2 - tol, t2 + tol); - assert(t1 * 2.1 > t2); + assert((t1 * 2.1 > t2) == true); } } @@ -3323,12 +3351,12 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t2 / 2 == t1); + assert((t2 / 2 == t1) == true); immutable tol = TickDuration(cast(long)(_abs(t2.length) * double.epsilon / 2.0)); assertApprox(t2 / 2.0, t1 - tol, t1 + tol); - assert(t2 / 2.1 < t1); + assert((t2 / 2.1 < t1) == true); - _assertThrown!TimeException(t2 / 0); + _assertThrownDep!TimeException(t2 / 0); } } @@ -3430,7 +3458,6 @@ struct TickDuration } } - /++ Generic way of converting between two time units. Conversions to smaller units use truncating division. Years and months can be converted to each @@ -3641,6 +3668,7 @@ Duration abs(Duration duration) @safe pure nothrow @nogc } /++ Ditto +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") TickDuration abs(TickDuration duration) @safe pure nothrow @nogc { return TickDuration(_abs(duration.length)); @@ -3650,9 +3678,12 @@ unittest { assert(abs(dur!"msecs"(5)) == dur!"msecs"(5)); assert(abs(dur!"msecs"(-5)) == dur!"msecs"(5)); +} - assert(abs(TickDuration(17)) == TickDuration(17)); - assert(abs(TickDuration(-17)) == TickDuration(17)); +deprecated unittest +{ + assert((abs(TickDuration(17)) == TickDuration(17)) == true); + assert((abs(TickDuration(-17)) == TickDuration(17)) == true); } @@ -3987,6 +4018,28 @@ unittest } } +version (CoreUnittest) deprecated void _assertThrownDep(T : Throwable = Exception, E) + (lazy E expression, + string msg = null, + string file = __FILE__, + size_t line = __LINE__) +{ + bool thrown = false; + + try + expression(); + catch (T t) + thrown = true; + + if (!thrown) + { + immutable tail = msg.length == 0 ? "." : ": " ~ msg; + + throw new AssertError("assertThrown() failed: No " ~ T.stringof ~ " was thrown" ~ tail, file, line); + } +} + + version (CoreUnittest) void assertApprox(D, E)(D actual, E lower, @@ -4001,7 +4054,7 @@ version (CoreUnittest) void assertApprox(D, E)(D actual, throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line); } -version (CoreUnittest) void assertApprox(D, E)(D actual, +version (CoreUnittest) deprecated void assertApprox(D, E)(D actual, E lower, E upper, string msg = "unittest failure", diff --git a/tests/dmd/runnable/testpdb.d b/tests/dmd/runnable/testpdb.d index f89f25bd8f8..55207c708e7 100644 --- a/tests/dmd/runnable/testpdb.d +++ b/tests/dmd/runnable/testpdb.d @@ -7,9 +7,9 @@ import core.demangle; void main(string[] args) { // https://issues.dlang.org/show_bug.cgi?id=4014 - // -gf should drag in full definitions of Object, TickDuration and ClockType + // -gf should drag in full definitions of Object, Duration and ClockType Object o = new Object; - TickDuration duration; // struct + Duration duration; // struct ClockType ct; // enumerator version (CRuntime_Microsoft) @@ -29,8 +29,8 @@ void main(string[] args) testSymbolHasChildren(objsym, "object.Object"); objsym.Release(); - IDiaSymbol ticksym = searchSymbol(globals, "core.time.TickDuration"); - testSymbolHasChildren(ticksym, "core.time.TickDuration"); + IDiaSymbol ticksym = searchSymbol(globals, "core.time.Duration"); + testSymbolHasChildren(ticksym, "core.time.Duration"); ticksym.Release(); IDiaSymbol ctsym = searchSymbol(globals, "core.time.ClockType"); From 93613242d10c84e117eed26416ddc1f5afe77dc5 Mon Sep 17 00:00:00 2001 From: FeepingCreature <540727+FeepingCreature@users.noreply.github.com> Date: Fri, 7 Apr 2023 14:50:05 +0200 Subject: [PATCH 104/301] Fix issue 23826: Check for deprecation when passing type member as template argument. (dlang/dmd!15078) --- dmd/typesem.d | 2 ++ tests/dmd/fail_compilation/fail23826.d | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/dmd/fail_compilation/fail23826.d diff --git a/dmd/typesem.d b/dmd/typesem.d index c923c6d7cfa..c941d3391d3 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -290,6 +290,8 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb if (!sm) return helper3(); + if (sm.isAliasDeclaration) + sm.checkDeprecated(loc, sc); s = sm.toAlias(); } diff --git a/tests/dmd/fail_compilation/fail23826.d b/tests/dmd/fail_compilation/fail23826.d new file mode 100644 index 00000000000..3db243a3ce8 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23826.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=23826 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23826.d(23): Deprecation: alias `fail23826.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +class S +{ + deprecated alias value = Alias!5; +} + +enum identity(alias A) = A; + +void main() +{ + auto a = identity!(S.value); +} From ee88f6c9351d7b7e980e1ada8ef52c3a3df1bc67 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Sat, 8 Apr 2023 17:53:10 +0800 Subject: [PATCH 105/301] Fix Issue 18493 - [betterC] Can't use aggregated type with postblit (dlang/dmd!15076) --- dmd/clone.d | 5 +++-- tests/dmd/compilable/test18493.d | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/compilable/test18493.d diff --git a/dmd/clone.d b/dmd/clone.d index fb3929fe913..60e373c502b 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -1261,8 +1261,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // if this field's postblit is not `nothrow`, add a `scope(failure)` // block to destroy any prior successfully postblitted fields should - // this field's postblit fail - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow) + // this field's postblit fail. + // Don't generate it for betterC code since it cannot throw exceptions. + if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && !global.params.betterC) { // create a list of destructors that need to be called Expression[] dtorCalls; diff --git a/tests/dmd/compilable/test18493.d b/tests/dmd/compilable/test18493.d new file mode 100644 index 00000000000..558bf447904 --- /dev/null +++ b/tests/dmd/compilable/test18493.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=18493 +// REQUIRED_ARGS: -betterC + +struct S +{ + this(this) + { + } + + ~this() + { + } +} + +struct C +{ + S s1; + S s2; +} From 674a5e01dc557971bf6f215d61dc33b19868b84a Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Sat, 8 Apr 2023 17:54:13 +0800 Subject: [PATCH 106/301] Fix Issue 23832 - dmd regression 2.103.0 silent error cannot call decode at runtime (dlang/dmd!15088) --- dmd/typinf.d | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/typinf.d b/dmd/typinf.d index 54561c5708d..f1f0ef0355e 100644 --- a/dmd/typinf.d +++ b/dmd/typinf.d @@ -47,6 +47,7 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope { if (!global.params.useTypeInfo) { + global.gag = 0; if (e) .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars()); else From 3ced001a03853058f55c6cef40e501b639c8ff7c Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 8 Apr 2023 10:36:00 -0700 Subject: [PATCH 107/301] parse.d: remove more dependencies on globals.d (dlang/dmd!15091) --- dmd/frontend.h | 15 ++++++++++++--- dmd/globals.h | 3 +++ dmd/lexer.d | 7 +++++-- dmd/mars.d | 4 ++++ dmd/parse.d | 8 ++++---- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 18e006856bd..d2bc45dfe69 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3348,20 +3348,29 @@ struct CompileEnv final _d_dynamicArray< const char > time; _d_dynamicArray< const char > vendor; _d_dynamicArray< const char > timestamp; + bool previewIn; + bool ddocOutput; + bool shortenedMethods; CompileEnv() : versionNumber(), date(), time(), vendor(), - timestamp() + timestamp(), + previewIn(), + ddocOutput(), + shortenedMethods(true) { } - CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}) : + CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool ddocOutput = false, bool shortenedMethods = true) : versionNumber(versionNumber), date(date), time(time), vendor(vendor), - timestamp(timestamp) + timestamp(timestamp), + previewIn(previewIn), + ddocOutput(ddocOutput), + shortenedMethods(shortenedMethods) {} }; diff --git a/dmd/globals.h b/dmd/globals.h index 6b86df76c65..836c0dd9b20 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -259,6 +259,9 @@ struct CompileEnv DString time; DString vendor; DString timestamp; + bool previewIn; + bool ddocOutput; + bool shortenedMethods; }; struct Global diff --git a/dmd/lexer.d b/dmd/lexer.d index 86f0d0cdf9c..6c53ec4a4ec 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -47,6 +47,10 @@ struct CompileEnv const(char)[] time; /// __TIME__ const(char)[] vendor; /// __VENDOR__ const(char)[] timestamp; /// __TIMESTAMP__ + + bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool ddocOutput; /// collect embedded documentation comments + bool shortenedMethods = true; /// allow => in normal function declarations } /*********************************************************** @@ -75,6 +79,7 @@ class Lexer ubyte wchar_tsize; /// size of C wchar_t, 2 or 4 ErrorSink eSink; /// send error messages through this interface + CompileEnv compileEnv; /// environment private { @@ -93,8 +98,6 @@ class Lexer int lastDocLine; // last line of previous doc comment Token* tokenFreelist; - - CompileEnv compileEnv; // environment } nothrow: diff --git a/dmd/mars.d b/dmd/mars.d index 46ae32fd2db..a7ff9c984b5 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -153,6 +153,10 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) if (parseCommandlineAndConfig(argc, argv, params, files)) return EXIT_FAILURE; + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (params.usage) { usage(); diff --git a/dmd/parse.d b/dmd/parse.d index 3055e25880c..0a85c1e775d 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -1237,7 +1237,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { if (added & STC.const_) error("attribute `const` is redundant with previously-applied `in`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { error("attribute `%s` is redundant with previously-applied `in`", (orig & STC.scope_) ? "scope".ptr : "ref".ptr); @@ -1253,7 +1253,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { if (orig & STC.const_) error("attribute `in` cannot be added after `const`: remove `const`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { // Windows `printf` does not support `%1$s` const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr; @@ -2738,7 +2738,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /** Extract unittest body as a string. Must be done eagerly since memory will be released by the lexer before doc gen. */ char* docline = null; - if (global.params.ddoc.doOutput && endPtr > begPtr) + if (compileEnv.ddocOutput && endPtr > begPtr) { /* Remove trailing whitespaces */ for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p) @@ -5186,7 +5186,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.goesTo: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - if (!global.params.shortenedMethods) + if (!compileEnv.shortenedMethods) error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`"); const returnloc = token.loc; nextToken(); From 8c546003502e479098984e14f7ae07a426bbc9ea Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 8 Apr 2023 10:36:23 -0700 Subject: [PATCH 108/301] Parser: rewrite constructor in terms of another (dlang/dmd!15090) --- dmd/parse.d | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/dmd/parse.d b/dmd/parse.d index 0a85c1e775d..1c2effda0f3 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -60,13 +60,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { - super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, - errorSink, - compileEnv); - //printf("Parser::Parser()1 %d\n", doUnittests); + this(_module, input, doDocComment, errorSink, compileEnv, doUnittests); + scanloc = loc; - this.doUnittests = doUnittests; if (!writeMixin(input, scanloc, global.params.mixinOut) && loc.filename) { @@ -78,12 +75,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); scanloc.filename = filename; } - - mod = _module; - linkage = LINK.d; - //nextToken(); // start up the scanner } + /************************************************** + * Main Parser constructor. + */ extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { @@ -92,10 +88,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer compileEnv); //printf("Parser::Parser()2 %d\n", doUnittests); - mod = _module; - linkage = LINK.d; + this.mod = _module; + this.linkage = LINK.d; this.doUnittests = doUnittests; - //nextToken(); // start up the scanner } /++ From 9f8ba4c603e7e7e23396197e4efbe6737978bcde Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 8 Apr 2023 21:28:22 -0700 Subject: [PATCH 109/301] better missing ; error message (dlang/dmd!15055) --- dmd/parse.d | 6 +++++- tests/dmd/fail_compilation/fail196.d | 18 +++++++++--------- tests/dmd/fail_compilation/ice11982.d | 17 ++++++++++------- .../fail_compilation/misc_parser_err_cov1.d | 2 +- tests/dmd/fail_compilation/parseStc.d | 2 +- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/dmd/parse.d b/dmd/parse.d index 1c2effda0f3..ff3201947ed 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -5838,7 +5838,11 @@ LagainStc: if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon) error("found `%s` when expecting `;` following statement", token.toChars()); else - check(TOK.semicolon, "statement"); + { + if (token.value != TOK.semicolon) + error("found `%s` when expecting `;` following statement `%s` on line %s", token.toChars(), exp.toChars(), exp.loc.toChars()); + nextToken(); + } } s = new AST.ExpStatement(loc, exp); break; diff --git a/tests/dmd/fail_compilation/fail196.d b/tests/dmd/fail_compilation/fail196.d index 2c7d93fe4e0..53505f496c2 100644 --- a/tests/dmd/fail_compilation/fail196.d +++ b/tests/dmd/fail_compilation/fail196.d @@ -6,15 +6,15 @@ fail_compilation/fail196.d(27): Error: implicit string concatenation is error-pr fail_compilation/fail196.d(27): Use the explicit syntax instead (concatenating literals is `@nogc`): "foo(xxx)" ~ ";\n assert(s == " fail_compilation/fail196.d(28): Error: semicolon needed to end declaration of `s`, instead of `foo` fail_compilation/fail196.d(27): `s` declared here -fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement -fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement -fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement +fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement `foo(xxx)` on line fail_compilation/fail196.d(28) +fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement `[foo[xxx]]` on line fail_compilation/fail196.d(30) +fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement `foo[xxx]` on line fail_compilation/fail196.d(31) +fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement `foo` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement `";\n assert(s == "` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement `");\n\n s = q" < foo` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement `xxx >> ";\n assert(s == "` on line fail_compilation/fail196.d(36) fail_compilation/fail196.d(37): Error: found `<` instead of statement fail_compilation/fail196.d(43): Error: unterminated string constant starting at fail_compilation/fail196.d(43) fail_compilation/fail196.d(45): Error: found `End of File` when expecting `}` following compound statement diff --git a/tests/dmd/fail_compilation/ice11982.d b/tests/dmd/fail_compilation/ice11982.d index ff5fae491c6..0f2ce413c40 100644 --- a/tests/dmd/fail_compilation/ice11982.d +++ b/tests/dmd/fail_compilation/ice11982.d @@ -1,16 +1,19 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11982.d(16): Error: basic type expected, not `scope` -fail_compilation/ice11982.d(16): Error: found `scope` when expecting `;` following statement -fail_compilation/ice11982.d(16): Error: basic type expected, not `}` -fail_compilation/ice11982.d(16): Error: missing `{ ... }` for function literal -fail_compilation/ice11982.d(16): Error: C style cast illegal, use `cast(funk)function _error_() +fail_compilation/ice11982.d(19): Error: basic type expected, not `scope` +fail_compilation/ice11982.d(19): Error: found `scope` when expecting `;` following statement `new _error_` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(19): Error: basic type expected, not `}` +fail_compilation/ice11982.d(19): Error: missing `{ ... }` for function literal +fail_compilation/ice11982.d(19): Error: C style cast illegal, use `cast(funk)function _error_() { } ` -fail_compilation/ice11982.d(16): Error: found `}` when expecting `;` following statement -fail_compilation/ice11982.d(17): Error: found `End of File` when expecting `}` following compound statement +fail_compilation/ice11982.d(19): Error: found `}` when expecting `;` following statement `cast(funk)function _error_() +{ +} +` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(20): Error: found `End of File` when expecting `}` following compound statement --- */ void main() { new scope ( funk ) function } diff --git a/tests/dmd/fail_compilation/misc_parser_err_cov1.d b/tests/dmd/fail_compilation/misc_parser_err_cov1.d index 11fddf069d6..d1361440920 100644 --- a/tests/dmd/fail_compilation/misc_parser_err_cov1.d +++ b/tests/dmd/fail_compilation/misc_parser_err_cov1.d @@ -23,7 +23,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected following `.`, not `+` fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`. fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;` -fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement +fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement `(__error) + 0` on line fail_compilation/misc_parser_err_cov1.d(41) fail_compilation/misc_parser_err_cov1.d(43): Error: found `End of File` when expecting `}` following compound statement --- */ diff --git a/tests/dmd/fail_compilation/parseStc.d b/tests/dmd/fail_compilation/parseStc.d index c9c42882438..d13006db0ab 100644 --- a/tests/dmd/fail_compilation/parseStc.d +++ b/tests/dmd/fail_compilation/parseStc.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/parseStc.d(12): Error: missing closing `)` after `if (x` fail_compilation/parseStc.d(12): Error: use `{ }` for an empty statement, not `;` -fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement +fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement `1` on line fail_compilation/parseStc.d(12) fail_compilation/parseStc.d(13): Error: redundant attribute `const` --- */ From ec1095e36fe5d72646c96b19bb353fd6c6e4321d Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 9 Apr 2023 15:33:06 -0700 Subject: [PATCH 110/301] ImportC: support __attribute__((aligned(N))) (dlang/dmd!15047) --- dmd/cparse.d | 33 ++++++++++++++++++++++--- dmd/frontend.h | 1 + dmd/id.d | 1 + tests/dmd/compilable/imports/c23789.i | 16 ++++++++++++ tests/dmd/compilable/test23789.d | 4 +++ tests/dmd/fail_compilation/alignedext.i | 14 +++++++++++ tests/dmd/fail_compilation/test23789.c | 2 +- 7 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/fail_compilation/alignedext.i diff --git a/dmd/cparse.d b/dmd/cparse.d index ba5bbf44007..5ee95b60211 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3187,8 +3187,8 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.int32Literal) { const n = token.unsvalue; - if (n < 1 || n & (n - 1) || ushort.max < n) - error("__decspec(align(%lld)) must be an integer positive power of 2", cast(ulong)n); + if (n < 1 || n & (n - 1) || 8192 < n) + error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n); specifier.packalign.set(cast(uint)n); specifier.packalign.setPack(true); nextToken(); @@ -3401,7 +3401,34 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.identifier) { - if (token.ident == Id.dllimport) + if (token.ident == Id.aligned) + { + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || ushort.max < n) + error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } + /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data + * type on the target machine. It's the opposite of __attribute__((packed)) + */ + } + else if (token.ident == Id.dllimport) { dllimport = true; nextToken(); diff --git a/dmd/frontend.h b/dmd/frontend.h index d2bc45dfe69..47694b74a74 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8867,6 +8867,7 @@ struct Id final static Identifier* vector_size; static Identifier* noreturn; static Identifier* _align; + static Identifier* aligned; static Identifier* builtins; static Identifier* builtin_va_list; static Identifier* builtin_va_arg; diff --git a/dmd/id.d b/dmd/id.d index 9fb770fb6c2..d26b16c9156 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -529,6 +529,7 @@ immutable Msgtable[] msgtable = { "__func__" }, { "noreturn" }, { "_align", "align" }, + { "aligned" }, { "__pragma", "pragma" }, { "builtins", "__builtins" }, { "builtin_va_list", "__builtin_va_list" }, diff --git a/tests/dmd/compilable/imports/c23789.i b/tests/dmd/compilable/imports/c23789.i index 1c35d13c199..74d7e80c2ad 100644 --- a/tests/dmd/compilable/imports/c23789.i +++ b/tests/dmd/compilable/imports/c23789.i @@ -13,3 +13,19 @@ void testpl(p) struct __declspec(align(2)) S *p; { } + +///// + +struct __attribute__((aligned(64))) N128A { + char c; +}; + +typedef struct __attribute__((aligned(32))) _N128B { + int x; +} N128B, *PN128A; + + +void testpl2(p) +struct __attribute__((aligned(2))) S *p; +{ +} diff --git a/tests/dmd/compilable/test23789.d b/tests/dmd/compilable/test23789.d index 263d057e419..37fc3d4bc20 100644 --- a/tests/dmd/compilable/test23789.d +++ b/tests/dmd/compilable/test23789.d @@ -5,3 +5,7 @@ import imports.c23789; static assert(M128A.alignof == 64); static assert(_M128B.alignof == 32); static assert(M128B.alignof == 32); + +static assert(N128A.alignof == 64); +static assert(_N128B.alignof == 32); +static assert(N128B.alignof == 32); diff --git a/tests/dmd/fail_compilation/alignedext.i b/tests/dmd/fail_compilation/alignedext.i new file mode 100644 index 00000000000..eae3137ce47 --- /dev/null +++ b/tests/dmd/fail_compilation/alignedext.i @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/alignedext.i(10): Error: __decspec(align(123)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(11): Error: __decspec(align(16384)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(13): Error: __attribute__((aligned(123))) must be an integer positive power of 2 and be <= 32,768 +fail_compilation/alignedext.i(14): Error: __attribute__((aligned(65536))) must be an integer positive power of 2 and be <= 32,768 +--- +*/ + +typedef struct __declspec(align(123)) S { int a; } S; +struct __declspec(align(16384)) T { int a; }; + +typedef struct __attribute__((aligned(123))) U { int a; } S; +struct __attribute__((aligned(65536))) V { int a; }; diff --git a/tests/dmd/fail_compilation/test23789.c b/tests/dmd/fail_compilation/test23789.c index 1c813da6b12..44edc0d98ba 100644 --- a/tests/dmd/fail_compilation/test23789.c +++ b/tests/dmd/fail_compilation/test23789.c @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/test23789.c(101): Error: __decspec(align(3)) must be an integer positive power of 2 +fail_compilation/test23789.c(101): Error: __decspec(align(3)) must be an integer positive power of 2 and be <= 8,192 fail_compilation/test23789.c(103): Error: alignment value expected, not `"a"` --- */ From b6bcb73e512b20ff3ac1e1709e831648316fa31f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 10 Apr 2023 00:53:36 -0700 Subject: [PATCH 111/301] parse.d: remove dependence on globals.d (dlang/dmd!15092) --- dmd/dsymbolsem.d | 83 ++++++++++++++++++++++++++++++++++++++++++++- dmd/expressionsem.d | 3 +- dmd/lexer.d | 2 +- dmd/parse.d | 64 +--------------------------------- dmd/statementsem.d | 3 +- dmd/typesem.d | 5 +-- 6 files changed, 91 insertions(+), 69 deletions(-) diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index d5914f65999..41b7732a267 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1937,7 +1937,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut); + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); @@ -7257,3 +7258,83 @@ PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args) else return PINLINE.never; } + +/*************************************************** + * Set up loc for a parse of a mixin. Append the input text to the mixin. + * Params: + * input = mixin text + * loc = location to adjust + * mixinOut = sink for mixin text data + * Returns: + * adjusted loc suitable for Parser + */ + +Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOut) +{ + Loc result; + if (mixinOut.doOutput) + { + const lines = mixinOut.bufferLines; + writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer); + result = Loc(mixinOut.name.ptr, lines + 2, loc.charnum); + } + else if (loc.filename) + { + /* Create a pseudo-filename for the mixin string, as it may not even exist + * in the source file. + */ + auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; + char* filename = cast(char*)mem.xmalloc(len); + snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); + result = Loc(filename, loc.linnum, loc.charnum); + } + else + result = loc; + return result; +} + +/************************************** + * Append source code text to output for better debugging. + * Canonicalize line endings. + * Params: + * s = source code text + * loc = location of source code text + * lines = line count to update + * output = sink for output + */ +private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref OutBuffer buf) +{ + buf.writestring("// expansion at "); + buf.writestring(loc.toChars()); + buf.writenl(); + + ++lines; + + // write by line to create consistent line endings + size_t lastpos = 0; + for (size_t i = 0; i < s.length; ++i) + { + // detect LF and CRLF + const c = s[i]; + if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) + { + buf.writestring(s[lastpos .. i]); + buf.writenl(); + ++lines; + if (c == '\r') + ++i; + lastpos = i + 1; + } + } + + if(lastpos < s.length) + buf.writestring(s[lastpos .. $]); + + if (s.length == 0 || s[$-1] != '\n') + { + buf.writenl(); // ensure empty line after expansion + ++lines; + } + buf.writenl(); + ++lines; +} diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index c0a050beaa9..cfa6d8d3439 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -6187,7 +6187,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const len = buf.length; const str = buf.extractChars()[0 .. len]; const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut); + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/lexer.d b/dmd/lexer.d index 6c53ec4a4ec..9677a6203ee 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -49,7 +49,7 @@ struct CompileEnv const(char)[] timestamp; /// __TIMESTAMP__ bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues - bool ddocOutput; /// collect embedded documentation comments + bool ddocOutput; /// collect embedded documentation comments bool shortenedMethods = true; /// allow => in normal function declarations } diff --git a/dmd/parse.d b/dmd/parse.d index ff3201947ed..68a25064fc0 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -18,7 +18,6 @@ import core.stdc.string; import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; @@ -55,26 +54,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /********************* * Use this constructor for string mixins. * Input: - * loc location in source file of mixin + * loc = location in source file of mixin */ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { //printf("Parser::Parser()1 %d\n", doUnittests); this(_module, input, doDocComment, errorSink, compileEnv, doUnittests); - scanloc = loc; - - if (!writeMixin(input, scanloc, global.params.mixinOut) && loc.filename) - { - /* Create a pseudo-filename for the mixin string, as it may not even exist - * in the source file. - */ - auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; - char* filename = cast(char*)mem.xmalloc(len); - snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); - scanloc.filename = filename; - } } /************************************************** @@ -9752,52 +9739,3 @@ private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) } return stc; } - -/************************************** - * dump mixin expansion to file for better debugging - */ -private bool writeMixin(const(char)[] s, ref Loc loc, ref Output output) -{ - if (!output.doOutput) - return false; - - OutBuffer* ob = output.buffer; - - ob.writestring("// expansion at "); - ob.writestring(loc.toChars()); - ob.writenl(); - - output.bufferLines++; - - loc = Loc(output.name.ptr, output.bufferLines + 1, loc.charnum); - - // write by line to create consistent line endings - size_t lastpos = 0; - for (size_t i = 0; i < s.length; ++i) - { - // detect LF and CRLF - const c = s[i]; - if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) - { - ob.writestring(s[lastpos .. i]); - ob.writenl(); - output.bufferLines++; - if (c == '\r') - ++i; - lastpos = i + 1; - } - } - - if(lastpos < s.length) - ob.writestring(s[lastpos .. $]); - - if (s.length == 0 || s[$-1] != '\n') - { - ob.writenl(); // ensure empty line after expansion - output.bufferLines++; - } - ob.writenl(); - output.bufferLines++; - - return true; -} diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 9c56ce6cdff..d1231dd3c13 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -4781,7 +4781,8 @@ private Statements* flatten(Statement statement, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; - scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut); + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); diff --git a/dmd/typesem.d b/dmd/typesem.d index c941d3391d3..e00d789b935 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -5010,7 +5010,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) * Return: * null if error, else RootObject AST as parsed */ -RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) +RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) { OutBuffer buf; if (expressionsToString(buf, sc, tm.exps)) @@ -5021,7 +5021,8 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + auto locm = adjustLocForMixin(str, loc, global.params.mixinOut); + scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); From c278fb115754d9804f120deb3477652dafbb49c1 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 10 Apr 2023 10:35:55 -0700 Subject: [PATCH 112/301] fix Issue 23509 - ImportC: need statement expressions extension for GLibC's assert() (dlang/dmd!15093) --- dmd/cparse.d | 40 +++++++++++++++++++++++++++++++- dmd/hdrgen.d | 5 ++-- tests/dmd/compilable/test23509.i | 8 +++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/compilable/test23509.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 5ee95b60211..e6c8055e0f9 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -799,7 +799,10 @@ final class CParser(AST) : Parser!AST case TOK.leftParenthesis: nextToken(); - e = cparseExpression(); + if (token.value == TOK.leftCurly) + e = cparseStatementExpression(); // gcc extension + else + e = cparseExpression(); check(TOK.rightParenthesis); break; @@ -1622,6 +1625,41 @@ final class CParser(AST) : Parser!AST return e; } + /***************************** + * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html + * Represent as a function literal, then call the function literal. + * Parser is on opening curly brace. + */ + private AST.Expression cparseStatementExpression() + { + AST.ParameterList parameterList; + StorageClass stc = 0; + const loc = token.loc; + typedefTab.push(null); + auto fbody = cparseStatement(ParseStatementFlags.scope_); + typedefTab.pop(); // end of function scope + + // Rewrite last ExpStatement (if there is one) as a ReturnStatement + auto ss = fbody.isScopeStatement(); + auto cs = ss.statement.isCompoundStatement(); + assert(cs); + if (const len = (*cs.statements).length) + { + auto s = (*cs.statements)[len - 1]; + if (auto es = s.isExpStatement()) + (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp); + } + + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0); + fd.fbody = fbody; + + auto fe = new AST.FuncExp(loc, fd); + auto args = new AST.Expressions(); + auto e = new AST.CallExp(loc, fe, args); // call the function literal + return e; + } + //} /********************************************************************************/ /********************************* Declaration Parser ***************************/ diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index f38ae9aad5f..76faa64315d 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -196,10 +196,9 @@ private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) foreach (sx; *s.statements) { auto ds = sx ? sx.isExpStatement() : null; - if (ds && ds.exp.op == EXP.declaration) + if (ds && ds.exp.isDeclarationExp()) { - auto d = (cast(DeclarationExp)ds.exp).declaration; - assert(d.isDeclaration()); + auto d = ds.exp.isDeclarationExp().declaration; if (auto v = d.isVarDeclaration()) { scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs); diff --git a/tests/dmd/compilable/test23509.i b/tests/dmd/compilable/test23509.i new file mode 100644 index 00000000000..fd2c5d85e23 --- /dev/null +++ b/tests/dmd/compilable/test23509.i @@ -0,0 +1,8 @@ +// https://issues.dlang.org/show_bug.cgi?id=23509 + +int max(int a, int b) +{ + return ({int _a = (a), _b = (b); _a > _b ? _a : _b; }); +} + +_Static_assert(max(3,4) == 4, "1"); From 8f76f8db7d778cd7b7dd77269aa2f4b80ba857f3 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 10 Apr 2023 16:53:38 -0700 Subject: [PATCH 113/301] fix Issue 23715 - ImportC: No rejection of _Thread_local variables declared at function scope without 'static' as per C11 6.2.4-5 (dlang/dmd!15094) --- dmd/cparse.d | 6 ++++++ tests/dmd/compilable/testcstuff1.c | 2 +- tests/dmd/fail_compilation/test23715.i | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/test23715.i diff --git a/dmd/cparse.d b/dmd/cparse.d index e6c8055e0f9..3e1fcd43f41 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2544,6 +2544,12 @@ final class CParser(AST) : Parser!AST error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`"); scw &= ~scwx; } + if (level == LVL.local && + scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern))) + { + error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3 + scw &= ~scwx; + } if (level & (LVL.parameter | LVL.prototype) && scw & ~SCW.xregister) { diff --git a/tests/dmd/compilable/testcstuff1.c b/tests/dmd/compilable/testcstuff1.c index 13b6d96fa96..15a907ffcb2 100644 --- a/tests/dmd/compilable/testcstuff1.c +++ b/tests/dmd/compilable/testcstuff1.c @@ -277,7 +277,7 @@ void test2() typedef int TI; //extern int ei; static int si; - _Thread_local int tli; + static _Thread_local int tli; int __declspec(thread) tlj; auto int ai; register int reg; diff --git a/tests/dmd/fail_compilation/test23715.i b/tests/dmd/fail_compilation/test23715.i new file mode 100644 index 00000000000..5a1a8047ea3 --- /dev/null +++ b/tests/dmd/fail_compilation/test23715.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23715.i(11): Error: `_Thread_local` in block scope must be accompanied with `static` or `extern` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23715 + +void test2() +{ + _Thread_local int tli; +} From d8cf8ccb06d111aaaf4d2956ec9aeffcaf2a7927 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 12 Apr 2023 13:04:15 -0700 Subject: [PATCH 114/301] ImportC: support dllimport, dllexport, naked attributes (dlang/dmd!15048) * ImportC: add more support for naked, dllimport, dllexport * ImportC: support dllimport, dllexport, naked attributes --- dmd/cparse.d | 57 +++++++++++++++++++++++------- dmd/declaration.d | 5 ++- dmd/declaration.h | 4 +++ dmd/frontend.h | 8 +++++ dmd/func.d | 8 +++-- tests/dmd/compilable/cattributes.i | 31 ++++++++++++++++ 6 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 tests/dmd/compilable/cattributes.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 3e1fcd43f41..6cc806cad47 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -1958,6 +1958,7 @@ final class CParser(AST) : Parser!AST if (specifier.scw & SCW.x_Thread_local) error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); s = fd; } else @@ -1967,7 +1968,9 @@ final class CParser(AST) : Parser!AST if (!hasInitializer && !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global)) initializer = new AST.VoidInitializer(token.loc); - s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + specifiersToVarDeclaration(vd, specifier); + s = vd; } if (level != LVL.global) insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes @@ -2132,6 +2135,7 @@ final class CParser(AST) : Parser!AST typedefTab.pop(); // end of function scope auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); if (addFuncName) { @@ -3182,9 +3186,6 @@ final class CParser(AST) : Parser!AST /* Check for dllexport, dllimport * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement - bool naked; // TODO implement nextToken(); // move past __declspec check(TOK.leftParenthesis); while (1) @@ -3200,17 +3201,17 @@ final class CParser(AST) : Parser!AST { if (token.ident == Id.dllimport) { - dllimport = true; + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; nextToken(); } else if (token.ident == Id.naked) { - naked = true; + specifier.naked = true; nextToken(); } else if (token.ident == Id.noreturn) @@ -3434,12 +3435,9 @@ final class CParser(AST) : Parser!AST */ private void cparseGnuAttribute(ref Specifier specifier) { - /* Check for dllimport, dllexport, vector_size(bytes) + /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes) * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement - if (!isGnuAttributeName()) return; @@ -3474,12 +3472,17 @@ final class CParser(AST) : Parser!AST } else if (token.ident == Id.dllimport) { - dllimport = true; + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; + nextToken(); + } + else if (token.ident == Id.naked) + { + specifier.naked = true; nextToken(); } else if (token.ident == Id.noreturn) @@ -4888,6 +4891,9 @@ final class CParser(AST) : Parser!AST struct Specifier { bool noreturn; /// noreturn attribute + bool naked; /// naked attribute + bool dllimport; /// dllimport attribute + bool dllexport; /// dllexport attribute SCW scw; /// storage-class specifiers MOD mod; /// type qualifiers AST.Expressions* alignExps; /// alignment @@ -4967,6 +4973,31 @@ final class CParser(AST) : Parser!AST return stc; } + /*********************** + * Add attributes from Specifier to function + * Params: + * fd = function to apply them to + * specifier = specifiers + */ + void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier) + { + fd.isNaked = specifier.naked; + fd.dllImport = specifier.dllimport; + fd.dllExport = specifier.dllexport; + } + + /*********************** + * Add attributes from Specifier to variable + * Params: + * vd = function to apply them to + * specifier = specifiers + */ + void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier) + { + vd.dllImport = specifier.dllimport; + vd.dllExport = specifier.dllexport; + } + /*********************** * Return suitable signed integer type for the given size * Params: diff --git a/dmd/declaration.d b/dmd/declaration.d index 8eaac374d73..89377db6309 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -1149,6 +1149,8 @@ extern (C++) class VarDeclaration : Declaration bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument bool isCmacro; /// it is a C macro turned into a C declaration + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) version (MARS) { bool inClosure; /// is inserted into a GC allocated closure @@ -1314,7 +1316,7 @@ extern (C++) class VarDeclaration : Declaration override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const @@ -1325,6 +1327,7 @@ extern (C++) class VarDeclaration : Declaration * export extern int sym3 = 0; // error, extern cannot have initializer */ bool result = + dllImport || visibility.kind == Visibility.Kind.export_ && storage_class & STC.extern_ && (storage_class & STC.static_ || parent.isModule()); diff --git a/dmd/declaration.h b/dmd/declaration.h index 2da65717103..7a69382e6ba 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -675,6 +675,10 @@ class FuncDeclaration : public Declaration bool isCrtCtor(bool v); bool isCrtDtor() const; bool isCrtDtor(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); // Data for a function declaration that is needed for the Objective-C // integration. diff --git a/dmd/frontend.h b/dmd/frontend.h index 47694b74a74..5be265dc13d 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2528,6 +2528,10 @@ class FuncDeclaration : public Declaration bool hasEscapingSiblings(bool v); bool computedEscapingSiblings() const; bool computedEscapingSiblings(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); private: uint32_t bitFields; public: @@ -6033,6 +6037,10 @@ class VarDeclaration : public Declaration bool isArgDtorVar(bool v); bool isCmacro() const; bool isCmacro(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); bool inClosure() const; bool inClosure(bool v); bool inAlignSection() const; diff --git a/dmd/func.d b/dmd/func.d index 7d8f00dc27c..2c6099c8983 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -204,6 +204,7 @@ private struct FUNCFLAG bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function + bool scanf; /// is a scanf-like function bool noreturn; /// the function does not return bool isNRVO = true; /// Support for named return value optimization @@ -214,11 +215,14 @@ private struct FUNCFLAG bool hasNoEH; /// No exception unwinding is needed bool inferRetType; /// Return type is to be inferred bool hasDualContext; /// has a dual-context 'this' parameter + bool hasAlwaysInlines; /// Contains references to functions that must be inlined bool isCrtCtor; /// Has attribute pragma(crt_constructor) bool isCrtDtor; /// Has attribute pragma(crt_destructor) bool hasEscapingSiblings;/// Has sibling functions that escape bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) } /*********************************************************** @@ -1297,14 +1301,14 @@ extern (C++) class FuncDeclaration : Declaration override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", visibility); - return (visibility.kind == Visibility.Kind.export_) && !fbody; + return (visibility.kind == Visibility.Kind.export_ || dllImport) && !fbody; } override final bool isCodeseg() const pure nothrow @nogc @safe diff --git a/tests/dmd/compilable/cattributes.i b/tests/dmd/compilable/cattributes.i new file mode 100644 index 00000000000..0f255b1ee01 --- /dev/null +++ b/tests/dmd/compilable/cattributes.i @@ -0,0 +1,31 @@ +/* Smoke test dllimport, dllexport, and naked attributes */ + +__declspec(dllimport) int abc; + +__declspec(dllimport) int def(); + +__declspec(dllexport) int ghi() { return 3; } + +__declspec(dllexport) int jkl; + +__declspec(naked) __declspec(dllexport) +int test(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abc + def() + ghi() + jkl; +} + +/*****************************************/ + +__attribute__((dllimport)) int abcx; + +__attribute__((dllimport)) int defx(); + +__attribute__((dllexport)) int ghix() { return 3; } + +__attribute__((dllexport)) int jklx; + +__attribute__((naked)) __attribute__((dllexport)) +int testx(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abcx + defx() + ghix() + jklx; +} From 4144b2e93976e2488d0aaabd97ee62fa4d5ecb10 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 12 Apr 2023 23:44:44 -0700 Subject: [PATCH 115/301] ImportC: support noinline (dlang/dmd!15103) --- dmd/cparse.d | 18 ++++++++++++++++++ dmd/id.d | 1 + tests/dmd/compilable/testnoinline.c | 9 +++++++++ 3 files changed, 28 insertions(+) create mode 100644 tests/dmd/compilable/testnoinline.c diff --git a/dmd/cparse.d b/dmd/cparse.d index 6cc806cad47..5d29592f142 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3175,6 +3175,7 @@ final class CParser(AST) : Parser!AST * dllimport * dllexport * naked + * noinline * noreturn * thread * Params: @@ -3214,6 +3215,11 @@ final class CParser(AST) : Parser!AST specifier.naked = true; nextToken(); } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; + nextToken(); + } else if (token.ident == Id.noreturn) { specifier.noreturn = true; @@ -3485,6 +3491,11 @@ final class CParser(AST) : Parser!AST specifier.naked = true; nextToken(); } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; + nextToken(); + } else if (token.ident == Id.noreturn) { specifier.noreturn = true; @@ -4872,6 +4883,8 @@ final class CParser(AST) : Parser!AST // C11 6.7.4 Function specifiers xinline = 0x40, x_Noreturn = 0x80, + + xnoinline = 0x100, } /// C11 6.7.3 Type qualifiers @@ -4984,6 +4997,11 @@ final class CParser(AST) : Parser!AST fd.isNaked = specifier.naked; fd.dllImport = specifier.dllimport; fd.dllExport = specifier.dllexport; + + if (specifier.scw & SCW.xnoinline) + fd.inlining = PINLINE.never; + else if (specifier.scw & SCW.xinline) + fd.inlining = PINLINE.always; } /*********************** diff --git a/dmd/id.d b/dmd/id.d index d26b16c9156..4d56191a37b 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -527,6 +527,7 @@ immutable Msgtable[] msgtable = { "thread" }, { "vector_size" }, { "__func__" }, + { "noinline" }, { "noreturn" }, { "_align", "align" }, { "aligned" }, diff --git a/tests/dmd/compilable/testnoinline.c b/tests/dmd/compilable/testnoinline.c new file mode 100644 index 00000000000..cf3928eb58b --- /dev/null +++ b/tests/dmd/compilable/testnoinline.c @@ -0,0 +1,9 @@ + +__attribute__((noinline)) int abc() { return 1; } +__declspec(noinline) int def() { return 2; } +inline int ghi() { return 3; } + +int test() +{ + return abc() + def() + ghi(); +} From 27a035a0277403d2cd40fcc2eaa91ae254a714a2 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 12 Apr 2023 23:51:36 -0700 Subject: [PATCH 116/301] ImportC: predefine _NO_CRT_STDIO_INLINE (dlang/dmd!15102) --- runtime/druntime/src/importc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index d19e0be82b4..afe024f5984 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -112,6 +112,7 @@ #define __ptr32 #define __ptr64 #define __unaligned +#define _NO_CRT_STDIO_INLINE 1 #endif /**************************** From ec0a2e1f80037fcc55d0cc931fd27a79a205b5e7 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Thu, 13 Apr 2023 18:12:10 +0300 Subject: [PATCH 117/301] Fix Issue 23836 - Two errors printed for typeof(super) in non-static member context (dlang/dmd!15104) --- dmd/expressionsem.d | 16 +++++++--------- tests/dmd/fail_compilation/fail_typeof.d | 21 +++++++++------------ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index cfa6d8d3439..9fe2686e5e1 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2948,7 +2948,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class or struct scope", e.toChars()); - goto Lerr; + return setError(); } ClassDeclaration cd = s.isClassDeclaration(); if (cd) @@ -2967,7 +2967,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } if (!fd) - goto Lerr; + { + e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); + return setError(); + } assert(fd.vthis); e.var = fd.vthis; @@ -2982,11 +2985,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); - result = ErrorExp.get(); } override void visit(SuperExp e) @@ -3016,7 +3014,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class scope", e.toChars()); - goto Lerr; + return setError(); } cd = s.isClassDeclaration(); if (cd) @@ -3025,7 +3023,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!cd) { e.error("class `%s` has no `super`", s.toChars()); - goto Lerr; + return setError(); } e.type = cd.type; result = e; diff --git a/tests/dmd/fail_compilation/fail_typeof.d b/tests/dmd/fail_compilation/fail_typeof.d index 392cebd2f73..a3b4d1a1415 100644 --- a/tests/dmd/fail_compilation/fail_typeof.d +++ b/tests/dmd/fail_compilation/fail_typeof.d @@ -1,17 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail_typeof.d(18): Error: undefined identifier `this` -fail_compilation/fail_typeof.d(23): Error: `this` is not in a class or struct scope -fail_compilation/fail_typeof.d(23): Error: `this` is only defined in non-static member functions, not `fail_typeof` -fail_compilation/fail_typeof.d(28): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(33): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(33): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(40): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(50): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(55): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(55): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(63): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(73): Error: undefined identifier `super`, did you mean `typeof(super)`? +fail_compilation/fail_typeof.d(15): Error: undefined identifier `this` +fail_compilation/fail_typeof.d(20): Error: `this` is not in a class or struct scope +fail_compilation/fail_typeof.d(25): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(30): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(37): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(47): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(52): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(60): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(70): Error: undefined identifier `super`, did you mean `typeof(super)`? --- */ From 4ff0fefb32ab0bd033390dd4ffdd63527e359bcf Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 13 Apr 2023 12:17:15 -0700 Subject: [PATCH 118/301] fix Issue 23402 - importc function definitions from includes can cause D name conflicts (dlang/dmd!15101) --- dmd/expressionsem.d | 11 +++++++++-- tests/dmd/runnable/imports/imp23402a.c | 1 + tests/dmd/runnable/imports/imp23402b.c | 1 + tests/dmd/runnable/test23402.d | 10 ++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/runnable/imports/imp23402a.c create mode 100644 tests/dmd/runnable/imports/imp23402b.c create mode 100644 tests/dmd/runnable/test23402.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 9fe2686e5e1..2b44f535911 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -4850,10 +4850,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return null; if (f) { - /* Error if match in more than one overload set, + /* Match in more than one overload set, * even if one is a 'better' match than the other. */ - ScopeDsymbol.multiplyDefined(loc, f, f2); + if (f.isCsymbol() && f2.isCsymbol()) + { + /* C has global name space, so just pick one, such as f. + * If f and f2 are not compatible, that's how C rolls. + */ + } + else + ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error } else f = f2; diff --git a/tests/dmd/runnable/imports/imp23402a.c b/tests/dmd/runnable/imports/imp23402a.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402a.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/imports/imp23402b.c b/tests/dmd/runnable/imports/imp23402b.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402b.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/test23402.d b/tests/dmd/runnable/test23402.d new file mode 100644 index 00000000000..22fd21a7170 --- /dev/null +++ b/tests/dmd/runnable/test23402.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23402 + +import imports.imp23402a; +import imports.imp23402b; + +int main() +{ + printf("hello world\n"); + return 0; +} From 46642cc97c87e60aff5b5f708056b1bd71235776 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 13 Apr 2023 12:18:34 -0700 Subject: [PATCH 119/301] ImportC: __attribute((deprecated)) and __declspec(deprecated) (dlang/dmd!15095) --- dmd/cparse.d | 39 ++++++++++++++++++++++++ dmd/id.d | 1 + runtime/druntime/src/importc.h | 3 ++ tests/dmd/fail_compilation/cdeprecated.i | 22 +++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 tests/dmd/fail_compilation/cdeprecated.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 5d29592f142..7b2439d25cf 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3172,6 +3172,7 @@ final class CParser(AST) : Parser!AST * * extended-decl-modifier: * align(number) + * deprecated(depMsg) * dllimport * dllexport * naked @@ -3252,6 +3253,17 @@ final class CParser(AST) : Parser!AST check(TOK.rightParenthesis); } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } else { nextToken(); @@ -3476,6 +3488,17 @@ final class CParser(AST) : Parser!AST * type on the target machine. It's the opposite of __attribute__((packed)) */ } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } else if (token.ident == Id.dllimport) { specifier.dllimport = true; @@ -4907,6 +4930,9 @@ final class CParser(AST) : Parser!AST bool naked; /// naked attribute bool dllimport; /// dllimport attribute bool dllexport; /// dllexport attribute + bool _deprecated; /// deprecated attribute + AST.Expression depMsg; /// deprecated message + SCW scw; /// storage-class specifiers MOD mod; /// type qualifiers AST.Expressions* alignExps; /// alignment @@ -4983,6 +5009,8 @@ final class CParser(AST) : Parser!AST stc = AST.STC.gshared; } } + if (specifier._deprecated && !specifier.depMsg) + stc |= AST.STC.deprecated_; return stc; } @@ -5179,6 +5207,17 @@ final class CParser(AST) : Parser!AST private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier) { //printf("applySpecifier() %s\n", s.toChars()); + if (specifier._deprecated) + { + if (specifier.depMsg) + { + // Wrap declaration in a DeprecatedDeclaration + auto decls = new AST.Dsymbols(1); + (*decls)[0] = s; + s = new AST.DeprecatedDeclaration(specifier.depMsg, decls); + } + } + if (specifier.alignExps) { //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign); diff --git a/dmd/id.d b/dmd/id.d index 4d56191a37b..bffad0b7bd6 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -529,6 +529,7 @@ immutable Msgtable[] msgtable = { "__func__" }, { "noinline" }, { "noreturn" }, + { "_deprecated", "deprecated" }, { "_align", "align" }, { "aligned" }, { "__pragma", "pragma" }, diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index afe024f5984..60414d81730 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -109,6 +109,9 @@ #if _MSC_VER //#undef _Post_writable_size //#define _Post_writable_size(x) // consider #include +#define _CRT_INSECURE_DEPRECATE(x) +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 #define __ptr32 #define __ptr64 #define __unaligned diff --git a/tests/dmd/fail_compilation/cdeprecated.i b/tests/dmd/fail_compilation/cdeprecated.i new file mode 100644 index 00000000000..75ce2acbe6e --- /dev/null +++ b/tests/dmd/fail_compilation/cdeprecated.i @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/cdeprecated.i(18): Deprecation: function `cdeprecated.mars` is deprecated +fail_compilation/cdeprecated.i(19): Deprecation: function `cdeprecated.jupiter` is deprecated - jumping jupiter +fail_compilation/cdeprecated.i(20): Deprecation: function `cdeprecated.saturn` is deprecated +fail_compilation/cdeprecated.i(21): Deprecation: function `cdeprecated.neptune` is deprecated - spinning neptune +--- +*/ +__declspec(deprecated) int mars(); +__declspec(deprecated("jumping jupiter")) int jupiter(); +__attribute__((deprecated)) extern int saturn(); +__attribute__((deprecated("spinning neptune"))) extern int neptune(); + +int test() +{ + return + mars() + + jupiter() + + saturn() + + neptune(); +} From 420ccce69cdd6b58b7983b5b92f6cd1fc91ae750 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 17 Apr 2023 11:42:05 +0200 Subject: [PATCH 120/301] Make big structs in gc.d zero initialized (dlang/dmd!15108) --- runtime/druntime/src/core/internal/gc/impl/conservative/gc.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d index 6d19247fbe0..62ce941e393 100644 --- a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -1509,7 +1509,7 @@ struct Gcx List*[Bins.B_NUMSMALL] bucket; // free list for each small size // run a collection when reaching those thresholds (number of used pages) - float smallCollectThreshold, largeCollectThreshold; + float smallCollectThreshold = 0.0f, largeCollectThreshold = 0.0f; uint usedSmallPages, usedLargePages; // total number of mapped pages uint mappedPages; @@ -3529,7 +3529,7 @@ struct Pool Small = 4, Large = 12 } - ShiftBy shiftBy; // shift count for the divisor used for determining bit indices. + ShiftBy shiftBy = void; // shift count for the divisor used for determining bit indices. // This tracks how far back we have to go to find the nearest B_PAGE at // a smaller address than a B_PAGEPLUS. To save space, we use a uint. From 89893be66f6dbf0d7de317cecc233315096586c1 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 17 Apr 2023 02:42:44 -0700 Subject: [PATCH 121/301] fix Issue 23427 - ImportC: some bitfield combinations lead to wrong size struct (dlang/dmd!15110) --- dmd/declaration.d | 9 +- tests/dmd/compilable/posixbitfields.c | 159 ++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/compilable/posixbitfields.c diff --git a/dmd/declaration.d b/dmd/declaration.d index 89377db6309..0e5df5eb550 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -1934,8 +1934,12 @@ extern (C++) class BitFieldDeclaration : VarDeclaration { // If the bit-field spans more units of alignment than its type, // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8) + if (fieldState.bitOffset == fieldState.fieldSize * 8 && + fieldState.bitOffset + fieldWidth > memalignsize * 8) + { + if (log) printf("more units of alignment than its type\n"); startNewField(); // the bit field is full + } else { // if alignment boundary is crossed @@ -1944,7 +1948,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) { - //printf("alignment is crossed\n"); + if (log) printf("alignment is crossed\n"); startNewField(); } } @@ -1984,6 +1988,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration fieldState.bitOffset = pastField; } + //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize); //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); } diff --git a/tests/dmd/compilable/posixbitfields.c b/tests/dmd/compilable/posixbitfields.c new file mode 100644 index 00000000000..8bda9d4b8b9 --- /dev/null +++ b/tests/dmd/compilable/posixbitfields.c @@ -0,0 +1,159 @@ +// DISABLED: win32 win64 + +// https://issues.dlang.org/show_bug.cgi?id=23427 + +_Static_assert(sizeof(unsigned) == 4, "1"); + +struct A { + unsigned x :8; + unsigned y :4; + unsigned z :20; +}; +_Static_assert(sizeof(struct A)==4, "2"); + +struct B { + unsigned x :4; + unsigned y :2; + unsigned z :26; +}; +_Static_assert(sizeof(struct B)==4, "3"); + +struct C { + unsigned x :4; + unsigned y :4; + unsigned z :24; +}; +_Static_assert(sizeof(struct C)==4, "4"); // This one fails + + +_Static_assert(sizeof(struct { + unsigned a: 1; + unsigned b: 7; + unsigned c: 24; +}) == sizeof(unsigned), "1 7 24"); + +_Static_assert(sizeof(struct { + unsigned a: 2; + unsigned b: 6; + unsigned c: 24; +}) == sizeof(unsigned), "2 6 24"); + +_Static_assert(sizeof(struct { + unsigned a: 3; + unsigned b: 5; + unsigned c: 24; +}) == sizeof(unsigned), "3 5 24"); + +_Static_assert(sizeof(struct { + unsigned a: 4; + unsigned b: 4; + unsigned c: 24; +}) == sizeof(unsigned), "4 4 24"); + +_Static_assert(sizeof(struct { + unsigned a: 5; + unsigned b: 3; + unsigned c: 24; +}) == sizeof(unsigned), "5 3 24"); + +_Static_assert(sizeof(struct { + unsigned a: 6; + unsigned b: 2; + unsigned c: 24; +}) == sizeof(unsigned), "6 2 24"); + +_Static_assert(sizeof(struct { + unsigned a: 7; + unsigned b: 1; + unsigned c: 24; +}) == sizeof(unsigned), "7 1 24"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 9; + unsigned c: 15; +}) == sizeof(unsigned), "8 9 15"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 10; + unsigned c: 14; +}) == sizeof(unsigned), "8 10 14"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 11; + unsigned c: 13; +}) == sizeof(unsigned), "8 11 13"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 12; + unsigned c: 12; +}) == sizeof(unsigned), "8 12 12"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 13; + unsigned c: 11; +}) == sizeof(unsigned), "8 13 11"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 14; + unsigned c: 10; +}) == sizeof(unsigned), "8 14 10"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 15; + unsigned c: 9; +}) == sizeof(unsigned), "8 15 9"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 16; + unsigned c: 8; +}) == sizeof(unsigned), "8 16 8"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 17; + unsigned c: 7; +}) == sizeof(unsigned), "8 17 7"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 18; + unsigned c: 6; +}) == sizeof(unsigned), "8 18 6"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 19; + unsigned c: 5; +}) == sizeof(unsigned), "8 19 5"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 20; + unsigned c: 4; +}) == sizeof(unsigned), "8 20 4"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 21; + unsigned c: 3; +}) == sizeof(unsigned), "8 21 3"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 22; + unsigned c: 2; +}) == sizeof(unsigned), "8 22 2"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 23; + unsigned c: 1; +}) == sizeof(unsigned), "8 23 1"); From 65c2c2f9a8c33f2856f2a16bc8a271e588ca8684 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 17 Apr 2023 11:59:44 +0200 Subject: [PATCH 122/301] Fix 21667 - scope parameter causes 'no size because of forward references' (dlang/dmd!14561) --- dmd/typesem.d | 22 ------------------- tests/dmd/compilable/test21667.d | 19 ++++++++++++++++ tests/dmd/fail_compilation/previewin.d | 8 +++---- tests/dmd/fail_compilation/testrvaluecpctor.d | 2 +- tests/dmd/runnable/mangle.d | 6 ----- 5 files changed, 24 insertions(+), 33 deletions(-) create mode 100644 tests/dmd/compilable/test21667.d diff --git a/dmd/typesem.d b/dmd/typesem.d index e00d789b935..8c6063de734 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1317,28 +1317,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)"); } - /* Scope attribute is not necessary if the parameter type does not have pointers - */ - const sr = buildScopeRef(fparam.storageClass); - switch (sr) - { - case ScopeRef.Scope: - case ScopeRef.RefScope: - case ScopeRef.ReturnRef_Scope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~STC.scope_; - break; - - case ScopeRef.ReturnScope: - case ScopeRef.Ref_ReturnScope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope); - break; - - default: - break; - } - // Remove redundant storage classes for type, they are already applied fparam.storageClass &= ~(STC.TYPECTOR); diff --git a/tests/dmd/compilable/test21667.d b/tests/dmd/compilable/test21667.d new file mode 100644 index 00000000000..6d83dc056c4 --- /dev/null +++ b/tests/dmd/compilable/test21667.d @@ -0,0 +1,19 @@ +// Issue 21667 - scope parameter causes 'no size because of forward references' +// https://issues.dlang.org/show_bug.cgi?id=21667 +@safe: + +struct Foo +{ + void delegate(scope Foo) dg; +} + +struct M +{ + F.Type f; +} + +struct F +{ + enum Type {a} + void foo(scope M m) {} +} diff --git a/tests/dmd/fail_compilation/previewin.d b/tests/dmd/fail_compilation/previewin.d index ca540930129..d0e97c8bcd3 100644 --- a/tests/dmd/fail_compilation/previewin.d +++ b/tests/dmd/fail_compilation/previewin.d @@ -4,10 +4,10 @@ TEST_OUTPUT: --- fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)` fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(17): Error: scope parameter `arg` may not be returned diff --git a/tests/dmd/fail_compilation/testrvaluecpctor.d b/tests/dmd/fail_compilation/testrvaluecpctor.d index 96511f511e4..50cebabfda4 100644 --- a/tests/dmd/fail_compilation/testrvaluecpctor.d +++ b/tests/dmd/fail_compilation/testrvaluecpctor.d @@ -6,7 +6,7 @@ TEST_OUTPUT: fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `__ctor` are callable using a `immutable` object -fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref Foo!int rhs)` +fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)` fail_compilation/testrvaluecpctor.d(16): `__ctor(Rhs, this This)(scope Rhs rhs)` --- */ diff --git a/tests/dmd/runnable/mangle.d b/tests/dmd/runnable/mangle.d index 7599e0e03c3..6e8f2b28987 100644 --- a/tests/dmd/runnable/mangle.d +++ b/tests/dmd/runnable/mangle.d @@ -571,12 +571,6 @@ void test12231() /***************************************************/ -int test2a(scope int a) { return a; } - -static assert(test2a.mangleof == "_D6mangle6test2aFiZi"); - -/***************************************************/ - class CC { int* p; From 0af4e7f318dfb0d4dc383fb05c123ea0c1a2c3c3 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Mon, 17 Apr 2023 15:57:30 +0300 Subject: [PATCH 123/301] Fix Issue 23838 - DMD lexer / parser examples might not compile (dlang/dmd!15106) --- tests/dmd/dub_package/avg.d | 7 ++++--- tests/dmd/dub_package/frontend.d | 2 +- tests/dmd/dub_package/frontend_file.d | 2 +- tests/dmd/dub_package/impvisitor.d | 10 ++++++---- tests/dmd/dub_package/lexer.d | 5 +++-- tests/dmd/dub_package/parser.d | 5 +++-- tests/dmd/dub_package/retrieveScope.d | 11 +++-------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/dmd/dub_package/avg.d b/tests/dmd/dub_package/avg.d index 4ce5776e777..b6f9f1e8898 100755 --- a/tests/dmd/dub_package/avg.d +++ b/tests/dmd/dub_package/avg.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ /* This file contains an example on how to use the transitive visitor. It implements a visitor which computes the average function length from @@ -10,7 +10,7 @@ dependency "dmd" path="../.." module examples.avg; import dmd.astbase; -import dmd.errors; +import dmd.errorsink; import dmd.parse; import dmd.target; import dmd.transitivevisitor; @@ -60,8 +60,9 @@ void main() auto id = Identifier.idPool(fname); auto m = new ASTBase.Module(&(fname.dup)[0], id, false, false); auto input = readText(fname); + input ~= '\0'; - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); diff --git a/tests/dmd/dub_package/frontend.d b/tests/dmd/dub_package/frontend.d index b034027deb9..184f5960d7b 100755 --- a/tests/dmd/dub_package/frontend.d +++ b/tests/dmd/dub_package/frontend.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; diff --git a/tests/dmd/dub_package/frontend_file.d b/tests/dmd/dub_package/frontend_file.d index 71e62f51528..a6d662ebf64 100755 --- a/tests/dmd/dub_package/frontend_file.d +++ b/tests/dmd/dub_package/frontend_file.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; import std.string : replace; diff --git a/tests/dmd/dub_package/impvisitor.d b/tests/dmd/dub_package/impvisitor.d index 85df47da226..c27a71abea0 100755 --- a/tests/dmd/dub_package/impvisitor.d +++ b/tests/dmd/dub_package/impvisitor.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import dmd.permissivevisitor; @@ -27,7 +27,7 @@ extern(C++) class ImportVisitor2(AST) : ParseTimeTransitiveVisitor!AST printf("%s", imp.id.toChars()); - if (imp.names.dim) + if (imp.names.length) { printf(" : "); foreach (const i, const name; imp.names) @@ -82,12 +82,13 @@ void main() import dmd.id; import dmd.globals; import dmd.identifier; + import dmd.errorsink; import dmd.target; import core.memory; GC.disable(); - string path = __FILE_FULL_PATH__.dirName.buildPath("../../../phobos/std/"); + string path = __FILE_FULL_PATH__.dirName.buildPath("../../../../phobos/std/"); string regex = "*.d"; auto dFiles = dirEntries(path, regex, SpanMode.depth); @@ -106,9 +107,10 @@ void main() auto id = Identifier.idPool(fn); auto m = new ASTBase.Module(&(fn.dup)[0], id, false, false); auto input = readText(fn); + input ~= '\0'; //writeln("Started parsing..."); - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); //writeln("Finished parsing. Starting transitive visitor"); diff --git a/tests/dmd/dub_package/lexer.d b/tests/dmd/dub_package/lexer.d index 608f18e031b..b496d4b62c9 100755 --- a/tests/dmd/dub_package/lexer.d +++ b/tests/dmd/dub_package/lexer.d @@ -1,12 +1,13 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.globals; import dmd.lexer; import dmd.tokens; + import dmd.errorsink; immutable expected = [ TOK.void_, @@ -18,7 +19,7 @@ void main() ]; immutable sourceCode = "void test() {} // foobar"; - scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0); + scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0, 0, new ErrorSinkStderr); lexer.nextToken; TOK[] result; diff --git a/tests/dmd/dub_package/parser.d b/tests/dmd/dub_package/parser.d index 2c9d7668866..9d24a77089a 100755 --- a/tests/dmd/dub_package/parser.d +++ b/tests/dmd/dub_package/parser.d @@ -1,13 +1,14 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.astbase; import dmd.globals; import dmd.parse; + import dmd.errorsink; - scope parser = new Parser!ASTBase(null, null, false); + scope parser = new Parser!ASTBase(null, null, false, new ErrorSinkStderr, null, false); assert(parser !is null); } diff --git a/tests/dmd/dub_package/retrieveScope.d b/tests/dmd/dub_package/retrieveScope.d index 36e05c365fe..bfd6d5f4614 100755 --- a/tests/dmd/dub_package/retrieveScope.d +++ b/tests/dmd/dub_package/retrieveScope.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." versions "CallbackAPI" +/ /* @@ -20,13 +20,13 @@ import std.path : dirName; import dmd.errors; import dmd.frontend; -import dmd.mars; import dmd.console; import dmd.arraytypes; import dmd.compiler; import dmd.dmodule; import dmd.dsymbol; import dmd.dsymbolsem; +import dmd.location; import dmd.semantic2; import dmd.semantic3; import dmd.statement; @@ -77,14 +77,9 @@ int main() global.gag = 1; initDMD(diagnosticHandler); - Strings libmodules; - Module m = createModule((dirName(__FILE_FULL_PATH__) ~ "/testfiles/correct.d").ptr, - libmodules); + Module m = parseModule(__FILE_FULL_PATH__ ~ "/testfiles/correct.d").module_; m.importedFrom = m; // m.isRoot() == true - m.read(Loc.initial); - m.parse(); - CallbackHelper.cursorLoc = Loc(to!string(m.srcfile).ptr, 22, 10); Compiler.onStatementSemanticStart = &CallbackHelper.statementSem; From c6c9755a386c087ba42cdd11d30931de3a20136b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 19 Apr 2023 00:51:31 -0700 Subject: [PATCH 124/301] fix Issue 22960 - importC: K&R-style functions assume variadic calling convention (dlang/dmd!15107) --- dmd/astenums.d | 1 + dmd/cparse.d | 6 ++---- dmd/expressionsem.d | 3 ++- dmd/frontend.h | 3 +++ dmd/hdrgen.d | 1 + dmd/inline.d | 3 ++- dmd/mtype.d | 2 +- dmd/traits.d | 2 ++ tests/dmd/compilable/cvariadic.i | 11 +++++++++++ 9 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 tests/dmd/compilable/cvariadic.i diff --git a/dmd/astenums.d b/dmd/astenums.d index 2c7788336bf..77f36f304a5 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -327,6 +327,7 @@ enum VarArg : ubyte variadic = 1, /// (T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) typesafe = 2, /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + KRvariadic = 3, /// K+R C style variadics (no function prototype) } /************************* diff --git a/dmd/cparse.d b/dmd/cparse.d index 7b2439d25cf..a9dddac7aad 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2081,8 +2081,7 @@ final class CParser(AST) : Parser!AST auto pl = ft.parameterList; if (pl.varargs != AST.VarArg.none && pl.length) error("function identifier-list cannot end with `...`"); - ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments - importBuiltins = true; // will need __va_list_tag + ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments auto plLength = pl.length; if (symbols.length != plLength) error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length); @@ -3046,8 +3045,7 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.rightParenthesis) // func() { nextToken(); - importBuiltins = true; // will need __va_list_tag - return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc); + return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc); } /* Create function prototype scope diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 2b44f535911..e3163ee58f5 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2265,7 +2265,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, default: break; } - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) { const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)"; if (arg.type.ty == Tarray) diff --git a/dmd/frontend.h b/dmd/frontend.h index 5be265dc13d..0a3646c98e1 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2405,6 +2405,7 @@ enum class VarArg : uint8_t none = 0u, variadic = 1u, typesafe = 2u, + KRvariadic = 3u, }; struct ParameterList final @@ -8873,7 +8874,9 @@ struct Id final static Identifier* naked; static Identifier* thread; static Identifier* vector_size; + static Identifier* noinline; static Identifier* noreturn; + static Identifier* _deprecated; static Identifier* _align; static Identifier* aligned; static Identifier* builtins; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 76faa64315d..3e115bb4f5a 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -3229,6 +3229,7 @@ private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* h final switch (pl.varargs) { case VarArg.none: + case VarArg.KRvariadic: break; case VarArg.variadic: diff --git a/dmd/inline.d b/dmd/inline.d index aaa7c3174c7..4e0b0a86531 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -1722,7 +1722,8 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat TypeFunction tf = fd.type.isTypeFunction(); // no variadic parameter lists - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) goto Lno; /* No lazy parameters when inlining by statement, as the inliner tries to diff --git a/dmd/mtype.d b/dmd/mtype.d index 95845a4f28b..f86bb2b9620 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -4267,7 +4267,7 @@ extern (C++) final class TypeFunction : TypeNext super(Tfunction, treturn); //if (!treturn) *(char*)0=0; // assert(treturn); - assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe); + assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.max); this.parameterList = pl; this.linkage = linkage; diff --git a/dmd/traits.d b/dmd/traits.d index 788e0fb6b82..da56c88e540 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -1316,6 +1316,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments` * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg * "typesafe" void typesafe(T[] ...) + * "KR" old K+R style */ // get symbol linkage as a string if (dim != 1) @@ -1350,6 +1351,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) case VarArg.variadic: style = (link == LINK.d) ? "argptr" : "stdarg"; break; + case VarArg.KRvariadic: style = "KR"; break; case VarArg.typesafe: style = "typesafe"; break; } auto se = new StringExp(e.loc, style); diff --git a/tests/dmd/compilable/cvariadic.i b/tests/dmd/compilable/cvariadic.i new file mode 100644 index 00000000000..70a9fef8cd1 --- /dev/null +++ b/tests/dmd/compilable/cvariadic.i @@ -0,0 +1,11 @@ +int abc() { return 1; } +int def(const char *p, ...) { return 2; } +int ghi(const char *p, int i) { return 3; } + +int main() +{ + abc("hello world %d\n", 1); + def("hello world %d\n", 2); + ghi("hello world %d\n", 3); + return 0; +} From 670ca7dc95ace3805bf3c74196a4ca42d753b1a8 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 19 Apr 2023 00:52:11 -0700 Subject: [PATCH 125/301] fix Issue 23401 - ImportC: add -cpp=filename switch to select C preprocessor (dlang/dmd!15112) --- dmd/cli.d | 6 ++++++ dmd/frontend.h | 5 ++++- dmd/globals.d | 1 + dmd/globals.h | 1 + dmd/mars.d | 12 ++++++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dmd/cli.d b/dmd/cli.d index f1afb0c8ee4..597b7beec3a 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -266,6 +266,12 @@ dmd -cov -unittest myprog.d --- `, ), + Option("cpp=", + "use filename as the name of the C preprocessor to use for ImportC files", + `Normally the C preprocessor used by the associated C compiler is used to + preprocess ImportC files, + this is overridden by the $(TT -cpp) switch.` + ), Option("D", "generate documentation", `$(P Generate $(LINK2 $(ROOT_DIR)spec/ddoc.html, documentation) from source.) diff --git a/dmd/frontend.h b/dmd/frontend.h index 0a3646c98e1..275eb9d1173 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3131,6 +3131,7 @@ struct Param final bool run; Array runargs; Array cppswitches; + const char* cpp; Array objfiles; Array linkswitches; Array linkswitchIsForCC; @@ -3228,6 +3229,7 @@ struct Param final run(), runargs(), cppswitches(), + cpp(), objfiles(), linkswitches(), linkswitchIsForCC(), @@ -3239,7 +3241,7 @@ struct Param final mapfile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)-1, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)-1, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : obj(obj), multiobj(multiobj), trace(trace), @@ -3334,6 +3336,7 @@ struct Param final run(run), runargs(runargs), cppswitches(cppswitches), + cpp(cpp), objfiles(objfiles), linkswitches(linkswitches), linkswitchIsForCC(linkswitchIsForCC), diff --git a/dmd/globals.d b/dmd/globals.d index 4d5d2461ae5..45b4528c204 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -214,6 +214,7 @@ extern (C++) struct Param bool run; // run resulting executable Strings runargs; // arguments for executable Array!(const(char)*) cppswitches; // C preprocessor switches + const(char)* cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array!(const(char)*) objfiles; diff --git a/dmd/globals.h b/dmd/globals.h index 836c0dd9b20..902cf83f81f 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -212,6 +212,7 @@ struct Param Strings runargs; // arguments for executable Array cppswitches; // preprocessor switches + const char *cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array objfiles; diff --git a/dmd/mars.d b/dmd/mars.d index a7ff9c984b5..539f712c0c3 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -1395,6 +1395,18 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param if (arg == "-allinst") // https://dlang.org/dmd.html#switch-allinst params.allInst = true; + else if (startsWith(p + 1, "cpp=")) // https://dlang.org/dmd.html#switch-cpp + { + if (p[5]) + { + params.cpp = p + 5; + } + else + { + errorInvalidSwitch(p, "it must be followed by the filename of the desired C preprocessor"); + return false; + } + } else if (arg == "-de") // https://dlang.org/dmd.html#switch-de params.useDeprecated = DiagnosticReporting.error; else if (arg == "-d") // https://dlang.org/dmd.html#switch-d From b595174babd7114eae60d7d49210d4171dd822b4 Mon Sep 17 00:00:00 2001 From: Dennis Date: Wed, 19 Apr 2023 12:26:53 +0200 Subject: [PATCH 126/301] Infer `scope` only on parameters with pointers (dlang/dmd!15116) --- dmd/escape.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/escape.d b/dmd/escape.d index 420fa7f80bb..4f1edaa4d05 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -2377,7 +2377,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) foreach (u, p; f.parameterList) { auto v = (*funcdecl.parameters)[u]; - if (!v.isScope() && inferScope(v)) + if (!v.isScope() && v.type.hasPointers() && inferScope(v)) { //printf("Inferring scope for %s\n", v.toChars()); p.storageClass |= STC.scope_ | STC.scopeinferred; From 93175f84d587d2dd7d01b70f278aefe65a3f17d9 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Thu, 20 Apr 2023 11:20:47 +0300 Subject: [PATCH 127/301] Fix Issue 20268 - anonymous function parameter mismatch errors don't include parameters (dlang/dmd!15113) --- dmd/dtemplate.d | 3 --- tests/dmd/fail_compilation/diag20268.d | 12 ++++++++++++ tests/dmd/fail_compilation/diag9831.d | 2 +- tests/dmd/fail_compilation/fail12236.d | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 tests/dmd/fail_compilation/diag20268.d diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index 011fb3d7a7b..ef743d60fd9 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -741,9 +741,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol const(char)* toCharsMaybeConstraints(bool includeConstraints) const { - if (literal) - return Dsymbol.toChars(); - OutBuffer buf; HdrGenState hgs; diff --git a/tests/dmd/fail_compilation/diag20268.d b/tests/dmd/fail_compilation/diag20268.d new file mode 100644 index 00000000000..a314561892a --- /dev/null +++ b/tests/dmd/fail_compilation/diag20268.d @@ -0,0 +1,12 @@ +// https://issues.dlang.org/show_bug.cgi?id=20268 + +/* +TEST_OUTPUT: +--- +fail_compilation/diag20268.d(12): Error: none of the overloads of template `diag20268.__lambda4` are callable using argument types `!()(int)` +fail_compilation/diag20268.d(11): Candidate is: `__lambda4(__T1, __T2)(x, y)` +--- +*/ + +alias f = (x,y) => true; +auto x = f(1); diff --git a/tests/dmd/fail_compilation/diag9831.d b/tests/dmd/fail_compilation/diag9831.d index b990ced0522..c93a06a465a 100644 --- a/tests/dmd/fail_compilation/diag9831.d +++ b/tests/dmd/fail_compilation/diag9831.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3` cannot access variable `c` in frame of function `D main` +fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main` fail_compilation/diag9831.d(11): `c` declared here --- */ diff --git a/tests/dmd/fail_compilation/fail12236.d b/tests/dmd/fail_compilation/fail12236.d index 738864cca35..824f5e48db2 100644 --- a/tests/dmd/fail_compilation/fail12236.d +++ b/tests/dmd/fail_compilation/fail12236.d @@ -7,7 +7,7 @@ fail_compilation/fail12236.d(21): Error: forward reference to inferred return ty fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)` fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1.mangleof)` +fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)` --- */ From bfe8a647ec3ac286fd38e39122525a7cea75e0a5 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 25 Apr 2023 03:15:16 -0700 Subject: [PATCH 128/301] core.internal.string: upgrade conversion functions (dlang/dmd!15123) --- runtime/druntime/src/core/internal/string.d | 62 ++++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/runtime/druntime/src/core/internal/string.d b/runtime/druntime/src/core/internal/string.d index 64a9cc92ffb..e09bba4707b 100644 --- a/runtime/druntime/src/core/internal/string.d +++ b/runtime/druntime/src/core/internal/string.d @@ -12,26 +12,57 @@ module core.internal.string; pure: nothrow: @nogc: +@safe: -alias UnsignedStringBuf = char[20]; +alias UnsignedStringBuf = char[64]; /** Converts an unsigned integer value to a string of characters. -This implementation is a template so it can be used when compiling with -betterC. +Can be used when compiling with -betterC. Does not allocate memory. Params: + T = char, wchar or dchar value = the unsigned integer value to convert buf = the pre-allocated buffer used to store the result - radix = the numeric base to use in the conversion (defaults to 10) + radix = the numeric base to use in the conversion 2 through 36 (defaults to 10) + upperCase = use upper case letters for radices 11 - 36 Returns: The unsigned integer value as a string of characters */ -char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe -if (radix >= 2 && radix <= 16) +T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ulong value, return scope T[] buf) +if (radix >= 2 && radix <= 36 && + (is(T == char) || is(T == wchar) || is(T == dchar))) { + enum baseChar = upperCase ? 'A' : 'a'; size_t i = buf.length; + + static if (size_t.sizeof == 4) // 32 bit CPU + { + if (value <= uint.max) + { + // use faster 32 bit arithmetic + uint val = cast(uint) value; + do + { + uint x = void; + if (val < radix) + { + x = cast(uint)val; + val = 0; + } + else + { + x = cast(uint)(val % radix); + val /= radix; + } + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); + } while (val); + return buf[i .. $]; + } + } + do { uint x = void; @@ -45,7 +76,7 @@ if (radix >= 2 && radix <= 16) x = cast(uint)(value % radix); value /= radix; } - buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a'); + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); } while (value); return buf[i .. $]; } @@ -73,7 +104,7 @@ Params: Returns: The unsigned integer value as a string of characters */ -auto unsignedToTempString(uint radix = 10)(ulong value) @safe +auto unsignedToTempString(uint radix = 10)(ulong value) { // Need a buffer of 65 bytes for radix of 2 with room for // signedToTempString to possibly add a negative sign. @@ -85,11 +116,12 @@ auto unsignedToTempString(uint radix = 10)(ulong value) @safe unittest { - UnsignedStringBuf buf; + UnsignedStringBuf buf = void; assert(0.unsignedToTempString(buf) == "0"); assert(1.unsignedToTempString(buf) == "1"); assert(12.unsignedToTempString(buf) == "12"); assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf"); + assert(0x12ABCF .unsignedToTempString!(16, true)(buf) == "12ABCF"); assert(long.sizeof.unsignedToTempString(buf) == "8"); assert(uint.max.unsignedToTempString(buf) == "4294967295"); assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615"); @@ -106,16 +138,17 @@ unittest // test bad radices assert(!is(typeof(100.unsignedToTempString!1(buf)))); assert(!is(typeof(100.unsignedToTempString!0(buf) == ""))); + assert(!is(typeof(100.unsignedToTempString!37(buf) == ""))); } -alias SignedStringBuf = char[20]; +alias SignedStringBuf = char[65]; -char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe +T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, return scope T[] buf) { bool neg = value < 0; if (neg) value = cast(ulong)-value; - auto r = unsignedToTempString!radix(value, buf); + auto r = unsignedToTempString!(radix, upperCase)(value, buf); if (neg) { // about to do a slice without a bounds check @@ -126,7 +159,7 @@ char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) return r; } -auto signedToTempString(uint radix = 10)(long value) @safe +auto signedToTempString(uint radix = 10)(long value) { bool neg = value < 0; if (neg) @@ -142,7 +175,7 @@ auto signedToTempString(uint radix = 10)(long value) @safe unittest { - SignedStringBuf buf; + SignedStringBuf buf = void; assert(0.signedToTempString(buf) == "0"); assert(1.signedToTempString(buf) == "1"); assert((-1).signedToTempString(buf) == "-1"); @@ -150,6 +183,7 @@ unittest assert((-12).signedToTempString(buf) == "-12"); assert(0x12ABCF .signedToTempString!16(buf) == "12abcf"); assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf"); + assert((-0x12ABCF) .signedToTempString!(16, true)(buf) == "-12ABCF"); assert(long.sizeof.signedToTempString(buf) == "8"); assert(int.max.signedToTempString(buf) == "2147483647"); assert(int.min.signedToTempString(buf) == "-2147483648"); @@ -183,7 +217,7 @@ unittest * Returns: * number of digits */ -int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36) +int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36) { int n = 1; while (1) From dc465cc42d691a0d0430dc30ca9e875dea2ab3f5 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 25 Apr 2023 12:57:41 -0700 Subject: [PATCH 129/301] fix Issue 23055 - importC: using compound-literal array as pointer in CTFE gives 'dereference of invalid pointer' (dlang/dmd!15121) * fix Issue 23055 - importC: using compound-literal array as pointer in CTFE gives 'dereference of invalid pointer' * fix Issue 23055 - importC: using compound-literal array as pointer in CTFE gives 'dereference of invalid pointer' --- dmd/dinterpret.d | 35 ++++++++++++++++++++++++++------ dmd/printast.d | 19 +++++++++++++++++ tests/dmd/runnable/test22070_2.c | 20 +++++++++++++++++- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index 255a8f2826e..f7b9c0e4a16 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -2441,7 +2441,7 @@ public: { debug (LOG) { - printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s ArrayLiteralExp::interpret() %s, %s\n", e.loc.toChars(), e.type.toChars(), e.toChars()); } if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements { @@ -2449,7 +2449,8 @@ public: return; } - Type tn = e.type.toBasetype().nextOf().toBasetype(); + Type tb = e.type.toBasetype(); + Type tn = tb.nextOf().toBasetype(); bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct); auto basis = interpretRegion(e.basis, istate); @@ -2458,6 +2459,7 @@ public: auto expsx = e.elements; size_t dim = expsx ? expsx.length : 0; + for (size_t i = 0; i < dim; i++) { Expression exp = (*expsx)[i]; @@ -3971,6 +3973,8 @@ public: */ private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment) { + //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); + dinteger_t lowerbound; dinteger_t upperbound; dinteger_t firstIndex; @@ -4030,7 +4034,7 @@ public: return newval; // For slice assignment, we check that the lengths match. - if (!isBlockAssignment) + if (!isBlockAssignment && e1.type.ty != Tpointer) { const srclen = resolveArrayLength(newval); if (srclen != (upperbound - lowerbound)) @@ -4245,8 +4249,8 @@ public: Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) { Expressions* w = ae.elements; - assert(ae.type.ty == Tsarray || ae.type.ty == Tarray); - bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type); + assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); + bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); for (size_t k = lwr; k < upr; k++) { if (!directblk && (*w)[k].op == EXP.arrayLiteral) @@ -6116,10 +6120,13 @@ public: override void visit(PtrExp e) { + // Called for both lvalues and rvalues + const lvalue = goal == CTFEGoal.LValue; debug (LOG) { - printf("%s PtrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s PtrExp::interpret(%d) %s, %s\n", e.loc.toChars(), lvalue, e.type.toChars(), e.toChars()); } + // Check for int<->float and long<->double casts. if (auto soe1 = e.e1.isSymOffExp()) if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type)) @@ -6177,6 +6184,20 @@ public: return; } + if (!lvalue && result.isArrayLiteralExp() && + result.type.isTypePointer()) + { + /* A pointer variable can point to an array literal like `[3]`. + * Dereferencing it means accessing the first element value. + * Dereference it only if result should be an rvalue + */ + auto ae = result.isArrayLiteralExp(); + if (ae.elements.length == 1) + { + result = (*ae.elements)[0]; + return; + } + } if (result.isStringExp() || result.isArrayLiteralExp()) return; @@ -6484,10 +6505,12 @@ Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal g { if (!e) return null; + //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars()); scope Interpreter v = new Interpreter(pue, istate, goal); e.accept(v); Expression ex = v.result; assert(goal == CTFEGoal.Nothing || ex !is null); + //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n"); return ex; } diff --git a/dmd/printast.d b/dmd/printast.d index d85105d6f20..8c0109524f7 100644 --- a/dmd/printast.d +++ b/dmd/printast.d @@ -219,6 +219,25 @@ extern (C++) final class PrintASTVisitor : Visitor printAST(e.value, indent + 2); } + override void visit(ArrayLiteralExp e) + { + visit(cast(Expression)e); + printIndent(indent + 2); + printf(".basis : %s\n", e.basis ? e.basis.toChars() : ""); + if (e.elements) + { + printIndent(indent + 2); + printf("["); + foreach (i, element; (*e.elements)[]) + { + if (i) + printf(", "); + printf("%s", element.toChars()); + } + printf("]\n"); + } + } + static void printIndent(int indent) { foreach (i; 0 .. indent) diff --git a/tests/dmd/runnable/test22070_2.c b/tests/dmd/runnable/test22070_2.c index 7263202c5a3..f26c70cc6d6 100644 --- a/tests/dmd/runnable/test22070_2.c +++ b/tests/dmd/runnable/test22070_2.c @@ -51,5 +51,23 @@ int main() return 1; if (test4() != '5') return 1; - return 0; + return test23055(); +} + +// https://issues.dlang.org/show_bug?id=23055 + +int *px = (int[1]){0}; + +int fn() +{ + int *p = (int[1]){0}; + *p = 7; + return *p; +} + +_Static_assert(fn() == 7, ""); + +int test23055() +{ + return (fn() != 7); } From 093cfa6780d26a144e7f71528e90b5b7b95bb8e5 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 26 Apr 2023 11:36:53 +0300 Subject: [PATCH 130/301] Fix Issue 19706 - Attribute inference in struct fails (dlang/dmd!15129) --- dmd/traits.d | 13 +++++++++++++ .../dmd/compilable/traits_getFunctionAttributes.d | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/dmd/traits.d b/dmd/traits.d index da56c88e540..0f363536d8e 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -1276,6 +1276,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return ErrorExp.get(); } + // https://issues.dlang.org/show_bug.cgi?id=19706 + // When getting the attributes of the instance of a + // templated member function semantic tiargs does + // not perform semantic3 on the instance. + // For more information see FuncDeclaration.functionSemantic. + // For getFunctionAttributes it is mandatory to do + // attribute inference. + if (fd && fd.parent && fd.parent.isTemplateInstance) + { + fd.functionSemantic3(); + tf = cast(TypeFunction)fd.type; + } + auto mods = new Expressions(); void addToMods(string str) diff --git a/tests/dmd/compilable/traits_getFunctionAttributes.d b/tests/dmd/compilable/traits_getFunctionAttributes.d index 1f25b269053..f4defb48095 100644 --- a/tests/dmd/compilable/traits_getFunctionAttributes.d +++ b/tests/dmd/compilable/traits_getFunctionAttributes.d @@ -1,9 +1,10 @@ module traits_getFunctionAttributes; +alias tuple(T...) = T; + void test_getFunctionAttributes() { - alias tuple(T...) = T; struct S { @@ -118,3 +119,14 @@ void test_getFunctionAttributes() static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system")); static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system")); } + +void bug19706() +{ + struct S + { + static int fImpl(Ret)() { return Ret.init; } + + // tells us: `fImpl!int` is @system + static assert(__traits(getFunctionAttributes, fImpl!int) == tuple!("pure", "nothrow", "@nogc", "@safe")); + } +} From ff9a3fa0ef52d5cda30c0f8bffb60cf2dfec8ef5 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 26 Apr 2023 01:39:57 -0700 Subject: [PATCH 131/301] fix Issue 23837 - importc fails to link on windows x86 but successes on x64 (dlang/dmd!15126) --- dmd/dstruct.d | 13 +++++++++---- tests/dmd/runnable/imports/mainx23837.c | 10 ++++++++++ tests/dmd/runnable/test23837.d | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/runnable/imports/mainx23837.c create mode 100644 tests/dmd/runnable/test23837.d diff --git a/dmd/dstruct.d b/dmd/dstruct.d index 67f29d28970..49b98411ad3 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -13,6 +13,8 @@ module dmd.dstruct; +import core.stdc.stdio; + import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; @@ -567,7 +569,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration * Returns: * true if it's all binary 0 */ -private bool _isZeroInit(Expression exp) +bool _isZeroInit(Expression exp) { switch (exp.op) { @@ -579,15 +581,18 @@ private bool _isZeroInit(Expression exp) case EXP.structLiteral: { - auto sle = cast(StructLiteralExp) exp; + auto sle = exp.isStructLiteralExp(); + if (sle.sd.isNested()) + return false; + const isCstruct = sle.sd.isCsymbol(); // C structs are default initialized to all zeros foreach (i; 0 .. sle.sd.fields.length) { auto field = sle.sd.fields[i]; if (field.type.size(field.loc)) { - auto e = (*sle.elements)[i]; + auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null; if (e ? !_isZeroInit(e) - : !field.type.isZeroInit(field.loc)) + : !isCstruct && !field.type.isZeroInit(field.loc)) return false; } } diff --git a/tests/dmd/runnable/imports/mainx23837.c b/tests/dmd/runnable/imports/mainx23837.c new file mode 100644 index 00000000000..0c446ab6ad1 --- /dev/null +++ b/tests/dmd/runnable/imports/mainx23837.c @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug?id=23837 + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; +}; diff --git a/tests/dmd/runnable/test23837.d b/tests/dmd/runnable/test23837.d new file mode 100644 index 00000000000..c0c86683ee7 --- /dev/null +++ b/tests/dmd/runnable/test23837.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23837 + +import imports.mainx23837; + +struct TexturePacker +{ + stbrp_context _context; +} + +int main() +{ + auto res = TexturePacker(); + return 0; +} From b8dc6bbf333975f3cd81ecf08ac2a06ec60a5db6 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 26 Apr 2023 01:58:59 -0700 Subject: [PATCH 132/301] fix Issue 23862 - with statement should accept an expression with enum type (dlang/dmd!15131) --- dmd/statementsem.d | 15 ++++++++++++--- tests/dmd/compilable/test23862.d | 15 +++++++++++++++ tests/dmd/fail_compilation/diag9312.d | 2 +- tests/dmd/fail_compilation/fail4375q.d | 2 +- 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 tests/dmd/compilable/test23862.d diff --git a/dmd/statementsem.d b/dmd/statementsem.d index d1231dd3c13..824a4ad5e55 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -3166,14 +3166,16 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else { - Type t = ws.exp.type.toBasetype(); + Type texp = ws.exp.type; + Type t = texp.toBasetype(); Expression olde = ws.exp; if (t.ty == Tpointer) { ws.exp = new PtrExp(ws.loc, ws.exp); ws.exp = ws.exp.expressionSemantic(sc); - t = ws.exp.type.toBasetype(); + texp = ws.exp.type; + t = texp.toBasetype(); } assert(t); @@ -3221,9 +3223,16 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sym.parent = sc.scopesym; sym.endlinnum = ws.endloc.linnum; } + else if (auto tenum = texp.isTypeEnum()) + { + ws.exp = new TypeExp(ws.exp.loc, tenum); + sym = new WithScopeSymbol(ws); + sym.parent = sc.scopesym; + sym.endlinnum = ws.endloc.linnum; + } else { - ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars()); + ws.error("`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars()); return setError(); } } diff --git a/tests/dmd/compilable/test23862.d b/tests/dmd/compilable/test23862.d new file mode 100644 index 00000000000..c5b063b2784 --- /dev/null +++ b/tests/dmd/compilable/test23862.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23862 + +enum E { A, B } + +void test(E e) +{ + with (e) + switch (e) + { + case A: + case B: + default: + break; + } +} diff --git a/tests/dmd/fail_compilation/diag9312.d b/tests/dmd/fail_compilation/diag9312.d index 94e3d3ffd43..98308133e02 100644 --- a/tests/dmd/fail_compilation/diag9312.d +++ b/tests/dmd/fail_compilation/diag9312.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9312.d(10): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/diag9312.d(10): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ diff --git a/tests/dmd/fail_compilation/fail4375q.d b/tests/dmd/fail_compilation/fail4375q.d index b02fbb169b7..f57e746b6ed 100644 --- a/tests/dmd/fail_compilation/fail4375q.d +++ b/tests/dmd/fail_compilation/fail4375q.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13) -fail_compilation/fail4375q.d(14): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ From 599b10730aac9df111a7303c047a5dbd68a9c48e Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 26 Apr 2023 16:15:45 +0300 Subject: [PATCH 133/301] Fix Issue 23861 - Compiler segmentation fault with ref and alias this (dlang/dmd!15133) --- dmd/expressionsem.d | 18 ++++++++++++++++-- tests/dmd/fail_compilation/fail23861.d | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/fail23861.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index e3163ee58f5..e34f73b7cdb 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -1220,7 +1220,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -1294,7 +1294,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = { Expression e = new CallExp(loc, e1); if (e2) + { e = new AssignExp(loc, e, e2); + if (saveAtts) + { + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; + } + } return e.expressionSemantic(sc); } } @@ -1412,7 +1419,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = } Expression e = new CallExp(loc, e1); if (e2) + { e = new AssignExp(loc, e, e2); + if (saveAtts) + { + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; + } + } return e.expressionSemantic(sc); } } @@ -9092,7 +9106,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) return setResult(e); if (e1x.checkRightThis(sc)) diff --git a/tests/dmd/fail_compilation/fail23861.d b/tests/dmd/fail_compilation/fail23861.d new file mode 100644 index 00000000000..23c540799e6 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23861.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23861 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23861.d(24): Error: cannot implicitly convert expression `3` of type `int` to `Foo` +--- +*/ + +Foo global; + +struct Foo +{ + ref Foo get() + { + return global; + } + alias get this; +} + +void main() +{ + Foo g; + g = 3; +} From 61b219be91d7f74f32713b743db6fef99c043d9d Mon Sep 17 00:00:00 2001 From: Max Haughton Date: Thu, 27 Apr 2023 17:21:58 +0100 Subject: [PATCH 134/301] Fix Issue 23846 - Respect the C standard when it comes to string to float ERRNO and return value --- dmd/root/port.d | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/dmd/root/port.d b/dmd/root/port.d index ac53b7d385f..576b0550ca4 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -70,7 +70,21 @@ extern (C++) struct Port return t; } - + static bool resultOutOfRange(FloatingType)(const FloatingType x, const int errnoValue) + { + import core.stdc.math : HUGE_VAL, HUGE_VALF; + static if (is(FloatingType == double)) + const FloatingType hugeVal = HUGE_VAL; + else static if (is(FloatingType == float)) + const FloatingType hugeVal = HUGE_VALF; + else static assert(0, "This function does not support " ~ FloatingType); + + if (errnoValue == ERANGE) + { + return x == hugeVal || x == 0.0f; + } + return false; + } static bool isFloat32LiteralOutOfRange(scope const(char)* s) { errno = 0; @@ -85,13 +99,14 @@ extern (C++) struct Port int res = _atoflt(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + version (CRuntime_DigitalMars) __locale_decpoint = save; + return errno == ERANGE; } else { - strtof(s, null); + const result = strtof(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } static bool isFloat64LiteralOutOfRange(scope const(char)* s) @@ -108,13 +123,14 @@ extern (C++) struct Port int res = _atodbl(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + version (CRuntime_DigitalMars) __locale_decpoint = save; + return errno == ERANGE; } else { - strtod(s, null); + const result = strtod(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } // Little endian From 13709425134c70fe7b232adf96178a59d597cbc0 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 27 Apr 2023 12:05:56 -0700 Subject: [PATCH 135/301] ImportC: support __attribute__((always_inline)) (dlang/dmd!15134) --- dmd/cparse.d | 5 +++++ dmd/frontend.h | 1 + dmd/id.d | 1 + tests/dmd/compilable/always_inline.i | 5 +++++ 4 files changed, 12 insertions(+) create mode 100644 tests/dmd/compilable/always_inline.i diff --git a/dmd/cparse.d b/dmd/cparse.d index a9dddac7aad..b9cf0759040 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3486,6 +3486,11 @@ final class CParser(AST) : Parser!AST * type on the target machine. It's the opposite of __attribute__((packed)) */ } + else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html + { + specifier.scw |= SCW.xinline; + nextToken(); + } else if (token.ident == Id._deprecated) { specifier._deprecated = true; diff --git a/dmd/frontend.h b/dmd/frontend.h index 275eb9d1173..b4d2ea5d6b8 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8877,6 +8877,7 @@ struct Id final static Identifier* naked; static Identifier* thread; static Identifier* vector_size; + static Identifier* always_inline; static Identifier* noinline; static Identifier* noreturn; static Identifier* _deprecated; diff --git a/dmd/id.d b/dmd/id.d index bffad0b7bd6..86f8d5329f9 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -527,6 +527,7 @@ immutable Msgtable[] msgtable = { "thread" }, { "vector_size" }, { "__func__" }, + { "always_inline" }, { "noinline" }, { "noreturn" }, { "_deprecated", "deprecated" }, diff --git a/tests/dmd/compilable/always_inline.i b/tests/dmd/compilable/always_inline.i new file mode 100644 index 00000000000..d7b4b0814b1 --- /dev/null +++ b/tests/dmd/compilable/always_inline.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug?id=21938 + +__attribute__((always_inline)) int square(int x) { return x * x; } + +int doSquare(int x) { return square(x); } From 1a9c5ce94f6934616197329112c9e1bce7429ea0 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 27 Apr 2023 12:07:01 -0700 Subject: [PATCH 136/301] ImportC: add __declspec and __attribute__ nothrow (dlang/dmd!15135) --- dmd/canthrow.d | 2 +- dmd/cparse.d | 15 +++++++++++- dmd/frontend.h | 1 + dmd/id.d | 1 + tests/dmd/fail_compilation/testnothrow.c | 31 ++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/testnothrow.c diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 79f1760bae5..7dfec8a043a 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -53,7 +53,7 @@ enum CT : BE */ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) { - //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); + //printf("Expression::canThrow(%d) %s\n", mustNotThrow, e.toChars()); // stop walking if we determine this expression can throw extern (C++) final class CanThrow : StoppableVisitor { diff --git a/dmd/cparse.d b/dmd/cparse.d index b9cf0759040..03762d671b8 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2892,7 +2892,8 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; - AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, 0); + StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); // tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -3176,6 +3177,7 @@ final class CParser(AST) : Parser!AST * naked * noinline * noreturn + * nothrow * thread * Params: * specifier = filled in with the attribute(s) @@ -3224,6 +3226,11 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } else if (token.ident == Id.thread) { specifier.scw |= SCW.x_Thread_local; @@ -3527,6 +3534,11 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } else if (token.ident == Id.vector_size) { nextToken(); @@ -4931,6 +4943,7 @@ final class CParser(AST) : Parser!AST { bool noreturn; /// noreturn attribute bool naked; /// naked attribute + bool _nothrow; /// nothrow attribute bool dllimport; /// dllimport attribute bool dllexport; /// dllexport attribute bool _deprecated; /// deprecated attribute diff --git a/dmd/frontend.h b/dmd/frontend.h index b4d2ea5d6b8..3b398de5936 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8880,6 +8880,7 @@ struct Id final static Identifier* always_inline; static Identifier* noinline; static Identifier* noreturn; + static Identifier* _nothrow; static Identifier* _deprecated; static Identifier* _align; static Identifier* aligned; diff --git a/dmd/id.d b/dmd/id.d index 86f8d5329f9..8ea9dc0b6de 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -530,6 +530,7 @@ immutable Msgtable[] msgtable = { "always_inline" }, { "noinline" }, { "noreturn" }, + { "_nothrow", "nothrow" }, { "_deprecated", "deprecated" }, { "_align", "align" }, { "aligned" }, diff --git a/tests/dmd/fail_compilation/testnothrow.c b/tests/dmd/fail_compilation/testnothrow.c new file mode 100644 index 00000000000..538e2c174bd --- /dev/null +++ b/tests/dmd/fail_compilation/testnothrow.c @@ -0,0 +1,31 @@ +/* TEST_OUTPUT: +--- +fail_compilation/testnothrow.c(105): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(104): Error: function `testnothrow.mul` may throw but is marked as `nothrow` +fail_compilation/testnothrow.c(111): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(110): Error: function `testnothrow.add` may throw but is marked as `nothrow` +--- +*/ + +// https://issues.dlang.org/show_bug?id=21938 + +#line 100 + +void throwing() { } + +__attribute__((nothrow)) int mul(int x) +{ + throwing(); + return x * x; +} + +__declspec(nothrow) int add(int x) +{ + throwing(); + return x + x; +} + +int doSquare(int x) +{ + return mul(x) + add(x); +} From cf556edad687e08172875ab0612b28c10397f122 Mon Sep 17 00:00:00 2001 From: Teodor Dutu Date: Fri, 28 Apr 2023 10:33:48 +0300 Subject: [PATCH 137/301] Translate `_d_arraycatnTX` to a template (dlang/dmd!14550) This brings the following changes: - Improves the existing template `_d_arraycatnTX`, to now concatenates both arrays and single elements - Changes the lowerings to `_d_arraycatT` to use the new template - Moves the lowering logic to `_d_arraycatnTX` to expressionsem.d - Adds a new field to `CatExp` called `lowering` to store the template lowering - Removes the old non-template `_d_arraycatnTX` and `_d_arraycatT` hooks - Moves `test19688.d` from `runnable/` to `compilable/` until https://issues.dlang.org/show_bug.cgi?id=23408 is fixed Signed-off-by: Teodor Dutu --- dmd/dinterpret.d | 61 ++++-- dmd/expression.d | 4 +- dmd/expression.h | 2 + dmd/expressionsem.d | 105 ++++++++-- dmd/frontend.h | 3 + dmd/id.d | 2 + dmd/inline.d | 27 +++ .../src/core/internal/array/concatenation.d | 187 ++++++++++++++---- runtime/druntime/src/object.d | 5 +- runtime/druntime/src/rt/lifetime.d | 142 ------------- runtime/druntime/src/rt/tracegc.d | 2 - .../dmd/{runnable => compilable}/test19688.d | 0 .../extra-files/hello-profile-postscript.sh | 4 +- tests/dmd/unit/lexer/location_offset.d | 2 +- 14 files changed, 321 insertions(+), 225 deletions(-) rename tests/dmd/{runnable => compilable}/test19688.d (100%) diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index f7b9c0e4a16..4ef6a392073 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -5754,7 +5754,25 @@ public: e2 = ue2.copy(); } - *pue = ctfeCat(e.loc, e.type, e1, e2); + Expression prepareCatOperand(Expression exp) + { + /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an + * array. This is needed because interpreting the `CatExp` calls + * `Cat()`, which cannot handle concatenations between different + * types, except for strings and chars. + */ + auto tb = e.type.toBasetype(); + auto tbNext = tb.nextOf(); + auto expTb = exp.type.toBasetype(); + + if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && + (tb.ty == Tarray || tb.ty == Tsarray) && + (expTb.ty == Tarray || expTb.ty == Tsarray)) + return new ArrayLiteralExp(exp.loc, e.type, exp); + return exp; + } + + *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2)); result = pue.exp(); if (CTFEExp.isCantExp(result)) @@ -6463,33 +6481,34 @@ void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, In } } - /********************************************* - * Checks if the given expresion is a call to the runtime hook `id`. - * Params: - * e = the expression to check - * id = the identifier of the runtime hook - * Returns: - * `e` cast to `CallExp` if it's the hook, `null` otherwise - */ - private CallExp isRuntimeHook(Expression e, Identifier id) +/********************************************* + * Checks if the given expresion is a call to the runtime hook `id`. + * + * Params: + * e = the expression to check + * id = the identifier of the runtime hook + * Returns: + * `e` cast to `CallExp` if it's the hook, `null` otherwise + */ +public CallExp isRuntimeHook(Expression e, Identifier id) +{ + if (auto ce = e.isCallExp()) { - if (auto ce = e.isCallExp()) + if (auto ve = ce.e1.isVarExp()) { - if (auto ve = ce.e1.isVarExp()) + if (auto fd = ve.var.isFuncDeclaration()) { - if (auto fd = ve.var.isFuncDeclaration()) - { - // If `_d_HookTraceImpl` is found, resolve the underlying - // hook and replace `e` and `fd` with it. - removeHookTraceImpl(ce, fd); - return fd.ident == id ? ce : null; - } + // If `_d_HookTraceImpl` is found, resolve the underlying hook + // and replace `e` and `fd` with it. + removeHookTraceImpl(ce, fd); + return fd.ident == id ? ce : null; } } - - return null; } + return null; +} + /******************************************** * Interpret the expression. * Params: diff --git a/dmd/expression.d b/dmd/expression.d index 4226709220e..067d22fe130 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -1542,7 +1542,7 @@ extern (C++) abstract class Expression : ASTNode // so don't print anything to avoid double error messages. if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_newclassT)) + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) { error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); @@ -6555,6 +6555,8 @@ extern (C++) final class MinExp : BinExp */ extern (C++) final class CatExp : BinExp { + Expression lowering; // call to druntime hook `_d_arraycatnTX` + extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope { super(loc, EXP.concatenate, e1, e2); diff --git a/dmd/expression.h b/dmd/expression.h index 1c38ec56fd4..a4b18b9fe9e 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -1207,6 +1207,8 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression *lowering; // call to druntime hook `_d_arraycatnTX` + void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index e34f73b7cdb..d288c05cc83 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -2190,7 +2190,11 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. - else if (!(pStc & STC.lazy_)) + // The arguments of `_d_arraycatnTX` are already handled in + // expressionsem.d, via `checkNewEscape`. Without `-dip1000`, the + // check does not return an error, so the lowering of `a ~ b` to + // `_d_arraycatnTX(a, b)` still occurs. + else if (!(pStc & STC.lazy_) && (!fd || fd.ident != Id._d_arraycatnTX)) { /* Argument value can escape from the called function. * Check arg to see if it matters. @@ -2221,6 +2225,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, // allocate the array literal as temporary static array on the stack ale.type = ale.type.nextOf().sarrayOf(ale.elements.length); auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale); + tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type.substWildTo(MODFlags.mutable)); @@ -10945,6 +10950,86 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + /** + * If the given expression is a `CatExp`, the function tries to lower it to + * `_d_arraycatnTX`. + * + * Params: + * ee = the `CatExp` to lower + * Returns: + * `_d_arraycatnTX(e1, e2, ..., en)` if `ee` is `e1 ~ e2 ~ ... en` + * `ee` otherwise + */ + private Expression lowerToArrayCat(CatExp exp) + { + // String literals are concatenated by the compiler. No lowering is needed. + if ((exp.e1.isStringExp() && (exp.e2.isIntegerExp() || exp.e2.isStringExp())) || + (exp.e2.isStringExp() && (exp.e1.isIntegerExp() || exp.e1.isStringExp()))) + return exp; + + Identifier hook = global.params.tracegc ? Id._d_arraycatnTXTrace : Id._d_arraycatnTX; + if (!verifyHookExist(exp.loc, *sc, hook, "concatenating arrays")) + { + setError(); + return result; + } + + void handleCatArgument(Expressions *arguments, Expression e) + { + if (auto ce = e.isCatExp()) + { + Expression lowering = ce.lowering; + + /* Skip `file`, `line`, and `funcname` if the hook of the parent + * `CatExp` is `_d_arraycatnTXTrace`. + */ + if (auto callExp = isRuntimeHook(lowering, hook)) + { + if (hook == Id._d_arraycatnTX) + arguments.pushSlice((*callExp.arguments)[]); + else + arguments.pushSlice((*callExp.arguments)[3 .. $]); + } + } + else + arguments.push(e); + } + + auto arguments = new Expressions(); + if (global.params.tracegc) + { + auto funcname = (sc.callsc && sc.callsc.func) ? + sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); + arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); + arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); + arguments.push(new StringExp(exp.loc, funcname.toDString())); + } + + handleCatArgument(arguments, exp.e1); + handleCatArgument(arguments, exp.e2); + + Expression id = new IdentifierExp(exp.loc, Id.empty); + id = new DotIdExp(exp.loc, id, Id.object); + + auto tiargs = new Objects(); + tiargs.push(exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); + id = new CallExp(exp.loc, id, arguments); + return id.expressionSemantic(sc); + } + + void trySetCatExpLowering(Expression exp) + { + /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be + * used with `-betterC`, but only during CTFE. + */ + if (global.params.betterC) + return; + + if (auto ce = exp.isCatExp()) + ce.lowering = lowerToArrayCat(ce); + } + override void visit(CatExp exp) { // https://dlang.org/spec/expression.html#cat_expressions @@ -11032,14 +11117,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (tb2.ty == Tarray || tb2.ty == Tsarray) - { - // Make e2 into [e2] - exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2); - } - else if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11070,14 +11151,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (tb1.ty == Tarray || tb1.ty == Tsarray) - { - // Make e1 into [e1] - exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1); - } - else if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11100,6 +11177,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (Expression ex = typeCombine(exp, sc)) { result = ex; + trySetCatExpLowering(result); return; } exp.type = exp.type.toHeadMutable(); @@ -11132,6 +11210,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } result = e; + trySetCatExpLowering(result); } override void visit(MulExp exp) diff --git a/dmd/frontend.h b/dmd/frontend.h index 3b398de5936..0d734561c0d 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -7876,6 +7876,7 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression lowering; Expression* resolveLoc(const Loc& loc, Scope* sc) override; void accept(Visitor* v) override; }; @@ -8735,6 +8736,8 @@ struct Id final static Identifier* _d_arrayappendcTXImpl; static Identifier* _d_arrayappendcTX; static Identifier* _d_arrayappendcTXTrace; + static Identifier* _d_arraycatnTX; + static Identifier* _d_arraycatnTXTrace; static Identifier* stdc; static Identifier* stdarg; static Identifier* va_start; diff --git a/dmd/id.d b/dmd/id.d index 8ea9dc0b6de..2a2703b7970 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -361,6 +361,8 @@ immutable Msgtable[] msgtable = { "_d_arrayappendcTXImpl" }, { "_d_arrayappendcTX" }, { "_d_arrayappendcTXTrace" }, + { "_d_arraycatnTX" }, + { "_d_arraycatnTXTrace" }, // varargs implementation { "stdc" }, diff --git a/dmd/inline.d b/dmd/inline.d index 4e0b0a86531..2989b6e39e7 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -747,6 +747,21 @@ public: result = ae; } + override void visit(CatExp e) + { + auto ce = e.copy().isCatExp(); + + if (auto lowering = ce.lowering) + ce.lowering = doInlineAs!Expression(lowering, ids); + else + { + ce.e1 = doInlineAs!Expression(e.e1, ids); + ce.e2 = doInlineAs!Expression(e.e2, ids); + } + + result = ce; + } + override void visit(BinExp e) { auto be = cast(BinExp)e.copy(); @@ -1241,6 +1256,18 @@ public: inlineScan(e.msg); } + override void visit(CatExp e) + { + if (auto lowering = e.lowering) + { + inlineScan(lowering); + return; + } + + inlineScan(e.e1); + inlineScan(e.e2); + } + override void visit(BinExp e) { inlineScan(e.e1); diff --git a/runtime/druntime/src/core/internal/array/concatenation.d b/runtime/druntime/src/core/internal/array/concatenation.d index 99f33da7683..ff777a6b3ab 100644 --- a/runtime/druntime/src/core/internal/array/concatenation.d +++ b/runtime/druntime/src/core/internal/array/concatenation.d @@ -8,71 +8,172 @@ */ module core.internal.array.concatenation; -/// See $(REF _d_arraycatnTX, rt,lifetime) -private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow; - -/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace` -template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) +/** + * Concatenate the arrays inside of `froms`. + * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`. + * + * Params: + * froms = Arrays to be concatenated. + * Returns: + * A newly allocated array that contains all the elements from `froms`. + */ +Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { - private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!"; + import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.lifetime : copyEmplace; + import core.stdc.string : memcpy; - /** - * Concatenating the arrays inside of `arrs`. - * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`. - * Params: - * arrs = Array containing arrays that will be concatenated. - * Returns: - * A newly allocated array that contains all the elements from all the arrays in `arrs`. - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow + Tret res; + size_t totalLen; + + alias T = typeof(res[0]); + enum elemSize = T.sizeof; + enum hasPostblit = __traits(hasPostblit, T); + + static foreach (from; froms) + static if (is (typeof(from) : T)) + totalLen++; + else + totalLen += from.length; + + if (totalLen == 0) + return res; + res.length = totalLen; + + /* Currently, if both a postblit and a cpctor are defined, the postblit is + * used. If this changes, the condition below will have to be adapted. + */ + static if (hasElaborateCopyConstructor!T && !hasPostblit) { - pragma(inline, false); - version (D_TypeInfo) - { - auto ti = typeid(ResultArrT); + size_t i = 0; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + copyEmplace(cast(T) from, res[i++]); + else + { + if (from.length) + foreach (ref elem; from) + copyEmplace(cast(T) elem, res[i++]); + } + } + else + { + auto resptr = cast(Unqual!T *) res; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + memcpy(resptr++, cast(Unqual!T *) &from, elemSize); + else + { + const len = from.length; + if (len) + { + memcpy(resptr, cast(Unqual!T *) from, len * elemSize); + resptr += len; + } + } + + static if (hasPostblit) + foreach (ref elem; res) + (cast() elem).__xpostblit(); + } + + return res; +} - byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length]; - void[] result = ._d_arraycatnTX(ti, arrs2); - return (cast(T*)result.ptr)[0 .. result.length]; +// postblit +@safe unittest +{ + int counter; + struct S + { + int val; + this(this) + { + counter++; } - else - assert(0, errorMessage); } - version (D_ProfileGC) + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = []; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem); + + assert(counter == 7); + assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S { - import core.internal.array.utils : _d_HookTraceImpl; - - /** - * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat). - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage); + int val; + this(ref return scope S rhs) + { + val = rhs.val; + counter++; + } } + + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3); + + assert(counter == 10); + assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]); } +// throwing @safe unittest { int counter; + bool didThrow; struct S { int val; this(this) { counter++; + if (counter == 4) + throw new Exception(""); } } - S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]]; - S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr); + try + { + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + _d_arraycatnTX!(S[])(arr1, arr2); + } + catch (Exception) + { + didThrow = true; + } + + assert(counter == 4); + assert(didThrow); +} + +version (D_ProfileGC) +{ + /** + * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation). + */ + Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted + { + version (D_TypeInfo) + { + import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure; + mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX")); - assert(counter == 8); - assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]); + import core.lifetime: forward; + return _d_arraycatnTX!Tret(forward!froms); + } + else + assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); + } } diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index a77788bec20..610cb7a30a1 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -4529,12 +4529,15 @@ public import core.internal.entrypoint : _d_cmain; public import core.internal.array.appending : _d_arrayappendT; version (D_ProfileGC) +{ public import core.internal.array.appending : _d_arrayappendTTrace; + public import core.internal.array.concatenation : _d_arraycatnTXTrace; +} public import core.internal.array.appending : _d_arrayappendcTXImpl; public import core.internal.array.comparison : __cmp; public import core.internal.array.equality : __equals; public import core.internal.array.casting: __ArrayCast; -public import core.internal.array.concatenation : _d_arraycatnTXImpl; +public import core.internal.array.concatenation : _d_arraycatnTX; public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.arrayassign : _d_arrayassign_l; diff --git a/runtime/druntime/src/rt/lifetime.d b/runtime/druntime/src/rt/lifetime.d index c5ca8f46973..a37541bf01f 100644 --- a/runtime/druntime/src/rt/lifetime.d +++ b/runtime/druntime/src/rt/lifetime.d @@ -2231,148 +2231,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak return x; } - -/** -Concatenate two arrays into a new array - ---- -void main() -{ - int[] x = [10, 20, 30]; - int[] y = [40, 50]; - int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); -} ---- - -Params: - ti = type that the two arrays share - x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length - y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length -Returns: - resulting concatenated array, with `.length` equal to new element length despite `byte` type -*/ -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak -out (result) -{ - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr); - assert(result.length == x.length + y.length); - - // If a postblit is involved, the contents of result might rightly differ - // from the bitwise concatenation of x and y. - if (!hasPostblit(tinext)) - { - for (size_t i = 0; i < x.length * sizeelem; i++) - assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); - for (size_t i = 0; i < y.length * sizeelem; i++) - assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); - } - - size_t cap = GC.sizeOf(result.ptr); - assert(!cap || cap > result.length * sizeelem); -} -do -{ - import core.stdc.string; - version (none) - { - /* Cannot use this optimization because: - * char[] a, b; - * char c = 'a'; - * b = a ~ c; - * c = 'b'; - * will change the contents of b. - */ - if (!y.length) - return x; - if (!x.length) - return y; - } - - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); - size_t xlen = x.length * sizeelem; - size_t ylen = y.length * sizeelem; - size_t len = xlen + ylen; - - if (!len) - return null; - - auto info = __arrayAlloc(len, ti, tinext); - byte* p = cast(byte*)__arrayStart(info); - p[len] = 0; // guessing this is to optimize for null-terminated arrays? - memcpy(p, x.ptr, xlen); - memcpy(p + xlen, y.ptr, ylen); - // do postblit processing - __doPostblit(p, xlen + ylen, tinext); - - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, len, isshared, tinext); - return p[0 .. x.length + y.length]; -} - - -/** -Concatenate multiple arrays at once - -This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. - -``` -void main() -{ - int[] a, b, c; - int[] res = a ~ b ~ c; - // _d_arraycatnTX(typeid(int[]), - // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); -} -``` - -Params: - ti = type of arrays to concatenate and resulting array - arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same - -Returns: - newly created concatenated array, `.length` equal to the total element length despite `void` type -*/ -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak -{ - import core.stdc.string; - - size_t length; - auto tinext = unqualify(ti.next); - auto size = tinext.tsize; // array element size - - foreach (b; arrs) - length += b.length; - - if (!length) - return null; - - auto allocsize = length * size; - auto info = __arrayAlloc(allocsize, ti, tinext); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, allocsize, isshared, tinext); - void *a = __arrayStart (info); - - size_t j = 0; - foreach (b; arrs) - { - if (b.length) - { - memcpy(a + j, b.ptr, b.length * size); - j += b.length * size; - } - } - - // do postblit processing - __doPostblit(a, j, tinext); - - return a[0..length]; -} - - /** Allocate an array literal diff --git a/runtime/druntime/src/rt/tracegc.d b/runtime/druntime/src/rt/tracegc.d index c89a358742c..ab65cb9887a 100644 --- a/runtime/druntime/src/rt/tracegc.d +++ b/runtime/druntime/src/rt/tracegc.d @@ -29,8 +29,6 @@ extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); extern (C) void _d_delinterface(void** p); extern (C) void _d_delmemory(void* *p); -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs); extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals); diff --git a/tests/dmd/runnable/test19688.d b/tests/dmd/compilable/test19688.d similarity index 100% rename from tests/dmd/runnable/test19688.d rename to tests/dmd/compilable/test19688.d diff --git a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh index 47c18c08955..cb986e0a4fe 100755 --- a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh +++ b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh @@ -3,7 +3,9 @@ source tools/common_funcs.sh # strip out Dmain since it's symbol differs between windows and non-windows -grep -v Dmain ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 +# strip out _d_arraycatnTX and _d_arraysetlengthT since they are part of the +# lowering of the array concatenation operator +grep -v 'Dmain\|_d_arraycatnTX\|_d_arraysetlengthT' ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 diff -up --strip-trailing-cr ${EXTRA_FILES}/${TEST_NAME}.d.trace.def ${OUTPUT_BASE}.d.trace.def2 diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index 873d9812382..4a950e866f2 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -557,7 +557,7 @@ static foreach (tok; __traits(allMembers, TOK)) @(tests[tok].description) unittest { - const newCode = "first_token " ~ tests[tok].code; + const newCode = "first_token " ~ tests[tok].code ~ '\0'; scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr, null); From 1e0501b5a7b53a0ce8bde398dfea94aae4800f22 Mon Sep 17 00:00:00 2001 From: Atila Neves Date: Fri, 28 Apr 2023 09:44:43 +0100 Subject: [PATCH 138/301] Make core.sync.condition compile with -preview=nosharedaccess (dlang/dmd!15137) --- runtime/druntime/src/core/sync/condition.d | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/runtime/druntime/src/core/sync/condition.d b/runtime/druntime/src/core/sync/condition.d index ddd04ae0576..afcfd744f0a 100644 --- a/runtime/druntime/src/core/sync/condition.d +++ b/runtime/druntime/src/core/sync/condition.d @@ -84,7 +84,8 @@ class Condition /// ditto this( shared Mutex m ) shared nothrow @safe @nogc { - this(m, true); + import core.atomic : atomicLoad; + this(atomicLoad(m), true); } // @@ -117,7 +118,15 @@ class Condition } else version (Posix) { - m_assocMutex = m; + static if (is(Q == shared)) + { + import core.atomic : atomicLoad; + m_assocMutex = atomicLoad(m); + } + else + { + m_assocMutex = m; + } static if ( is( typeof( pthread_condattr_setclock ) ) ) { () @trusted @@ -183,7 +192,8 @@ class Condition /// ditto @property shared(Mutex) mutex() shared { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } // undocumented function for internal use @@ -195,7 +205,8 @@ class Condition // ditto final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } //////////////////////////////////////////////////////////////////////////// From 028ac2d0c12c400d169a77e6c21ca172c5544298 Mon Sep 17 00:00:00 2001 From: Max Haughton Date: Fri, 28 Apr 2023 10:54:05 +0100 Subject: [PATCH 139/301] Update compiler/src/dmd/root/port.d Co-authored-by: Dennis --- dmd/root/port.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/root/port.d b/dmd/root/port.d index 576b0550ca4..a7d08042be4 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -70,7 +70,7 @@ extern (C++) struct Port return t; } - static bool resultOutOfRange(FloatingType)(const FloatingType x, const int errnoValue) + private extern (D) static bool resultOutOfRange(FloatingType)(const FloatingType x, const int errnoValue) { import core.stdc.math : HUGE_VAL, HUGE_VALF; static if (is(FloatingType == double)) From c68f5c696af4dbdb35892885c0d28574d17da1af Mon Sep 17 00:00:00 2001 From: Dennis Date: Fri, 28 Apr 2023 13:18:56 +0200 Subject: [PATCH 140/301] Fix frontend.h (dlang/dmd!15141) --- dmd/frontend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 0d734561c0d..9f5ee1d1fcb 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -7876,7 +7876,7 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: - Expression lowering; + Expression* lowering; Expression* resolveLoc(const Loc& loc, Scope* sc) override; void accept(Visitor* v) override; }; From a4fd23635ebf4d10ab90db0de9c40a466d584df6 Mon Sep 17 00:00:00 2001 From: Max Haughton Date: Fri, 28 Apr 2023 12:56:44 +0100 Subject: [PATCH 141/301] Correct CRuntime_Microsoft --- dmd/root/port.d | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dmd/root/port.d b/dmd/root/port.d index a7d08042be4..01be55febb1 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -92,6 +92,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -99,7 +101,6 @@ extern (C++) struct Port int res = _atoflt(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; - version (CRuntime_DigitalMars) __locale_decpoint = save; return errno == ERANGE; } else @@ -116,6 +117,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -123,7 +126,6 @@ extern (C++) struct Port int res = _atodbl(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; - version (CRuntime_DigitalMars) __locale_decpoint = save; return errno == ERANGE; } else From ac9c9f64e22261cd373326245d20358c5f2b9108 Mon Sep 17 00:00:00 2001 From: Atila Neves Date: Thu, 27 Apr 2023 11:54:04 +0100 Subject: [PATCH 142/301] Make core.demangle compile with -preview=nosharedacccess --- runtime/druntime/src/core/demangle.d | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index e1b81dc29c3..f08e1f86444 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -2764,6 +2764,7 @@ private shared CXX_DEMANGLER __cxa_demangle; CXX_DEMANGLER getCXXDemangler() nothrow @trusted { + import core.atomic : atomicLoad, atomicStore; if (__cxa_demangle is null) version (Posix) { @@ -2777,17 +2778,21 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT; if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle")) - __cxa_demangle = found; + atomicStore(__cxa_demangle, found); } if (__cxa_demangle is null) - __cxa_demangle = (const char* mangled_name, char* output_buffer, - size_t* length, int* status) nothrow pure @trusted { - *status = -1; - return null; - }; + { + static extern(C) char* _(const char* mangled_name, char* output_buffer, + size_t* length, int* status) nothrow pure @trusted + { + *status = -1; + return null; + } + atomicStore(__cxa_demangle, &_); + } - return __cxa_demangle; + return atomicLoad(__cxa_demangle); } /** From e4c06d38fb4ff07265d401ca69494b88e76ac36f Mon Sep 17 00:00:00 2001 From: Atila Neves Date: Thu, 27 Apr 2023 15:06:22 +0100 Subject: [PATCH 143/301] Move unittest causing cyclic dependency --- runtime/druntime/src/core/atomic.d | 36 -------------------- runtime/druntime/src/core/thread/package.d | 39 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/runtime/druntime/src/core/atomic.d b/runtime/druntime/src/core/atomic.d index 5f6f407bc35..1fba06cc021 100644 --- a/runtime/druntime/src/core/atomic.d +++ b/runtime/druntime/src/core/atomic.d @@ -1108,42 +1108,6 @@ version (CoreUnittest) assert(ptr is null); } - unittest - { - import core.thread; - - // Use heap memory to ensure an optimizing - // compiler doesn't put things in registers. - uint* x = new uint(); - bool* f = new bool(); - uint* r = new uint(); - - auto thr = new Thread(() - { - while (!*f) - { - } - - atomicFence(); - - *r = *x; - }); - - thr.start(); - - *x = 42; - - atomicFence(); - - *f = true; - - atomicFence(); - - thr.join(); - - assert(*r == 42); - } - // === atomicFetchAdd and atomicFetchSub operations ==== @betterC pure nothrow @nogc @safe unittest { diff --git a/runtime/druntime/src/core/thread/package.d b/runtime/druntime/src/core/thread/package.d index 71b0237c114..d81ebbdcc82 100644 --- a/runtime/druntime/src/core/thread/package.d +++ b/runtime/druntime/src/core/thread/package.d @@ -18,3 +18,42 @@ public import core.thread.threadbase; public import core.thread.threadgroup; public import core.thread.types; public import core.thread.context; + + +// this test is here to avoid a cyclic dependency between +// core.thread and core.atomic +unittest +{ + import core.atomic; + + // Use heap memory to ensure an optimizing + // compiler doesn't put things in registers. + uint* x = new uint(); + bool* f = new bool(); + uint* r = new uint(); + + auto thr = new Thread(() + { + while (!*f) + { + } + + atomicFence(); + + *r = *x; + }); + + thr.start(); + + *x = 42; + + atomicFence(); + + *f = true; + + atomicFence(); + + thr.join(); + + assert(*r == 42); +} From b7dfbc7acc1a08de6054d1d42a154cc8c8cdd3ae Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 29 Apr 2023 10:32:06 -0700 Subject: [PATCH 144/301] ImportC: add __attribute__((pure)) (dlang/dmd!15145) --- dmd/cparse.d | 8 ++++++++ dmd/frontend.h | 1 + dmd/func.d | 4 ++-- dmd/id.d | 1 + tests/dmd/fail_compilation/attrpure.i | 12 ++++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/attrpure.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 03762d671b8..9e56b86252c 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2893,6 +2893,8 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + if (specifier._pure) + stc |= STC.pure_; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); // tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -3539,6 +3541,11 @@ final class CParser(AST) : Parser!AST specifier._nothrow = true; nextToken(); } + else if (token.ident == Id._pure) + { + specifier._pure = true; + nextToken(); + } else if (token.ident == Id.vector_size) { nextToken(); @@ -4944,6 +4951,7 @@ final class CParser(AST) : Parser!AST bool noreturn; /// noreturn attribute bool naked; /// naked attribute bool _nothrow; /// nothrow attribute + bool _pure; /// pure attribute bool dllimport; /// dllimport attribute bool dllexport; /// dllexport attribute bool _deprecated; /// deprecated attribute diff --git a/dmd/frontend.h b/dmd/frontend.h index 9f5ee1d1fcb..3061126949c 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8896,6 +8896,7 @@ struct Id final static Identifier* show; static Identifier* push; static Identifier* pop; + static Identifier* _pure; static Identifier* define; static Identifier* undef; static void initialize(); diff --git a/dmd/func.d b/dmd/func.d index 2c6099c8983..8e11ab1bb4c 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -377,8 +377,8 @@ extern (C++) class FuncDeclaration : Declaration extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) { super(loc, ident); - //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type); - //printf("storage_class = x%x\n", storage_class); + //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); + //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; this.type = type; if (type) diff --git a/dmd/id.d b/dmd/id.d index 2a2703b7970..a2271d51017 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -546,6 +546,7 @@ immutable Msgtable[] msgtable = { "show" }, { "push" }, { "pop" }, + { "_pure", "pure" }, { "define" }, { "undef" }, ]; diff --git a/tests/dmd/fail_compilation/attrpure.i b/tests/dmd/fail_compilation/attrpure.i new file mode 100644 index 00000000000..ae2ed880b33 --- /dev/null +++ b/tests/dmd/fail_compilation/attrpure.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/attrpure.i(11): Error: `pure` function `attrpure.pureAsSnow` cannot call impure function `attrpure.impure` +--- +*/ + +void impure(); + +__attribute__((pure)) void pureAsSnow() +{ + impure(); +} From 8d6783dbc92fc78068bc224f2ffd89c7ab32e41f Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Sat, 29 Apr 2023 22:35:14 +0300 Subject: [PATCH 145/301] Fix Issue 23863 - typeof rejects AliasSeq!() as argument (dlang/dmd!15140) --- dmd/typesem.d | 6 ++++-- tests/dmd/compilable/test23863.d | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/compilable/test23863.d diff --git a/dmd/typesem.d b/dmd/typesem.d index 8c6063de734..570d7d07b31 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -2804,8 +2804,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type } mt.exp = exp2; - if (mt.exp.op == EXP.type || - mt.exp.op == EXP.scope_) + if ((mt.exp.op == EXP.type || mt.exp.op == EXP.scope_) && + // https://issues.dlang.org/show_bug.cgi?id=23863 + // compile time sequences are valid types + !mt.exp.type.isTypeTuple()) { if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof mt.exp.checkType()) diff --git a/tests/dmd/compilable/test23863.d b/tests/dmd/compilable/test23863.d new file mode 100644 index 00000000000..c3941ce2f81 --- /dev/null +++ b/tests/dmd/compilable/test23863.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23863 + +alias AliasSeq(T...) = T; + +struct S +{ +} +alias Empty = S.tupleof; +Empty x; // accepts valid + +static assert(is(typeof(x))); +static assert(is(typeof(Empty))); +static assert(is(typeof(AliasSeq!(int)))); +static assert(is(typeof(Empty) == AliasSeq!())); +static assert(is(typeof(AliasSeq!()) == AliasSeq!())); From b55f7b037ecd35baef8c933f42d3a522e89c2382 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 29 Apr 2023 22:53:49 -0700 Subject: [PATCH 146/301] fix Issue 23866 - ImportC: Multiple __declspecs rejected --- dmd/cparse.d | 15 +++++++++------ tests/dmd/compilable/test23866.i | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 tests/dmd/compilable/test23866.i diff --git a/dmd/cparse.d b/dmd/cparse.d index 9e56b86252c..9b7db1f33f4 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -2350,12 +2350,15 @@ final class CParser(AST) : Parser!AST * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt) * struct-or-union gnu-attribute (opt) identifier */ - if (token.value == TOK.__attribute__) - cparseGnuAttributes(tagSpecifier); - - if (token.value == TOK.__declspec) - cparseDeclspec(tagSpecifier); - + while (1) + { + if (token.value == TOK.__attribute__) + cparseGnuAttributes(tagSpecifier); + else if (token.value == TOK.__declspec) + cparseDeclspec(tagSpecifier); + else + break; + } t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols); tkwx = TKW.xtag; break; diff --git a/tests/dmd/compilable/test23866.i b/tests/dmd/compilable/test23866.i new file mode 100644 index 00000000000..71a251a364d --- /dev/null +++ b/tests/dmd/compilable/test23866.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=23866 + +struct __declspec(align(16)) __declspec(no_init_all) S { }; + +typedef struct __attribute__((aligned(16))) __declspec(no_init_all) S2 { } S2; From 5cc78a43a6841b12fcac31104c04f27e2924b19f Mon Sep 17 00:00:00 2001 From: Lance Bachmeier Date: Sat, 29 Apr 2023 16:55:10 -0500 Subject: [PATCH 147/301] Fix issues 23867 and 23869 - define __builtin_isfinite and __builtin_isnan These GNU C builtins are referenced by macros such as: * ISNAN * R_FINITE --- runtime/druntime/src/importc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 60414d81730..66c4eb7aa8c 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -74,6 +74,9 @@ */ #define __extension__ /* ignore it, as ImportC doesn't do warnings */ +#define __builtin_isnan(x) isnan(x) +#define __builtin_isfinite(x) finite(x) + /******************************** * __has_extension is a clang thing: * https://clang.llvm.org/docs/LanguageExtensions.html From c73d40649a7d7da59a3cbda79009ca0b44e49ca6 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 1 May 2023 03:23:58 -0700 Subject: [PATCH 148/301] refactor SmallBuffer (dlang/dmd!15144) --- dmd/common/string.d | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/dmd/common/string.d b/dmd/common/string.d index 1111cec2cf1..a1614fd907c 100644 --- a/dmd/common/string.d +++ b/dmd/common/string.d @@ -13,7 +13,7 @@ module dmd.common.string; nothrow: /** -Defines a temporary array using a fixed-length buffer as back store. If the length +Defines a temporary array of `Element`s using a fixed-length buffer as back store. If the length of the buffer suffices, it is readily used. Otherwise, `malloc` is used to allocate memory for the array and `free` is used for deallocation in the destructor. @@ -21,19 +21,26 @@ destructor. This type is meant to use exclusively as an automatic variable. It is not default constructible or copyable. */ -struct SmallBuffer(T) +struct SmallBuffer(Element) { import core.stdc.stdlib : malloc, free; - private T[] _extent; + private Element[] _extent; private bool needsFree; nothrow: + @nogc: @disable this(); // no default ctor - @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable - - this(size_t len, T[] buffer) + @disable this(ref const SmallBuffer!Element); // noncopyable, nonassignable + + /*********** + * Construct a SmallBuffer + * Params: + * len = number of elements in array + * buffer = slice to use as backing-store, if len will fit in it + */ + scope this(size_t len, return scope Element[] buffer) { if (len <= buffer.length) { @@ -41,7 +48,8 @@ struct SmallBuffer(T) } else { - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } @@ -54,16 +62,22 @@ struct SmallBuffer(T) free(_extent.ptr); } - void create(size_t len) + /****** + * Resize existing SmallBuffer. + * Params: + * len = number of elements after resize + */ + scope void create(size_t len) { if (len <= _extent.length) { - _extent = _extent[0 .. len]; + _extent = _extent[0 .. len]; // reuse existing storage } else { __dtor(); - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } From f6eb853d9ef7090ded3884abf081ca7f0aa5d0bd Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Mon, 1 May 2023 17:26:04 +0700 Subject: [PATCH 149/301] Stack direction definition for supported architectures only (dlang/dmd!15142) Co-authored-by: Denis Feklushkin --- runtime/druntime/src/core/thread/types.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/druntime/src/core/thread/types.d b/runtime/druntime/src/core/thread/types.d index eb84ad74b48..998f610c255 100644 --- a/runtime/druntime/src/core/thread/types.d +++ b/runtime/druntime/src/core/thread/types.d @@ -41,8 +41,9 @@ version (GNU) } else { - // this should be true for most architectures - enum isStackGrowingDown = true; + version (X86) enum isStackGrowingDown = true; + else version (X86_64) enum isStackGrowingDown = true; + else static assert(0, "It is undefined how the stack grows on this architecture."); } package From d0476e9f213d4757b808bddfc788c1b26a3571e7 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 1 May 2023 13:28:10 +0200 Subject: [PATCH 150/301] Use DotExpFlag type for flag parameter (dlang/dmd!15065) --- dmd/aliasthis.d | 4 ++-- dmd/expressionsem.d | 49 +++++++++++++++++++++++++++------------------ dmd/frontend.h | 1 + dmd/mtype.d | 1 + dmd/typesem.d | 6 +++--- 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/dmd/aliasthis.d b/dmd/aliasthis.d index bc3d919cc80..ce384593c59 100644 --- a/dmd/aliasthis.d +++ b/dmd/aliasthis.d @@ -78,7 +78,7 @@ extern (C++) final class AliasThis : Dsymbol * Params: * sc = context * e = expression forming the `this` - * gag = if true do not print errors, return null instead + * gag = do not print errors, return `null` instead * findOnly = don't do further processing like resolving properties, * i.e. just return plain dotExp() result. * Returns: @@ -93,7 +93,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find { Loc loc = e.loc; Type tthis = (e.op == EXP.type ? e.type : null); - const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0); + const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); uint olderrors = gag ? global.startGagging() : 0; e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); if (!e || findOnly) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index d288c05cc83..cf4aac4a889 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -634,7 +634,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) } else if (auto dti = ce.e1.isDotTemplateInstanceExp()) { - if (Expression ey = dti.dotTemplateSemanticProp(sc, 1)) + if (Expression ey = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag)) { ce.e1 = ey; return null; @@ -6967,7 +6967,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // Indicate we need to resolve by UFCS. - Expression e = exp.dotTemplateSemanticProp(sc, 1); + Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) e = resolveUFCSProperties(sc, exp); if (e is exp) @@ -9057,7 +9057,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (auto dti = e1x.isDotTemplateInstanceExp()) { - Expression e = dti.dotTemplateSemanticProp(sc, 1); + Expression e = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) { return setResult(resolveUFCSProperties(sc, e1x, exp.e2)); @@ -12891,7 +12891,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length) { // bypass checkPurity - return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0); + return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref)); } if (!exp.e1.isDotExp()) @@ -12943,11 +12943,11 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) * Params: * exp = expression to resolve * sc = context - * flag = if 1 then do not emit error messages, just return null + * gag = do not emit error messages, just return `null` * Returns: * resolved expression, null if error */ -Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) +Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); @@ -13190,9 +13190,9 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule()) { - flag = 0; + gag = false; } - if (flag) + if (gag) return null; s = ie.sds.search_correct(exp.ident); if (s && symbolIsVisible(sc, s)) @@ -13218,7 +13218,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) )) { Type t1bn = t1b.nextOf(); - if (flag) + if (gag) { if (AggregateDeclaration ad = isAggregate(t1bn)) { @@ -13232,11 +13232,12 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) * as: * (*p).ident */ - if (flag && t1bn.ty == Tvoid) + if (gag && t1bn.ty == Tvoid) return null; Expression e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); - return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + const newFlag = cast(DotExpFlag) (gag * DotExpFlag.gag | exp.noderef * DotExpFlag.noDeref); + return e.type.dotExp(sc, e, exp.ident, newFlag); } else if (exp.ident == Id.__xalignof && exp.e1.isVarExp() && @@ -13269,8 +13270,11 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) else { if (exp.e1.isTypeExp() || exp.e1.isTemplateExp()) - flag = 0; - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + gag = false; + + const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); + + Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -13279,9 +13283,16 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } } -// Resolve e1.ident!tiargs without seeing UFCS. -// If flag == 1, stop "not a property" error and return NULL. -Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int flag) +/** + * Resolve `e1.ident!tiargs` without seeing UFCS. + * Params: + * exp = the `DotTemplateInstanceExp` to resolve + * sc = the semantic scope + * gag = stop "not a property" error and return `null`. + * Returns: + * `null` if error or not found, or the resolved expression. + */ +Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool gag) { static if (LOGSEMANTIC) { @@ -13315,11 +13326,11 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int fl /* No built-in type has templatized properties, so do shortcut. * It is necessary in: 1024.max!"a < b" */ - if (flag) + if (gag) return null; } - e = die.dotIdSemanticProp(sc, flag); - if (flag) + e = die.dotIdSemanticProp(sc, gag); + if (gag) { if (!e || isDotOpDispatch(e)) diff --git a/dmd/frontend.h b/dmd/frontend.h index 3061126949c..93b6239a56b 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2948,6 +2948,7 @@ extern Expression* initializerToExpression(Initializer* init, Type* itype = null enum class DotExpFlag { + none = 0, gag = 1, noDeref = 2, noAliasThis = 4, diff --git a/dmd/mtype.d b/dmd/mtype.d index f86bb2b9620..badc5795547 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -313,6 +313,7 @@ int mutabilityOfType(bool isref, Type t) */ enum DotExpFlag { + none = 0, gag = 1, // don't report "not a property" error and just return null noDeref = 2, // the use of the expression will not attempt a dereference noAliasThis = 4, // don't do 'alias this' resolution diff --git a/dmd/typesem.d b/dmd/typesem.d index 570d7d07b31..f0decf2a48d 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -3136,7 +3136,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type * Returns: * resulting expression with e.ident resolved */ -Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) +Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { Expression visitType(Type mt) { @@ -3634,7 +3634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) * template opDispatch(name) if (isValid!name) { ... } */ uint errors = gagError ? global.startGagging() : 0; - e = dti.dotTemplateSemanticProp(sc, 0); + e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; return returnExp(e); @@ -3930,7 +3930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) return mt.getProperty(sc, e.loc, ident, flag & 1); } - Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1); + Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag); if (!(flag & 1) && !res) { if (auto ns = mt.sym.search_correct(ident)) From f305b545fc72c2981a8d3274ba939190ab420d48 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Mon, 1 May 2023 16:05:15 +0200 Subject: [PATCH 151/301] Fix 19454 - Name collisions with unnamed function parameters --- dmd/frontend.h | 22 +++++++++---------- dmd/semantic3.d | 2 +- .../compilable/dtoh_CPPNamespaceDeclaration.d | 4 ++-- tests/dmd/compilable/dtoh_functions.d | 2 +- .../dmd/compilable/dtoh_invalid_identifiers.d | 2 +- tests/dmd/compilable/dtoh_special_enum.d | 20 ++++++++--------- tests/dmd/fail_compilation/bug9631.d | 8 +++---- tests/dmd/fail_compilation/diag10415.d | 2 +- tests/dmd/fail_compilation/diag11769.d | 4 ++-- tests/dmd/fail_compilation/diag14818.d | 4 ++-- tests/dmd/fail_compilation/diag8101b.d | 12 +++++----- tests/dmd/fail_compilation/fail16600.d | 4 ++-- tests/dmd/fail_compilation/fail17518.d | 4 ++-- tests/dmd/fail_compilation/fail17955.d | 2 +- tests/dmd/fail_compilation/fail20609.d | 2 +- tests/dmd/fail_compilation/fail22202.d | 2 +- tests/dmd/fail_compilation/fail332.d | 16 +++++++------- tests/dmd/fail_compilation/ice13225.d | 2 +- tests/dmd/fail_compilation/ice23097.d | 2 +- tests/dmd/fail_compilation/ice9540.d | 2 +- tests/dmd/fail_compilation/retscope2.d | 4 ++-- tests/dmd/fail_compilation/retscope6.d | 2 +- tests/dmd/runnable/xtest46.d | 14 ++++++------ 23 files changed, 69 insertions(+), 69 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 93b6239a56b..e8c2acc134a 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2690,14 +2690,14 @@ class SemanticTimePermissiveVisitor : public Visitor { public: using Visitor::visit; - void visit(Dsymbol* _param_0) override; - void visit(Parameter* _param_0) override; - void visit(Statement* _param_0) override; - void visit(Type* _param_0) override; - void visit(Expression* _param_0) override; - void visit(TemplateParameter* _param_0) override; - void visit(Condition* _param_0) override; - void visit(Initializer* _param_0) override; + void visit(Dsymbol* __param_0_) override; + void visit(Parameter* __param_0_) override; + void visit(Statement* __param_0_) override; + void visit(Type* __param_0_) override; + void visit(Expression* __param_0_) override; + void visit(TemplateParameter* __param_0_) override; + void visit(Condition* __param_0_) override; + void visit(Initializer* __param_0_) override; }; class StatementRewriteWalker : public SemanticTimePermissiveVisitor @@ -3794,7 +3794,7 @@ class TypeFunction final : public TypeNext bool isDstyleVariadic() const; StorageClass parameterStorageClass(Type* tthis, Parameter* p); Type* addStorageClass(StorageClass stc) override; - Type* substWildTo(uint32_t _param_0) override; + Type* substWildTo(uint32_t __param_0_) override; MATCH constConv(Type* to) override; bool iswild() const; void accept(Visitor* v) override; @@ -5550,7 +5550,7 @@ class VisibilityDeclaration final : public AttribDeclaration Scope* newScope(Scope* sc) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; const char* kind() const override; - const char* toPrettyChars(bool _param_0) override; + const char* toPrettyChars(bool __param_0_) override; VisibilityDeclaration* isVisibilityDeclaration() override; void accept(Visitor* v) override; }; @@ -6767,7 +6767,7 @@ class TemplateDeclaration final : public ScopeDsymbol Array lastConstraintNegs; Array* lastConstraintTiargs; public: - TemplateDeclaration* syntaxCopy(Dsymbol* _param_0) override; + TemplateDeclaration* syntaxCopy(Dsymbol* __param_0_) override; bool overloadInsert(Dsymbol* s) override; bool hasStaticCtorOrDtor() override; const char* kind() const override; diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 33a43187fa8..a76496b2771 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -467,7 +467,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* Generate identifier for un-named parameter, * because we need it later on. */ - fparam.ident = id = Identifier.generateId("_param_", i); + fparam.ident = id = Identifier.generateId("__param_", i); stc |= STC.temp; } Type vtype = fparam.type; diff --git a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d index 870387c7113..04363711bd3 100644 --- a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d +++ b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d @@ -48,7 +48,7 @@ namespace nameSpace extern void fn2(); } - extern double identity(double _param_0); + extern double identity(double __param_0_); } --- @@ -63,5 +63,5 @@ extern(C++, "nameSpace") void fn2() {} } - double identity(double) { return _param_0; } + double identity(double) { return __param_0; } } diff --git a/tests/dmd/compilable/dtoh_functions.d b/tests/dmd/compilable/dtoh_functions.d index 1feff408adc..38607f6d4b1 100644 --- a/tests/dmd/compilable/dtoh_functions.d +++ b/tests/dmd/compilable/dtoh_functions.d @@ -159,7 +159,7 @@ extern int32_t(*f)(int32_t ); extern void special(int32_t a = ptr->i, int32_t b = ptr->get(1, 2), int32_t j = (*f)(1)); -extern void variadic(int32_t _param_0, ...); +extern void variadic(int32_t __param_0_, ...); --- +/ diff --git a/tests/dmd/compilable/dtoh_invalid_identifiers.d b/tests/dmd/compilable/dtoh_invalid_identifiers.d index b8e8d05649c..a8f5b990413 100644 --- a/tests/dmd/compilable/dtoh_invalid_identifiers.d +++ b/tests/dmd/compilable/dtoh_invalid_identifiers.d @@ -110,7 +110,7 @@ struct InvalidNames final } }; -extern void useInvalid(InvalidNames _param_0); +extern void useInvalid(InvalidNames __param_0_); extern size_t offsetof(); diff --git a/tests/dmd/compilable/dtoh_special_enum.d b/tests/dmd/compilable/dtoh_special_enum.d index 37b450703a9..ee86a5e7121 100644 --- a/tests/dmd/compilable/dtoh_special_enum.d +++ b/tests/dmd/compilable/dtoh_special_enum.d @@ -40,25 +40,25 @@ struct _d_dynamicArray final #endif enum class __c_not_special; -extern "C" void fn_long(long _param_0); +extern "C" void fn_long(long __param_0_); -extern "C" void fn_ulong(unsigned long _param_0); +extern "C" void fn_ulong(unsigned long __param_0_); -extern "C" void fn_longlong(long long _param_0); +extern "C" void fn_longlong(long long __param_0_); -extern "C" void fn_ulonglong(unsigned long long _param_0); +extern "C" void fn_ulonglong(unsigned long long __param_0_); -extern "C" void fn_long_double(long double _param_0); +extern "C" void fn_long_double(long double __param_0_); -extern "C" void fn_wchar_t(wchar_t _param_0); +extern "C" void fn_wchar_t(wchar_t __param_0_); -extern "C" void fn_complex_float(_Complex float _param_0); +extern "C" void fn_complex_float(_Complex float __param_0_); -extern "C" void fn_complex_double(_Complex double _param_0); +extern "C" void fn_complex_double(_Complex double __param_0_); -extern "C" void fn_complex_real(_Complex long double _param_0); +extern "C" void fn_complex_real(_Complex long double __param_0_); -extern "C" void fn_not_special(__c_not_special _param_0); +extern "C" void fn_not_special(__c_not_special __param_0_); --- +/ diff --git a/tests/dmd/fail_compilation/bug9631.d b/tests/dmd/fail_compilation/bug9631.d index c980d76a73d..802d1c2983e 100644 --- a/tests/dmd/fail_compilation/bug9631.d +++ b/tests/dmd/fail_compilation/bug9631.d @@ -66,8 +66,8 @@ fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is n fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s` fail_compilation/bug9631.d(80): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)` fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s` -fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S _param_0` +fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0` --- */ void arg() @@ -89,8 +89,8 @@ void arg() /* TEST_OUTPUT: --- -fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0` +fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S __param_0` fail_compilation/bug9631.d(107): Error: none of the overloads of template `bug9631.targ.ft` are callable using argument types `!()(S)` fail_compilation/bug9631.d(105): Candidate is: `ft()(tem!().S)` fail_compilation/bug9631.d(109): Error: none of the overloads of template `bug9631.targ.ft2` are callable using argument types `!()(S, int)` diff --git a/tests/dmd/fail_compilation/diag10415.d b/tests/dmd/fail_compilation/diag10415.d index 1fde171b713..207f6a4aa15 100644 --- a/tests/dmd/fail_compilation/diag10415.d +++ b/tests/dmd/fail_compilation/diag10415.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/diag10415.d(36): Error: none of the overloads of `x` are callable using argument types `(int) const` fail_compilation/diag10415.d(13): Candidates are: `diag10415.C.x()` -fail_compilation/diag10415.d(18): `diag10415.C.x(int _param_0)` +fail_compilation/diag10415.d(18): `diag10415.C.x(int __param_0)` fail_compilation/diag10415.d(39): Error: d.x is not an lvalue --- */ diff --git a/tests/dmd/fail_compilation/diag11769.d b/tests/dmd/fail_compilation/diag11769.d index 2717de4a6e7..75047f53785 100644 --- a/tests/dmd/fail_compilation/diag11769.d +++ b/tests/dmd/fail_compilation/diag11769.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both: -fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring _param_0)` +fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring __param_0)` and: -fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring _param_0)` +fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring __param_0)` --- */ diff --git a/tests/dmd/fail_compilation/diag14818.d b/tests/dmd/fail_compilation/diag14818.d index f9b535ab8d7..6147f32d0db 100644 --- a/tests/dmd/fail_compilation/diag14818.d +++ b/tests/dmd/fail_compilation/diag14818.d @@ -2,8 +2,8 @@ TEST_OUTPUT: --- fail_compilation/diag14818.d(40): Error: none of the overloads of `func` are callable using argument types `(string)` -fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int _param_0)` -fail_compilation/diag14818.d(19): `diag14818.bar(double _param_0)` +fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int __param_0)` +fail_compilation/diag14818.d(19): `diag14818.bar(double __param_0)` fail_compilation/diag14818.d(41): Error: template instance `diag14818.X!string` does not match any template declaration fail_compilation/diag14818.d(41): Candidates are: fail_compilation/diag14818.d(24): Foo(T) if (is(T == int)) diff --git a/tests/dmd/fail_compilation/diag8101b.d b/tests/dmd/fail_compilation/diag8101b.d index bc0ee9d2fb7..a55ef731ad2 100644 --- a/tests/dmd/fail_compilation/diag8101b.d +++ b/tests/dmd/fail_compilation/diag8101b.d @@ -2,13 +2,13 @@ TEST_OUTPUT: --- fail_compilation/diag8101b.d(28): Error: none of the overloads of `foo` are callable using argument types `(double)` -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` -fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)` -fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int _param_0` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` +fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int __param_0)` is not callable using argument types `(double)` +fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int __param_0` fail_compilation/diag8101b.d(33): Error: none of the overloads of `foo` are callable using a `const` object -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` fail_compilation/diag8101b.d(35): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object fail_compilation/diag8101b.d(22): Consider adding `const` or `inout` here --- diff --git a/tests/dmd/fail_compilation/fail16600.d b/tests/dmd/fail_compilation/fail16600.d index eb341c64af5..3bd600e507b 100644 --- a/tests/dmd/fail_compilation/fail16600.d +++ b/tests/dmd/fail_compilation/fail16600.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both: -fail_compilation/fail16600.d(16): `fail16600.S.this(string _param_0)` +fail_compilation/fail16600.d(16): `fail16600.S.this(string __param_0)` and: -fail_compilation/fail16600.d(17): `fail16600.S.this(string _param_0) immutable` +fail_compilation/fail16600.d(17): `fail16600.S.this(string __param_0) immutable` --- */ diff --git a/tests/dmd/fail_compilation/fail17518.d b/tests/dmd/fail_compilation/fail17518.d index 385483c048d..cf2648db40b 100644 --- a/tests/dmd/fail_compilation/fail17518.d +++ b/tests/dmd/fail_compilation/fail17518.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) _param_0) inout` is not callable using argument types `(Wrong)` -fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) _param_0` +fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) __param_0) inout` is not callable using argument types `(Wrong)` +fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) __param_0` --- */ diff --git a/tests/dmd/fail_compilation/fail17955.d b/tests/dmd/fail_compilation/fail17955.d index 95eb5cc8c1f..08329194228 100644 --- a/tests/dmd/fail_compilation/fail17955.d +++ b/tests/dmd/fail_compilation/fail17955.d @@ -11,7 +11,7 @@ fail_compilation/fail17955.d(49): instantiated from here: `toRedis!(SysTi fail_compilation/fail17955.d(40): ... (2 instantiations, -v to show) ... fail_compilation/fail17955.d(32): instantiated from here: `indicesOf!(isRedisType, resetCodeExpireTime)` fail_compilation/fail17955.d(67): instantiated from here: `RedisStripped!(User, true)` -fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring _param_0)` +fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring __param_0)` fail_compilation/fail17955.d(95): Error: undefined identifier `DateTimeException` fail_compilation/fail17955.d(25): Error: variable `fail17955.isISOExtStringSerializable!(SysTime).isISOExtStringSerializable` - type `void` is inferred from initializer `fromISOExtString("")`, and variables cannot be of type `void` fail_compilation/fail17955.d(54): Error: function `fail17955.toRedis!(SysTime).toRedis` has no `return` statement, but is expected to return a value of type `string` diff --git a/tests/dmd/fail_compilation/fail20609.d b/tests/dmd/fail_compilation/fail20609.d index 05b7c85375a..80a5d461574 100644 --- a/tests/dmd/fail_compilation/fail20609.d +++ b/tests/dmd/fail_compilation/fail20609.d @@ -4,7 +4,7 @@ fail_compilation/fail20609.d(26): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(23): Candidate is: `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(27): Error: none of the overloads of `this` are callable using argument types `(int)` -fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object _param_0)` +fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object __param_0)` fail_compilation/fail20609.d(23): `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(37): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(37): All possible candidates are marked as `deprecated` or `@disable` diff --git a/tests/dmd/fail_compilation/fail22202.d b/tests/dmd/fail_compilation/fail22202.d index 167d3624879..d865fd95553 100644 --- a/tests/dmd/fail_compilation/fail22202.d +++ b/tests/dmd/fail_compilation/fail22202.d @@ -3,7 +3,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy _param_0)` is not callable using argument types `(SystemCopy)` +fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy __param_0)` is not callable using argument types `(SystemCopy)` fail_compilation/fail22202.d(21): `inout ref inout(SystemCopy)(ref inout(SystemCopy) other)` copy constructor cannot be called from a `pure @safe nogc` context --- */ diff --git a/tests/dmd/fail_compilation/fail332.d b/tests/dmd/fail_compilation/fail332.d index 91f80464705..77e8cd8eb00 100644 --- a/tests/dmd/fail_compilation/fail332.d +++ b/tests/dmd/fail_compilation/fail332.d @@ -1,14 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail332.d(22): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `()` -fail_compilation/fail332.d(22): missing argument for parameter #1: `int _param_0` -fail_compilation/fail332.d(23): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `(typeof(null))` -fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int _param_0` -fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(string)` -fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] _param_0...` -fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(int, typeof(null))` -fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] _param_0...` +fail_compilation/fail332.d(22): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `()` +fail_compilation/fail332.d(22): missing argument for parameter #1: `int __param_0` +fail_compilation/fail332.d(23): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `(typeof(null))` +fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int __param_0` +fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(string)` +fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] __param_0...` +fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(int, typeof(null))` +fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] __param_0...` --- */ diff --git a/tests/dmd/fail_compilation/ice13225.d b/tests/dmd/fail_compilation/ice13225.d index 6988cd7c18d..abc30eaf4f2 100644 --- a/tests/dmd/fail_compilation/ice13225.d +++ b/tests/dmd/fail_compilation/ice13225.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S _param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` +fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S __param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` fail_compilation/ice13225.d(16): Error: undefined identifier `undefined` --- */ diff --git a/tests/dmd/fail_compilation/ice23097.d b/tests/dmd/fail_compilation/ice23097.d index 4fd1f61f828..90cf03f1b48 100644 --- a/tests/dmd/fail_compilation/ice23097.d +++ b/tests/dmd/fail_compilation/ice23097.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice23097.d(12): Error: undefined identifier `ICE` fail_compilation/ice23097.d(27): Error: template instance `ice23097.ice23097!(S23097)` error instantiating -fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 _param_0)` is not callable using argument types `(S23097)` +fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 __param_0)` is not callable using argument types `(S23097)` fail_compilation/ice23097.d(27): generating a copy constructor for `struct S23097` failed, therefore instances of it are uncopyable --- */ diff --git a/tests/dmd/fail_compilation/ice9540.d b/tests/dmd/fail_compilation/ice9540.d index 5276e83c722..456d3e12f23 100644 --- a/tests/dmd/fail_compilation/ice9540.d +++ b/tests/dmd/fail_compilation/ice9540.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int _param_0)` is not callable using argument types `()` +fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int __param_0)` is not callable using argument types `()` fail_compilation/ice9540.d(35): too few arguments, expected 1, got 0 fail_compilation/ice9540.d(26): Error: template instance `ice9540.A.test.AddFront!(this, f)` error instantiating --- diff --git a/tests/dmd/fail_compilation/retscope2.d b/tests/dmd/fail_compilation/retscope2.d index 829fb6a1970..2e7940f70fe 100644 --- a/tests/dmd/fail_compilation/retscope2.d +++ b/tests/dmd/fail_compilation/retscope2.d @@ -86,8 +86,8 @@ fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(604): Error: scope variable `_param_0` assigned to non-scope anonymous parameter calling `foo600` -fail_compilation/retscope2.d(604): Error: scope variable `_param_1` assigned to non-scope anonymous parameter calling `foo600` +fail_compilation/retscope2.d(604): Error: scope variable `__param_0` assigned to non-scope anonymous parameter calling `foo600` +fail_compilation/retscope2.d(604): Error: scope variable `__param_1` assigned to non-scope anonymous parameter calling `foo600` fail_compilation/retscope2.d(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/retscope6.d b/tests/dmd/fail_compilation/retscope6.d index 5c581d1db71..ddeae81bc23 100644 --- a/tests/dmd/fail_compilation/retscope6.d +++ b/tests/dmd/fail_compilation/retscope6.d @@ -25,7 +25,7 @@ int* test() @safe --- fail_compilation/retscope6.d(7034): Error: address of variable `i` assigned to `s` with longer lifetime fail_compilation/retscope6.d(7035): Error: address of variable `i` assigned to `s` with longer lifetime -fail_compilation/retscope6.d(7025): Error: scope variable `_param_2` assigned to `ref` variable `t` with longer lifetime +fail_compilation/retscope6.d(7025): Error: scope variable `__param_2` assigned to `ref` variable `t` with longer lifetime fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime --- diff --git a/tests/dmd/runnable/xtest46.d b/tests/dmd/runnable/xtest46.d index 2d4f559d78d..5017a767693 100644 --- a/tests/dmd/runnable/xtest46.d +++ b/tests/dmd/runnable/xtest46.d @@ -5016,18 +5016,18 @@ void test6763() { int n; - f6763(0); //With D2: Error: function main.f ((ref const const(int) _param_0)) is not callable using argument types (int) + f6763(0); //With D2: Error: function main.f ((ref const const(int) __param_0)) is not callable using argument types (int) c6763(0); r6763(n); static assert(__traits(compiles, r6763(0))); i6763(0); o6763(n); static assert(!__traits(compiles, o6763(0))); // https://issues.dlang.org/show_bug.cgi?id=6755 - static assert(typeof(f6763).stringof == "void(int _param_0)"); - static assert(typeof(c6763).stringof == "void(const(int) _param_0)"); - static assert(typeof(r6763).stringof == "void(ref int _param_0)"); - static assert(typeof(i6763).stringof == "void(in int _param_0)"); - static assert(typeof(o6763).stringof == "void(out int _param_0)"); + static assert(typeof(f6763).stringof == "void(int __param_0)"); + static assert(typeof(c6763).stringof == "void(const(int) __param_0)"); + static assert(typeof(r6763).stringof == "void(ref int __param_0)"); + static assert(typeof(i6763).stringof == "void(in int __param_0)"); + static assert(typeof(o6763).stringof == "void(out int __param_0)"); } /***************************************************/ @@ -5997,7 +5997,7 @@ void test7618(const int x = 1) { int func(ref int x) { return 1; } static assert(!__traits(compiles, func(x))); - // Error: function test.foo.func (ref int _param_0) is not callable using argument types (const(int)) + // Error: function test.foo.func (ref int __param_0) is not callable using argument types (const(int)) int delegate(ref int) dg = (ref int x) => 1; static assert(!__traits(compiles, dg(x))); From 0d100a73d1b4763c39f59da7ad39d9cd71174ece Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Tue, 2 May 2023 12:49:41 +0300 Subject: [PATCH 152/301] Fix Issue 23874 - -profile=gc segfaults / ICE regression --- dmd/expressionsem.d | 2 +- tests/dmd/compilable/test23874.d | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/compilable/test23874.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index cf4aac4a889..987c42cdc7e 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -11023,7 +11023,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be * used with `-betterC`, but only during CTFE. */ - if (global.params.betterC) + if (global.params.betterC || !sc.needsCodegen()) return; if (auto ce = exp.isCatExp()) diff --git a/tests/dmd/compilable/test23874.d b/tests/dmd/compilable/test23874.d new file mode 100644 index 00000000000..63c3ea1f5d9 --- /dev/null +++ b/tests/dmd/compilable/test23874.d @@ -0,0 +1,9 @@ +// https://issues.dlang.org/show_bug.cgi?id=23874 +// REQUIRED_ARGS: -profile=gc + +string myToString() +{ + return ""; +} + +enum x = myToString ~ ""; From b15a02134f152a8adbf0a2eb85f6a2f85e239ed2 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 3 May 2023 11:08:05 +0300 Subject: [PATCH 153/301] Fix Issue 23873 - [ICE] segfault on imported static if ; else auto x (dlang/dmd!15168) --- dmd/dimport.d | 16 +++++++++++++++- dmd/dsymbolsem.d | 8 +++++--- tests/dmd/fail_compilation/imports/import23873.d | 2 ++ tests/dmd/fail_compilation/test23873.d | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/fail_compilation/imports/import23873.d create mode 100644 tests/dmd/fail_compilation/test23873.d diff --git a/dmd/dimport.d b/dmd/dimport.d index b653d9bbf89..c4d5ddbc079 100644 --- a/dmd/dimport.d +++ b/dmd/dimport.d @@ -26,6 +26,7 @@ import dmd.location; import dmd.mtype; import dmd.visitor; +import core.stdc.stdio; /*********************************************************** */ extern (C++) final class Import : Dsymbol @@ -232,7 +233,20 @@ extern (C++) final class Import : Dsymbol * most likely because of parsing errors. * Therefore we cannot trust the resulting AST. */ - if (load(sc)) return; + if (load(sc)) + { + // https://issues.dlang.org/show_bug.cgi?id=23873 + // For imports that are not at module or function level, + // e.g. aggregate level, the import symbol is added to the + // symbol table and later semantic is performed on it. + // This leads to semantic analysis on an malformed AST + // which causes all kinds of segfaults. + // The fix is to note that the module has errors and avoid + // semantic analysis on it. + if(mod) + mod.errors = true; + return; + } if (!mod) return; // Failed diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 506946fe458..e7490a5f125 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1366,9 +1366,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { static if (LOG) { - printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars()); + printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); scope(exit) - printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); + printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg); } if (imp.semanticRun > PASS.initial) return; @@ -1434,7 +1434,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor imp.addPackageAccess(scopesym); } - imp.mod.dsymbolSemantic(null); + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); if (imp.mod.needmoduleinfo) { diff --git a/tests/dmd/fail_compilation/imports/import23873.d b/tests/dmd/fail_compilation/imports/import23873.d new file mode 100644 index 00000000000..39334cf62ef --- /dev/null +++ b/tests/dmd/fail_compilation/imports/import23873.d @@ -0,0 +1,2 @@ +static if ; +else auto x diff --git a/tests/dmd/fail_compilation/test23873.d b/tests/dmd/fail_compilation/test23873.d new file mode 100644 index 00000000000..bb6a71dcc24 --- /dev/null +++ b/tests/dmd/fail_compilation/test23873.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23873 + +/* +TEST_OUTPUT: +--- +fail_compilation/imports/import23873.d(1): Error: (expression) expected following `static if` +fail_compilation/imports/import23873.d(1): Error: declaration expected following attribute, not `;` +fail_compilation/imports/import23873.d(3): Error: no identifier for declarator `x` +--- +*/ +struct Foo +{ + import imports.import23873; +} From 27f6e48d7a9689ca59512be977df42f71062ebc9 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Thu, 4 May 2023 14:17:10 +0200 Subject: [PATCH 154/301] fix issues 20737 and 23014 - TLS variables unusable with -betterC/importC for Windows MSVC targets (dlang/dmd!15170) always generate access via __tls_index --- tests/dmd/runnable/betterc.d | 11 +++++++++++ tests/dmd/runnable/test23014.i | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/dmd/runnable/betterc.d b/tests/dmd/runnable/betterc.d index 74967e9bc35..3d8f7da0fcc 100644 --- a/tests/dmd/runnable/betterc.d +++ b/tests/dmd/runnable/betterc.d @@ -42,6 +42,7 @@ extern (C) void main() test18472(); testRuntimeLowerings(); test18457(); + test20737(); } /*******************************************/ @@ -199,3 +200,13 @@ void test18457() } assert(dtor == 1); } + +/**********************************************/ +// https://issues.dlang.org/show_bug.cgi?id=20737 +int tlsVar; + +int test20737() +{ + tlsVar = 123; + return 0; +} diff --git a/tests/dmd/runnable/test23014.i b/tests/dmd/runnable/test23014.i index 12716caf29b..6730085507a 100644 --- a/tests/dmd/runnable/test23014.i +++ b/tests/dmd/runnable/test23014.i @@ -1,5 +1,4 @@ /* EXTRA_SOURCES: imports/imp23014.i - * DISABLED: win32mscoff win64 */ static _Thread_local int tmp; From 36a7d912a429c6c28396d0cf381147b2ff777e3d Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Fri, 5 May 2023 10:19:20 +0300 Subject: [PATCH 155/301] Fix Issue 23882 - ICE (segfault) on nasty alias this code (dlang/dmd!15177) --- dmd/dcast.d | 4 +++ tests/dmd/fail_compilation/test23882.d | 37 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/dmd/fail_compilation/test23882.d diff --git a/dmd/dcast.d b/dmd/dcast.d index 8ffbef3c966..6fcc2806585 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -71,6 +71,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { + // no need for an extra cast when matching is exact + if (match == MATCH.convert && e.type.isTypeNoreturn()) { return specialNoreturnCast(e, t); @@ -88,6 +90,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) auto ad = isAggregate(e.type); if (ad && ad.aliasthis) { + if (!ad.type || ad.type.isTypeError()) + return e; auto ts = ad.type.isTypeStruct(); const adMatch = ts ? ts.implicitConvToWithoutAliasThis(t) diff --git a/tests/dmd/fail_compilation/test23882.d b/tests/dmd/fail_compilation/test23882.d new file mode 100644 index 00000000000..f6b57c4ea04 --- /dev/null +++ b/tests/dmd/fail_compilation/test23882.d @@ -0,0 +1,37 @@ +// https://issues.dlang.org/show_bug.cgi?id=23882 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23882.d(26): Error: `typeof((*YC).S).init` is used as a type +--- +*/ + +struct G(H) +{ + Tuple!(R) S; +} + +struct BB(H) +{ + H* YC; + alias YC this; +} + +struct R +{ + BB!(G!float) CB; + alias CB this; + + this(typeof(CB.S).init); +} + +struct Tuple(Specs) +{ + Specs expand; + + this(Specs values) + { + expand = values; + } +} From 2055d38a2b4c2e61c257c927d734e76679e662b4 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Fri, 5 May 2023 10:19:36 +0300 Subject: [PATCH 156/301] Fix Issue 23874 - -profile=gc segfaults / ICE regression (dlang/dmd!15179) --- dmd/dsymbolsem.d | 2 +- tests/dmd/compilable/test23874.d | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index e7490a5f125..7333c656b8f 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -484,7 +484,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Infering the type requires running semantic, // so mark the scope as ctfe if required - bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0; + bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { sc.flags |= SCOPE.condition; diff --git a/tests/dmd/compilable/test23874.d b/tests/dmd/compilable/test23874.d index 63c3ea1f5d9..81ee9d59ec2 100644 --- a/tests/dmd/compilable/test23874.d +++ b/tests/dmd/compilable/test23874.d @@ -7,3 +7,4 @@ string myToString() } enum x = myToString ~ ""; +immutable x2 = myToString ~ ""; From b2952dcf7c9e1a0120c87ba19b121f3f03603117 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Fri, 5 May 2023 11:05:27 +0300 Subject: [PATCH 157/301] Fix Issue 22760 - Segmentation fault in CppMangleVisitor.template_arg (dlang/dmd!15099) --- dmd/cppmangle.d | 10 +++++++++- tests/dmd/compilable/test22760.d | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/compilable/test22760.d diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index 40092c3f584..ee1340d6342 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -446,7 +446,15 @@ private final class CppMangleVisitor : Visitor if (this.context.res.dyncast() == DYNCAST.dsymbol) parentti = this.context.res.asFuncDecl().parent.isTemplateInstance(); else - parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance(); + { + auto parent = this.context.res.asType().toDsymbol(null).parent; + parentti = parent.isTemplateInstance(); + // https://issues.dlang.org/show_bug.cgi?id=22760 + // The template instance may sometimes have the form + // S1!int.S1, therefore the above instruction might yield null + if (parentti is null && parent.parent) + parentti = parent.parent.isTemplateInstance(); + } return (*parentti.tiargs)[arg]; }()); scope (exit) this.context.pop(prev); diff --git a/tests/dmd/compilable/test22760.d b/tests/dmd/compilable/test22760.d new file mode 100644 index 00000000000..5957db3256c --- /dev/null +++ b/tests/dmd/compilable/test22760.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=22760 + +extern(C++) void f(T)(T) +{ +} +struct S1(T) +{ + struct S2 + { + } +} +void fun() +{ + f(S1!int.S2()); +} From dfd17ef49ace46d0ceac64c10ac587c6925d20b8 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 10 May 2023 09:10:54 +0300 Subject: [PATCH 158/301] Fix Issue 23905 - Initialization of SumType with opaque enum causes ICE (dlang/dmd!15207) --- dmd/denum.d | 7 ++++++- tests/dmd/fail_compilation/test23905.d | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/test23905.d diff --git a/dmd/denum.d b/dmd/denum.d index 221250b27e0..87b40b854b9 100644 --- a/dmd/denum.d +++ b/dmd/denum.d @@ -169,7 +169,12 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return defaultval; } //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); - if (defaultval) + // https://issues.dlang.org/show_bug.cgi?id=23904 + // Return defaultval only if it is not ErrorExp. + // A speculative context may set defaultval to ErrorExp; + // subsequent non-speculative contexts need to be able + // to print the error. + if (defaultval && !defaultval.isErrorExp()) return defaultval; if (isCsymbol()) diff --git a/tests/dmd/fail_compilation/test23905.d b/tests/dmd/fail_compilation/test23905.d new file mode 100644 index 00000000000..5b30fa855e2 --- /dev/null +++ b/tests/dmd/fail_compilation/test23905.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23905 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23905.d(24): Error: enum `test23905.Foo` is opaque and has no default initializer +--- +*/ + +struct SumType(T) +{ + T storage; + + bool opEquals(Rhs)(Rhs rhs) + if (is(typeof(Rhs.init))) + { + } + +} + +enum Foo; + +void main(){ + SumType!Foo data = Foo.init; +} From 9f617d3c61764873317577ad9ebd0eb10222226e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 16 May 2023 00:07:19 -0700 Subject: [PATCH 159/301] fix Issue 23913 - __traits(getMember) fails for some C symbols (dlang/dmd!15234) --- dmd/expressionsem.d | 12 +++++++++++- tests/dmd/compilable/imports/library.c | 5 +++++ tests/dmd/compilable/test23913.d | 7 +++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/compilable/imports/library.c create mode 100644 tests/dmd/compilable/test23913.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 987c42cdc7e..bdad3a27262 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -13175,10 +13175,20 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) Expression se = new ScopeExp(exp.loc, imp.pkg); return se.expressionSemantic(sc); } + + if (auto attr = s.isAttribDeclaration()) + { + if (auto sm = ie.sds.search(exp.loc, exp.ident, flags)) + { + auto es = new DsymbolExp(exp.loc, sm); + return es; + } + } + // BUG: handle other cases like in IdentifierExp::semantic() debug { - printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind()); + printf("s = %p '%s', kind = '%s'\n", s, s.toChars(), s.kind()); } assert(0); } diff --git a/tests/dmd/compilable/imports/library.c b/tests/dmd/compilable/imports/library.c new file mode 100644 index 00000000000..4951e465574 --- /dev/null +++ b/tests/dmd/compilable/imports/library.c @@ -0,0 +1,5 @@ +typedef enum SomeEnum +{ + foo = 0, + bar = -10000, +} SomeEnum; diff --git a/tests/dmd/compilable/test23913.d b/tests/dmd/compilable/test23913.d new file mode 100644 index 00000000000..e39c6dec317 --- /dev/null +++ b/tests/dmd/compilable/test23913.d @@ -0,0 +1,7 @@ +// EXTRA_FILES: imports/library.c + +// https://issues.dlang.org/show_bug.cgi?id=23913 + +import imports.library; + +alias x = __traits(getMember, imports.library, "SomeEnum"); From af9e05f1ae7d32a4071ea07731a08f36a2a77fee Mon Sep 17 00:00:00 2001 From: Ernesto Castellotti Date: Wed, 31 May 2023 16:42:29 +0200 Subject: [PATCH 160/301] Fix issue 23949 - core.stdc.assert_ for FreeBSD and DragonFlyBSD is incorrect --- runtime/druntime/src/core/stdc/assert_.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/druntime/src/core/stdc/assert_.d b/runtime/druntime/src/core/stdc/assert_.d index fc9402f6051..a7a54e873c2 100644 --- a/runtime/druntime/src/core/stdc/assert_.d +++ b/runtime/druntime/src/core/stdc/assert_.d @@ -60,7 +60,7 @@ else version (FreeBSD) /*** * Assert failure function in the FreeBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (NetBSD) { @@ -83,7 +83,7 @@ else version (DragonFlyBSD) /*** * Assert failure function in the DragonFlyBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (CRuntime_Glibc) { From ef0719f36b649f57ece5726717cef198ab78b28e Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 2 Jun 2023 00:45:56 +0200 Subject: [PATCH 161/301] =?UTF-8?q?Implement=20-femit-local-var-lifetime?= =?UTF-8?q?=20which=20adds=20local=20(stack)=20variable=E2=80=A6=20(#4395)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement -femit-local-var-lifetime which adds local (stack) variable lifetime annotation to LLVM IR, which enables sharing stack space for variables whose lifetimes do not overlap. Resolves issue #2227 This is not enabled by default yet, to prevent miscompilation due to bugs (should be enabled in future for optimization levels > 0, and when sanitizers are enabled). --- CHANGELOG.md | 1 + gen/funcgenstate.cpp | 4 +- gen/funcgenstate.h | 3 + gen/llvmhelpers.cpp | 10 ++ gen/llvmhelpers.h | 3 + gen/optimizer.cpp | 6 +- gen/statements.cpp | 10 ++ gen/variable_lifetime.cpp | 97 ++++++++++++++++ gen/variable_lifetime.h | 56 ++++++++++ runtime/CMakeLists.txt | 2 +- runtime/phobos | 2 +- tests/codegen/lifetime_local_variables.d | 122 +++++++++++++++++++++ tests/sanitizers/asan_use_after_scope.d | 24 ++++ tests/sanitizers/asan_use_after_scope_if.d | 23 ++++ 14 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 gen/variable_lifetime.cpp create mode 100644 gen/variable_lifetime.h create mode 100644 tests/codegen/lifetime_local_variables.d create mode 100644 tests/sanitizers/asan_use_after_scope.d create mode 100644 tests/sanitizers/asan_use_after_scope_if.d diff --git a/CHANGELOG.md b/CHANGELOG.md index f525837384b..2c328259200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) +- New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) #### Platform support diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index fbdb9a34625..a59ec360e80 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -100,8 +100,8 @@ llvm::BasicBlock *SwitchCaseTargets::getOrCreate(Statement *stmt, } FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) - : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), - irs(irs) {} + : irFunc(irFunc), scopes(irs), localVariableLifetimeAnnotator(irs), + jumpTargets(scopes), switchTargets(), irs(irs) {} LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, llvm::FunctionType *calleeType, diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h index af719c74b8a..0c5759d3a81 100644 --- a/gen/funcgenstate.h +++ b/gen/funcgenstate.h @@ -17,6 +17,7 @@ #include "gen/irstate.h" #include "gen/pgo_ASTbased.h" #include "gen/trycatchfinally.h" +#include "gen/variable_lifetime.h" #include "llvm/ADT/DenseMap.h" #include @@ -176,6 +177,8 @@ class FuncGenState { TryCatchFinallyScopes scopes; + LocalVariableLifetimeAnnotator localVariableLifetimeAnnotator; + JumpTargets jumpTargets; // PGO information diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 2a4a550ee4a..a7e2c2f8c31 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -919,19 +919,29 @@ void DtoVarDeclaration(VarDeclaration *vd) { Type *type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type; llvm::Value *allocainst; + bool isRealAlloca = false; LLType *lltype = DtoType(type); // void for noreturn if (lltype->isVoidTy() || gDataLayout->getTypeSizeInBits(lltype) == 0) { allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype)); } else if (type != vd->type) { allocainst = DtoAlloca(type, vd->toChars()); + isRealAlloca = true; } else { allocainst = DtoAlloca(vd, vd->toChars()); + isRealAlloca = true; } irLocal->value = allocainst; if (!lltype->isVoidTy()) gIR->DBuilder.EmitLocalVariable(allocainst, vd); + + // Lifetime annotation is only valid on alloca. + if (isRealAlloca) { + // The lifetime of a stack variable starts from the point it is declared + gIR->funcGen().localVariableLifetimeAnnotator.addLocalVariable( + allocainst, DtoConstUlong(type->size())); + } } IF_LOG Logger::cout() << "llvm value for decl: " << *getIrLocal(vd)->value diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index 7dbacbefda6..13ee25d3108 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -294,3 +294,6 @@ DValue *makeVarDValue(Type *type, VarDeclaration *vd, bool toInPlaceConstruction(DLValue *lhs, Expression *rhs); std::string llvmTypeToString(LLType *type); + +void emitLifetimeStart(llvm::Value *size, llvm::Value *addr); +void emitLifetimeEnd(llvm::Value *size, llvm::Value *addr); diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 7d5946e276c..29cc8c2f43d 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -203,8 +203,10 @@ static void legacyAddGarbageCollect2StackPass(const PassManagerBuilder &builder, } static void legacyAddAddressSanitizerPasses(const PassManagerBuilder &Builder, - PassManagerBase &PM) { - PM.add(createAddressSanitizerFunctionPass()); + PassManagerBase &PM) { + PM.add(createAddressSanitizerFunctionPass(/*CompileKernel = */ false, + /*Recover = */ false, + /*UseAfterScope = */ true)); PM.add(createModuleAddressSanitizerLegacyPassPass()); } diff --git a/gen/statements.cpp b/gen/statements.cpp index 042077cc9bd..e80d6173fdb 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -397,6 +397,9 @@ class ToIRVisitor : public Visitor { // start a dwarf lexical block irs->DBuilder.EmitBlockStart(stmt->loc); emitCoverageLinecountInc(stmt->loc); + // Open a new scope for the optional condition variable (`if (auto i = ...)`) + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); + // This is a (dirty) hack to get codegen time conditional // compilation, on account of the fact that we are trying @@ -482,6 +485,9 @@ class ToIRVisitor : public Visitor { // rewrite the scope irs->ir->SetInsertPoint(endbb); + // Close the scope for the optional condition variable. This is suboptimal, + // because the condition variable is not in scope in the else block. + irs->funcGen().localVariableLifetimeAnnotator.popScope(); } ////////////////////////////////////////////////////////////////////////// @@ -494,9 +500,11 @@ class ToIRVisitor : public Visitor { PGO.setCurrentStmt(stmt); if (stmt->statement) { + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); irs->DBuilder.EmitBlockStart(stmt->statement->loc); stmt->statement->accept(this); irs->DBuilder.EmitBlockEnd(); + irs->funcGen().localVariableLifetimeAnnotator.popScope(); } } @@ -636,6 +644,7 @@ class ToIRVisitor : public Visitor { // start new dwarf lexical block irs->DBuilder.EmitBlockStart(stmt->loc); + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); // create for blocks llvm::BasicBlock *forbb = irs->insertBB("forcond"); @@ -717,6 +726,7 @@ class ToIRVisitor : public Visitor { irs->ir->SetInsertPoint(endbb); // end the dwarf lexical block + irs->funcGen().localVariableLifetimeAnnotator.popScope(); irs->DBuilder.EmitBlockEnd(); } diff --git a/gen/variable_lifetime.cpp b/gen/variable_lifetime.cpp new file mode 100644 index 00000000000..4accea82960 --- /dev/null +++ b/gen/variable_lifetime.cpp @@ -0,0 +1,97 @@ +//===-- gen/variable_lifetime.cpp - -----------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Codegen for local variable lifetime: llvm.lifetime.start abd +// llvm.lifetime.end. +// +//===----------------------------------------------------------------------===// + +#include "gen/variable_lifetime.h" + +#include "driver/cl_options.h" +#include "gen/irstate.h" + +#include +#include + +// TODO: make this option depend on -O and -fsanitize settings. +static llvm::cl::opt fEmitLocalVarLifetime( + "femit-local-var-lifetime", + llvm::cl::desc( + "Emit local variable lifetime, enabling more optimizations."), + llvm::cl::Hidden, llvm::cl::ZeroOrMore); + +LocalVariableLifetimeAnnotator::LocalVariableLifetimeAnnotator(IRState &irs) + : irs(irs) { + allocaType = + llvm::Type::getInt8Ty(irs.context()) + ->getPointerTo(irs.module.getDataLayout().getAllocaAddrSpace()); +} + +void LocalVariableLifetimeAnnotator::pushScope() { scopes.emplace_back(); } + +void LocalVariableLifetimeAnnotator::addLocalVariable(llvm::Value *address, + llvm::Value *size) { + assert(address); + assert(size); + + if (!fEmitLocalVarLifetime) + return; + + if (scopes.empty()) + return; + + // Push to scopes + scopes.back().variables.emplace_back(size, address); + + // Emit lifetime start + address = irs.ir->CreateBitCast(address, allocaType); + irs.CreateCallOrInvoke(getLLVMLifetimeStartFn(), {size, address}, "", + true /*nothrow*/); +} + +// Emits end-of-lifetime annotation for all variables in current scope. +void LocalVariableLifetimeAnnotator::popScope() { + if (scopes.empty()) + return; + + for (const auto &var : scopes.back().variables) { + auto size = var.first; + auto address = var.second; + + address = irs.ir->CreateBitCast(address, allocaType); + assert(address); + + irs.CreateCallOrInvoke(getLLVMLifetimeEndFn(), {size, address}, "", + true /*nothrow*/); + } + scopes.pop_back(); +} + +/// Lazily declare the @llvm.lifetime.start intrinsic. +llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeStartFn() { + if (lifetimeStartFunction) + return lifetimeStartFunction; + + lifetimeStartFunction = llvm::Intrinsic::getDeclaration( + &irs.module, llvm::Intrinsic::lifetime_start, allocaType); + assert(lifetimeStartFunction); + return lifetimeStartFunction; +} + +/// Lazily declare the @llvm.lifetime.end intrinsic. +llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeEndFn() { + if (lifetimeEndFunction) + return lifetimeEndFunction; + + lifetimeEndFunction = llvm::Intrinsic::getDeclaration( + &irs.module, llvm::Intrinsic::lifetime_end, allocaType); + assert(lifetimeEndFunction); + return lifetimeEndFunction; +} diff --git a/gen/variable_lifetime.h b/gen/variable_lifetime.h new file mode 100644 index 00000000000..f482030c9ad --- /dev/null +++ b/gen/variable_lifetime.h @@ -0,0 +1,56 @@ +//===-- gen/variable_lifetime.h - -------------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Codegen for local variable lifetime: llvm.lifetime.start abd +// llvm.lifetime.end. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include + +namespace llvm { +class Function; +class Type; +class Value; +} +struct IRState; + +struct LocalVariableLifetimeAnnotator { + struct LocalVariableScope { + std::vector> variables; + }; + /// Stack of scopes, each scope can have multiple variables. + std::vector scopes; + + /// Cache the llvm types and intrinsics used for codegen. + llvm::Function *lifetimeStartFunction = nullptr; + llvm::Function *lifetimeEndFunction = nullptr; + llvm::Type *allocaType = nullptr; + + llvm::Function *getLLVMLifetimeStartFn(); + llvm::Function *getLLVMLifetimeEndFn(); + + IRState &irs; + +public: + LocalVariableLifetimeAnnotator(IRState &irs); + + /// Opens a new scope. + void pushScope(); + + /// Closes current scope and emits end-of-lifetime annotation for all + /// variables in current scope. + void popScope(); + + /// Register a new local variable for lifetime annotation. + void addLocalVariable(llvm::Value *address, llvm::Value *size); +}; diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index d595144737b..c6334de4be8 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -38,7 +38,7 @@ set(BUILD_SHARED_LIBS AUTO CACHE STRING "Whet set(D_FLAGS -w;-de;-preview=dip1000;-preview=dtorfields;-preview=fieldwise CACHE STRING "Runtime D compiler flags, separated by ';'") set(D_EXTRA_FLAGS "" CACHE STRING "Runtime extra D compiler flags, separated by ';'") set(D_FLAGS_DEBUG -g;-link-defaultlib-debug;-d-debug CACHE STRING "Runtime D compiler flags (debug libraries), separated by ';'") -set(D_FLAGS_RELEASE -O3;-release CACHE STRING "Runtime D compiler flags (release libraries), separated by ';'") +set(D_FLAGS_RELEASE -O3;-release;-femit-local-var-lifetime CACHE STRING "Runtime D compiler flags (release libraries), separated by ';'") set(COMPILE_ALL_D_FILES_AT_ONCE ON CACHE BOOL "Compile all D files for the runtime libs in a single command line instead of separately. Disabling this is useful for many CPU cores and/or iterative development.") set(RT_ARCHIVE_WITH_LDC ON CACHE STRING "Whether to archive the static runtime libs via LDC instead of CMake archiver") set(RT_CFLAGS "" CACHE STRING "Runtime extra C compiler flags, separated by ' '") diff --git a/runtime/phobos b/runtime/phobos index 6c83b490f7d..f4961356a33 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 6c83b490f7d6c66bf430e5249dae608848d3ac2c +Subproject commit f4961356a33849f24557a77bdc386eff852bb9f5 diff --git a/tests/codegen/lifetime_local_variables.d b/tests/codegen/lifetime_local_variables.d new file mode 100644 index 00000000000..5036da5f017 --- /dev/null +++ b/tests/codegen/lifetime_local_variables.d @@ -0,0 +1,122 @@ +// RUN: %ldc -femit-local-var-lifetime -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll + +extern(C): // disable mangling for easier matching + +void opaque(byte* i); + +// CHECK-LABEL: define void @foo_array_foo() +void foo_array_foo() { + // CHECK: alloca [400 x i8] + // CHECK: alloca [800 x i8] + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 400 + byte[400] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 400 + } + + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 800 + byte[800] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 800 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_forloop_foo() +void foo_forloop_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of while-loop variable. + opaque(&i); + for (byte[13] d; d[0] < 2; d[0]++) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 13 + // Lifetime should start before initializing the variable + // CHECK: call void @llvm.memset.p0i8.i{{.*}}13 + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 44 + byte[44] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 44 + // CHECK: endfor: + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 13 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_whileloop_foo() +void foo_whileloop_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of while-loop variable. + opaque(&i); + while (ulong d = 131) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8 + // Lifetime should start before initializing the variable + // CHECK: store i64 131 + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 33 + byte[33] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 33 + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_if_foo() +void foo_if_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of if-statement condition variable. + opaque(&i); + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8 + // Lifetime should start before initializing the variable + // CHECK: store i64 565 + if (ulong d = 565) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 72 + byte[72] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 72 + } else { + // d is out of scope here. + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 51 + byte[51] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 51 + } + // CHECK: endif: + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8 + + // CHECK-LABEL: ret void +} + +struct S { + byte[123] a; + ~this() { + opaque(&a[1]); + } +} + +void opaque_S(S* i); + +// CHECK-LABEL: define void @foo_struct_foo() +void foo_struct_foo() { + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 123 + S s; + // CHECK: invoke void @opaque_S + opaque_S(&s); + } + + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 123 + // CHECK-NEXT: ret void +} diff --git a/tests/sanitizers/asan_use_after_scope.d b/tests/sanitizers/asan_use_after_scope.d new file mode 100644 index 00000000000..ff16b164b8c --- /dev/null +++ b/tests/sanitizers/asan_use_after_scope.d @@ -0,0 +1,24 @@ +// REQUIRES: ASan + +// RUN: %ldc -femit-local-var-lifetime -g -fsanitize=address %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s + +// CHECK: ERROR: AddressSanitizer: stack-use-after-scope +// CHECK-NEXT: WRITE of size 4 + +void useAfterScope() { + int* p; + { + int x = 0; + p = &x; // cannot statically disallow this because + *p = 1; // this is a valid use of things + } +// CHECK-NEXT: #0 {{.*}} in {{.*}}asan_use_after_scope.d:[[@LINE+1]] + *p = 5; // but then this can happen... stack use after scope bug! +} + +void main() +{ +// CHECK-NEXT: #1 {{.*}} in {{.*}}asan_use_after_scope.d:[[@LINE+1]] + useAfterScope(); +} diff --git a/tests/sanitizers/asan_use_after_scope_if.d b/tests/sanitizers/asan_use_after_scope_if.d new file mode 100644 index 00000000000..a62c30a89c8 --- /dev/null +++ b/tests/sanitizers/asan_use_after_scope_if.d @@ -0,0 +1,23 @@ +// REQUIRES: ASan + +// RUN: %ldc -femit-local-var-lifetime -g -fsanitize=address %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s + +// CHECK: ERROR: AddressSanitizer: stack-use-after-scope +// CHECK-NEXT: WRITE of size 4 + +void useAfterScope(int xparam) { + int* p; + if (int x = xparam) { + p = &x; // cannot statically disallow this because + *p = 1; // this is a valid use of things + } +// CHECK-NEXT: #0 {{.*}} in {{.*}}asan_use_after_scope_if.d:[[@LINE+1]] + *p = 5; // but then this can happen... stack use after scope bug! +} + +void main() +{ +// CHECK-NEXT: #1 {{.*}} in {{.*}}asan_use_after_scope_if.d:[[@LINE+1]] + useAfterScope(1); +} From dd47833a2909a6d959d11f1aea5ede2cc43abb7c Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 08:26:05 +0100 Subject: [PATCH 162/301] .github/workflows: Support LLVM 16.0.3 via Ubuntu test matrix Signed-off-by: Ikey Doherty --- .github/workflows/supported_llvm_versions.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 9b9ff4021f0..99b0598357a 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -15,6 +15,10 @@ jobs: fail-fast: false matrix: include: + - job_name: Ubuntu 20.04, LLVM 16, latest LDC beta + os: ubuntu-20.04 + host_dc: ldc-beta + llvm_version: 16.0.3 - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta @@ -95,6 +99,8 @@ jobs: version='${{ matrix.llvm_version }}' if [[ '${{ runner.os }}' == macOS ]]; then suffix='x86_64-apple-darwin' + elif [[ "$version" =~ ^1[6-9]\. ]]; then # LLVM 16.0.3+ + suffix='x86_64-linux-gnu-ubuntu-22.04' # LLVM 13.0.1+ elif [[ "$version" =~ ^1[3-9]\. ]]; then suffix='x86_64-linux-gnu-ubuntu-18.04' # LLVM 13.0.1+ else From 2d3bb571e5bc09afebfa08a8bd9740cc3ff7f15d Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 10:23:43 +0100 Subject: [PATCH 163/301] .github/workflows: Set gdb appropriately on Ubuntu 22.04 Signed-off-by: Ikey Doherty --- .github/workflows/supported_llvm_versions.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 99b0598357a..1ce366e1b50 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -15,8 +15,8 @@ jobs: fail-fast: false matrix: include: - - job_name: Ubuntu 20.04, LLVM 16, latest LDC beta - os: ubuntu-20.04 + - job_name: Ubuntu 22.04, LLVM 16, latest LDC beta + os: ubuntu-22.04 host_dc: ldc-beta llvm_version: 16.0.3 - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta @@ -81,7 +81,12 @@ jobs: set -eux sudo apt-get update # Don't use latest gdb v10+ from Ubuntu toolchain PPA with regressions, use official v9 - sudo apt-get install gdb=9.1-0ubuntu1 llvm + version='${{ matrix.llvm_version }}' + if [[ "$version" =~ ^1[6-9]\. ]]; then # LLVM 16.0.3+ + sudo apt-get install gdb llvm + else + sudo apt-get install gdb=9.1-0ubuntu1 llvm + fi - name: Try to restore cached LLVM uses: actions/cache@v2 From f06d31de56c9f16ca551da18dd6dc1aff62b5a33 Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 08:40:52 +0100 Subject: [PATCH 164/301] gen/runtime: Use the correct header path for ModRef In stable LLVM 16 this is now under `Support` and not `IR` Signed-off-by: Ikey Doherty --- gen/runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 37cd6dc7419..c2040341f21 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -33,7 +33,7 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/Attributes.h" #if LDC_LLVM_VER >= 1600 -#include "llvm/IR/ModRef.h" +#include "llvm/Support/ModRef.h" #endif #include "llvm/IR/Module.h" #include "llvm/Support/CommandLine.h" From 173a4cbda00ead925e1bde23d0c54fb7fbf4f204 Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 08:43:32 +0100 Subject: [PATCH 165/301] gen/optimizer: Port to LLVM 16 Context usage The StandardInstrumentations type now takes an LLVMContext parameter so pass it along from the current module. Signed-off-by: Ikey Doherty --- gen/optimizer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 29cc8c2f43d..d07d4bcb223 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -637,7 +637,11 @@ void runOptimizationPasses(llvm::Module *M) { bool debugLogging = false; ppo.Indent = false; ppo.SkipAnalyses = false; + #if LDC_LLVM_VER < 1600 StandardInstrumentations si(debugLogging, /*VerifyEach=*/false, ppo); + #else + StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo); + #endif si.registerCallbacks(pic, &fam); From a253748d2d3fd0323225751324e1aa3549872f9d Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 08:48:56 +0100 Subject: [PATCH 166/301] gen/ms-cxx-helper: Use new `insertInto` LLVM16 API Instructions now use friend APIs to add themselves into a BasicBlock, rather than a BasicBlock having an API to add the instructions. Signed-off-by: Ikey Doherty --- gen/ms-cxx-helper.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 52814fd5cfc..9f4eb7627f6 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -114,7 +114,12 @@ void cloneBlocks(const std::vector &srcblocks, if (!newInst) newInst = Inst->clone(); + #if LDC_LLVM_VER < 1600 nbb->getInstList().push_back(newInst); + #else + newInst->insertInto(nbb, nbb->end()); + #endif + VMap[Inst] = newInst; // Add instruction map to value. if (unwindTo) if (auto dest = getUnwindDest(Inst)) From 0454f3378f0b03a92d51bd86ce550ce15b1749cb Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 09:01:59 +0100 Subject: [PATCH 167/301] driver/main: For LLVM > 16, stop using AggressiveInstCombine This was killed upstream in the codegen pipeline as part of a bunch of legacy PM removals: - https://reviews.llvm.org/D137116 Signed-off-by: Ikey Doherty --- driver/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/driver/main.cpp b/driver/main.cpp index 6d1c3441eb0..fcaaf289830 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -579,7 +579,9 @@ void initializePasses() { #endif initializeVectorization(Registry); initializeInstCombine(Registry); +#if LDC_LLVM_VER < 1600 initializeAggressiveInstCombine(Registry); +#endif initializeIPO(Registry); #if LDC_LLVM_VER < 1600 initializeInstrumentation(Registry); From 90a82f59e07848693b5284591c788956bf58fe02 Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 09:06:43 +0100 Subject: [PATCH 168/301] driver/targetmachine: Fix aarch64 support for LLVM >= 1600 This isn't *the* most ideal approach and in future we may want to select specific fallbacks other than "generic". Signed-off-by: Ikey Doherty --- driver/targetmachine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index be47a1dc931..3f16e20813c 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -203,9 +203,11 @@ static std::string getARMTargetCPU(const llvm::Triple &triple) { } static std::string getAArch64TargetCPU(const llvm::Triple &triple) { +#if LDC_LLVM_VER < 1600 auto defaultCPU = llvm::AArch64::getDefaultCPU(triple.getArchName()); if (!defaultCPU.empty()) return std::string(defaultCPU); +#endif return "generic"; } From ad710703695762b3d0f039735a3cac0203bf38fb Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 10:53:55 +0100 Subject: [PATCH 169/301] For LLVM >= 16, use std::optional APIs Unfortunately when building with LLVM < 15, we're using C++-11, so the std::optional bits are only available in C++-17. Even more unfortunately we can't really override this, rather inherit the fact of using C++-17 when using LLVM > 16. Thus, we add a slightly messy conditional compilation situation to allow usage of LDC on older LLVM versions, and with newer LLVM/libcxx. Signed-off-by: Ikey Doherty --- driver/args.cpp | 4 ++++ driver/linker.cpp | 4 ++++ driver/linker.h | 4 ++++ gen/dcompute/druntime.cpp | 7 ++++++- gen/dibuilder.cpp | 21 +++++++++++++++++++++ gen/optimizer.cpp | 5 +++++ gen/uda.cpp | 4 ++++ utils/not.cpp | 4 ++++ 8 files changed, 52 insertions(+), 1 deletion(-) diff --git a/driver/args.cpp b/driver/args.cpp index da291202d18..bad8e45dee5 100644 --- a/driver/args.cpp +++ b/driver/args.cpp @@ -175,7 +175,11 @@ int executeAndWait(std::vector fullArgs, } const std::vector argv = toRefsVector(fullArgs); +#if LDC_LLVM_VER < 1600 auto envVars = llvm::None; +#else + auto envVars = std::nullopt; +#endif return llvm::sys::ExecuteAndWait(argv[0], argv, envVars, {}, 0, 0, errorMsg); } diff --git a/driver/linker.cpp b/driver/linker.cpp index 76266379d29..803d5e883ed 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -195,7 +195,11 @@ static std::vector getDefaultLibNames() { llvm::Optional> getExplicitPlatformLibs() { if (platformLib.getNumOccurrences() > 0) return parseLibNames(platformLib); +#if LDC_LLVM_VER < 1600 return llvm::None; +#else + return std::nullopt; +#endif } ////////////////////////////////////////////////////////////////////////////// diff --git a/driver/linker.h b/driver/linker.h index f962054efaf..28362d86526 100644 --- a/driver/linker.h +++ b/driver/linker.h @@ -42,7 +42,11 @@ bool linkAgainstSharedDefaultLibs(); /** * Returns the -platformlib library names, if specified. */ +#if LDC_LLVM_VER < 1600 llvm::Optional> getExplicitPlatformLibs(); +#else +std::optional> getExplicitPlatformLibs(); +#endif /** * Returns the value of -mscrtlib. diff --git a/gen/dcompute/druntime.cpp b/gen/dcompute/druntime.cpp index 6867765466e..aee93f4c532 100644 --- a/gen/dcompute/druntime.cpp +++ b/gen/dcompute/druntime.cpp @@ -42,8 +42,13 @@ bool isFromLDC_OpenCL(Dsymbol *sym) { } llvm::Optional toDcomputePointer(StructDeclaration *sd) { - if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) + if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) { +#if LDC_LLVM_VER < 1600 return llvm::Optional(llvm::None); +#else + return std::optional(std::nullopt); +#endif + } TemplateInstance *ti = sd->isInstantiated(); int addrspace = isExpression((*ti->tiargs)[0])->toInteger(); diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 1e817ee3f4a..f7068294dd9 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -372,7 +372,11 @@ DIType DIBuilder::CreateEnumType(TypeEnum *type) { DIType DIBuilder::CreatePointerType(TypePointer *type) { // TODO: The addressspace is important for dcompute targets. See e.g. // https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00326.html +#if LDC_LLVM_VER < 1600 const llvm::Optional DWARFAddressSpace = llvm::None; +#else + const std::optional DWARFAddressSpace = std::nullopt; +#endif const auto name = processDIName(type->toPrettyChars(true)); @@ -730,7 +734,11 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type) { } DISubroutineType DIBuilder::CreateEmptyFunctionType() { +#if LDC_LLVM_VER < 1600 auto paramsArray = DBuilder.getOrCreateTypeArray(llvm::None); +#else + auto paramsArray = DBuilder.getOrCreateTypeArray(std::nullopt); +#endif return DBuilder.createSubroutineType(paramsArray); } @@ -774,9 +782,16 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { return nullptr; if (t->ty == TY::Tnull) { // display null as void* +#if LDC_LLVM_VER < 1600 return DBuilder.createPointerType( CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, /* DWARFAddressSpace */ llvm::None, "typeof(null)"); +#else + return DBuilder.createPointerType( + CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, + /* DWARFAddressSpace */ std::nullopt, "typeof(null)"); +#endif + } if (auto te = t->isTypeEnum()) return CreateEnumType(te); @@ -798,8 +813,14 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { const auto aggregateDIType = CreateCompositeType(t); const auto name = (tc->sym->toPrettyChars(true) + llvm::StringRef("*")).str(); +#if LDC_LLVM_VER < 1600 return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, llvm::None, processDIName(name)); +#else + return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, + std::nullopt, processDIName(name)); +#endif + } if (auto tf = t->isTypeFunction()) return CreateFunctionType(tf); diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index d07d4bcb223..dc661b01fb0 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -574,8 +574,13 @@ static llvm::Optional getPGOOptions() { PGOOptions::CSPGOAction::NoCSAction, debugInfoForProfiling, pseudoProbeForProfiling); } +#if LDC_LLVM_VER < 1600 return None; +#else + return std::nullopt; +#endif } + static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsigned sizeLevelVal) { PipelineTuningOptions pto; diff --git a/gen/uda.cpp b/gen/uda.cpp index 371b565b38a..521205c5da7 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -219,7 +219,11 @@ void applyAttrAllocSize(StructLiteralExp *sle, IrFunction *irFunc) { if (numArgIdx >= 0) { builder.addAllocSizeAttr(llvmSizeIdx, llvmNumIdx); } else { +#if LDC_LLVM_VER < 1600 builder.addAllocSizeAttr(llvmSizeIdx, llvm::Optional()); +#else + builder.addAllocSizeAttr(llvmSizeIdx, std::optional()); +#endif } llvm::Function *func = irFunc->getLLVMFunc(); diff --git a/utils/not.cpp b/utils/not.cpp index b434af73315..7ca8d1d8a66 100644 --- a/utils/not.cpp +++ b/utils/not.cpp @@ -43,7 +43,11 @@ int main(int argc, const char **argv) { Argv.reserve(argc); for (int i = 0; i < argc; ++i) Argv.push_back(argv[i]); +#if LDC_LLVM_VER < 1600 auto Env = llvm::None; +#else + auto Env = std::nullopt; +#endif std::string ErrMsg; int Result = sys::ExecuteAndWait(*Program, Argv, Env, {}, 0, 0, &ErrMsg); From f3e5276cd9778be4cf30904af20136910dc1867a Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Mon, 22 May 2023 12:02:28 +0100 Subject: [PATCH 170/301] ldc-profdata: Add llvm-profdata from LLVM release/16.x Modified to have an explicit `main()` per the other profdata imports in this tree. Signed-off-by: Ikey Doherty --- tools/ldc-profdata/llvm-profdata-16.0.cpp | 3013 +++++++++++++++++++++ 1 file changed, 3013 insertions(+) create mode 100644 tools/ldc-profdata/llvm-profdata-16.0.cpp diff --git a/tools/ldc-profdata/llvm-profdata-16.0.cpp b/tools/ldc-profdata/llvm-profdata-16.0.cpp new file mode 100644 index 00000000000..57dfd18076c --- /dev/null +++ b/tools/ldc-profdata/llvm-profdata-16.0.cpp @@ -0,0 +1,3013 @@ +//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// llvm-profdata merges .profdata files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Object/Binary.h" +#include "llvm/ProfileData/InstrProfCorrelator.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/InstrProfWriter.h" +#include "llvm/ProfileData/MemProf.h" +#include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/ProfileData/RawMemProfReader.h" +#include "llvm/ProfileData/SampleProfReader.h" +#include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Discriminator.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; + +// We use this string to indicate that there are +// multiple static functions map to the same name. +const std::string DuplicateNameStr = "----"; + +enum ProfileFormat { + PF_None = 0, + PF_Text, + PF_Compact_Binary, + PF_Ext_Binary, + PF_GCC, + PF_Binary +}; + +enum class ShowFormat { Text, Json, Yaml }; + +static void warn(Twine Message, std::string Whence = "", + std::string Hint = "") { + WithColor::warning(); + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; +} + +static void warn(Error E, StringRef Whence = "") { + if (E.isA()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + warn(IPE.message(), std::string(Whence), std::string("")); + }); + } +} + +static void exitWithError(Twine Message, std::string Whence = "", + std::string Hint = "") { + WithColor::error(); + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; + ::exit(1); +} + +static void exitWithError(Error E, StringRef Whence = "") { + if (E.isA()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + instrprof_error instrError = IPE.get(); + StringRef Hint = ""; + if (instrError == instrprof_error::unrecognized_format) { + // Hint in case user missed specifying the profile type. + Hint = "Perhaps you forgot to use the --sample or --memory option?"; + } + exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); + }); + return; + } + + exitWithError(toString(std::move(E)), std::string(Whence)); +} + +static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { + exitWithError(EC.message(), std::string(Whence)); +} + +namespace { +enum ProfileKinds { instr, sample, memory }; +enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; +} + +static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, + StringRef Whence = "") { + if (FailMode == failIfAnyAreInvalid) + exitWithErrorCode(EC, Whence); + else + warn(EC.message(), std::string(Whence)); +} + +static void handleMergeWriterError(Error E, StringRef WhenceFile = "", + StringRef WhenceFunction = "", + bool ShowHint = true) { + if (!WhenceFile.empty()) + errs() << WhenceFile << ": "; + if (!WhenceFunction.empty()) + errs() << WhenceFunction << ": "; + + auto IPE = instrprof_error::success; + E = handleErrors(std::move(E), + [&IPE](std::unique_ptr E) -> Error { + IPE = E->get(); + return Error(std::move(E)); + }); + errs() << toString(std::move(E)) << "\n"; + + if (ShowHint) { + StringRef Hint = ""; + if (IPE != instrprof_error::success) { + switch (IPE) { + case instrprof_error::hash_mismatch: + case instrprof_error::count_mismatch: + case instrprof_error::value_site_count_mismatch: + Hint = "Make sure that all profile data to be merged is generated " + "from the same binary."; + break; + default: + break; + } + } + + if (!Hint.empty()) + errs() << Hint << "\n"; + } +} + +namespace { +/// A remapper from original symbol names to new symbol names based on a file +/// containing a list of mappings from old name to new name. +class SymbolRemapper { + std::unique_ptr File; + DenseMap RemappingTable; + +public: + /// Build a SymbolRemapper from a file containing a list of old/new symbols. + static std::unique_ptr create(StringRef InputFile) { + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + auto Remapper = std::make_unique(); + Remapper->File = std::move(BufOrError.get()); + + for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + std::pair Parts = LineIt->split(' '); + if (Parts.first.empty() || Parts.second.empty() || + Parts.second.count(' ')) { + exitWithError("unexpected line in remapping file", + (InputFile + ":" + Twine(LineIt.line_number())).str(), + "expected 'old_symbol new_symbol'"); + } + Remapper->RemappingTable.insert(Parts); + } + return Remapper; + } + + /// Attempt to map the given old symbol into a new symbol. + /// + /// \return The new symbol, or \p Name if no such symbol was found. + StringRef operator()(StringRef Name) { + StringRef New = RemappingTable.lookup(Name); + return New.empty() ? Name : New; + } +}; +} + +struct WeightedFile { + std::string Filename; + uint64_t Weight; +}; +typedef SmallVector WeightedFileVector; + +/// Keep track of merged data and reported errors. +struct WriterContext { + std::mutex Lock; + InstrProfWriter Writer; + std::vector> Errors; + std::mutex &ErrLock; + SmallSet &WriterErrorCodes; + + WriterContext(bool IsSparse, std::mutex &ErrLock, + SmallSet &WriterErrorCodes) + : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) { + } +}; + +/// Computer the overlap b/w profile BaseFilename and TestFileName, +/// and store the program level result to Overlap. +static void overlapInput(const std::string &BaseFilename, + const std::string &TestFilename, WriterContext *WC, + OverlapStats &Overlap, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + auto ReaderOrErr = InstrProfReader::create(TestFilename); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Errors.emplace_back(make_error(IPE), TestFilename); + return; + } + + auto Reader = std::move(ReaderOrErr.get()); + for (auto &I : *Reader) { + OverlapStats FuncOverlap(OverlapStats::FunctionLevel); + FuncOverlap.setFuncInfo(I.Name, I.Hash); + + WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); + FuncOverlap.dump(OS); + } +} + +/// Load an input into a writer context. +static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, + const InstrProfCorrelator *Correlator, + const StringRef ProfiledBinary, WriterContext *WC) { + std::unique_lock CtxGuard{WC->Lock}; + + // Copy the filename, because llvm::ThreadPool copied the input "const + // WeightedFile &" by value, making a reference to the filename within it + // invalid outside of this packaged task. + std::string Filename = Input.Filename; + + using ::llvm::memprof::RawMemProfReader; + if (RawMemProfReader::hasFormat(Input.Filename)) { + auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary); + if (!ReaderOrErr) { + exitWithError(ReaderOrErr.takeError(), Input.Filename); + } + std::unique_ptr Reader = std::move(ReaderOrErr.get()); + // Check if the profile types can be merged, e.g. clang frontend profiles + // should not be merged with memprof profiles. + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Cannot merge MemProf profile with Clang generated profile.", + std::error_code()), + Filename); + return; + } + + auto MemProfError = [&](Error E) { + instrprof_error IPE = InstrProfError::take(std::move(E)); + WC->Errors.emplace_back(make_error(IPE), Filename); + }; + + // Add the frame mappings into the writer context. + const auto &IdToFrame = Reader->getFrameMapping(); + for (const auto &I : IdToFrame) { + bool Succeeded = WC->Writer.addMemProfFrame( + /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError); + // If we weren't able to add the frame mappings then it doesn't make sense + // to try to add the records from this profile. + if (!Succeeded) + return; + } + const auto &FunctionProfileData = Reader->getProfileData(); + // Add the memprof records into the writer context. + for (const auto &I : FunctionProfileData) { + WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second); + } + return; + } + + auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Errors.emplace_back(make_error(IPE), Filename); + return; + } + + auto Reader = std::move(ReaderOrErr.get()); + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Merge IR generated profile with Clang generated profile.", + std::error_code()), + Filename); + return; + } + + for (auto &I : *Reader) { + if (Remapper) + I.Name = (*Remapper)(I.Name); + const StringRef FuncName = I.Name; + bool Reported = false; + WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { + if (Reported) { + consumeError(std::move(E)); + return; + } + Reported = true; + // Only show hint the first time an error occurs. + instrprof_error IPE = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{WC->ErrLock}; + bool firstTime = WC->WriterErrorCodes.insert(IPE).second; + handleMergeWriterError(make_error(IPE), Input.Filename, + FuncName, firstTime); + }); + } + + if (Reader->hasError()) { + if (Error E = Reader->getError()) + WC->Errors.emplace_back(std::move(E), Filename); + } + + std::vector BinaryIds; + if (Error E = Reader->readBinaryIds(BinaryIds)) + WC->Errors.emplace_back(std::move(E), Filename); + WC->Writer.addBinaryIds(BinaryIds); +} + +/// Merge the \p Src writer context into \p Dst. +static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { + for (auto &ErrorPair : Src->Errors) + Dst->Errors.push_back(std::move(ErrorPair)); + Src->Errors.clear(); + + if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) + exitWithError(std::move(E)); + + Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { + instrprof_error IPE = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{Dst->ErrLock}; + bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; + if (firstTime) + warn(toString(make_error(IPE))); + }); +} + +static void writeInstrProfile(StringRef OutputFilename, + ProfileFormat OutputFormat, + InstrProfWriter &Writer) { + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, + OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (OutputFormat == PF_Text) { + if (Error E = Writer.writeText(Output)) + warn(std::move(E)); + } else { + if (Output.is_displayed()) + exitWithError("cannot write a non-text format profile to the terminal"); + if (Error E = Writer.write(Output)) + warn(std::move(E)); + } +} + +static void mergeInstrProfile(const WeightedFileVector &Inputs, + StringRef DebugInfoFilename, + SymbolRemapper *Remapper, + StringRef OutputFilename, + ProfileFormat OutputFormat, bool OutputSparse, + unsigned NumThreads, FailureMode FailMode, + const StringRef ProfiledBinary) { + if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && + OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) + exitWithError("unknown format is specified"); + + std::unique_ptr Correlator; + if (!DebugInfoFilename.empty()) { + if (auto Err = + InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator)) + exitWithError(std::move(Err), DebugInfoFilename); + if (auto Err = Correlator->correlateProfileData()) + exitWithError(std::move(Err), DebugInfoFilename); + } + + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + + // If NumThreads is not specified, auto-detect a good default. + if (NumThreads == 0) + NumThreads = std::min(hardware_concurrency().compute_thread_count(), + unsigned((Inputs.size() + 1) / 2)); + + // Initialize the writer contexts. + SmallVector, 4> Contexts; + for (unsigned I = 0; I < NumThreads; ++I) + Contexts.emplace_back(std::make_unique( + OutputSparse, ErrorLock, WriterErrorCodes)); + + if (NumThreads == 1) { + for (const auto &Input : Inputs) + loadInput(Input, Remapper, Correlator.get(), ProfiledBinary, + Contexts[0].get()); + } else { + ThreadPool Pool(hardware_concurrency(NumThreads)); + + // Load the inputs in parallel (N/NumThreads serial steps). + unsigned Ctx = 0; + for (const auto &Input : Inputs) { + Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary, + Contexts[Ctx].get()); + Ctx = (Ctx + 1) % NumThreads; + } + Pool.wait(); + + // Merge the writer contexts together (~ lg(NumThreads) serial steps). + unsigned Mid = Contexts.size() / 2; + unsigned End = Contexts.size(); + assert(Mid > 0 && "Expected more than one context"); + do { + for (unsigned I = 0; I < Mid; ++I) + Pool.async(mergeWriterContexts, Contexts[I].get(), + Contexts[I + Mid].get()); + Pool.wait(); + if (End & 1) { + Pool.async(mergeWriterContexts, Contexts[0].get(), + Contexts[End - 1].get()); + Pool.wait(); + } + End = Mid; + Mid /= 2; + } while (Mid > 0); + } + + // Handle deferred errors encountered during merging. If the number of errors + // is equal to the number of inputs the merge failed. + unsigned NumErrors = 0; + for (std::unique_ptr &WC : Contexts) { + for (auto &ErrorPair : WC->Errors) { + ++NumErrors; + warn(toString(std::move(ErrorPair.first)), ErrorPair.second); + } + } + if (NumErrors == Inputs.size() || + (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) + exitWithError("no profile can be merged"); + + writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); +} + +/// The profile entry for a function in instrumentation profile. +struct InstrProfileEntry { + uint64_t MaxCount = 0; + uint64_t NumEdgeCounters = 0; + float ZeroCounterRatio = 0.0; + InstrProfRecord *ProfRecord; + InstrProfileEntry(InstrProfRecord *Record); + InstrProfileEntry() = default; +}; + +InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { + ProfRecord = Record; + uint64_t CntNum = Record->Counts.size(); + uint64_t ZeroCntNum = 0; + for (size_t I = 0; I < CntNum; ++I) { + MaxCount = std::max(MaxCount, Record->Counts[I]); + ZeroCntNum += !Record->Counts[I]; + } + ZeroCounterRatio = (float)ZeroCntNum / CntNum; + NumEdgeCounters = CntNum; +} + +/// Either set all the counters in the instr profile entry \p IFE to +/// -1 / -2 /in order to drop the profile or scale up the +/// counters in \p IFP to be above hot / cold threshold. We use +/// the ratio of zero counters in the profile of a function to +/// decide the profile is helpful or harmful for performance, +/// and to choose whether to scale up or drop it. +static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot, + uint64_t HotInstrThreshold, + uint64_t ColdInstrThreshold, + float ZeroCounterThreshold) { + InstrProfRecord *ProfRecord = IFE.ProfRecord; + if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { + // If all or most of the counters of the function are zero, the + // profile is unaccountable and should be dropped. Reset all the + // counters to be -1 / -2 and PGO profile-use will drop the profile. + // All counters being -1 also implies that the function is hot so + // PGO profile-use will also set the entry count metadata to be + // above hot threshold. + // All counters being -2 implies that the function is warm so + // PGO profile-use will also set the entry count metadata to be + // above cold threshold. + auto Kind = + (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm); + ProfRecord->setPseudoCount(Kind); + return; + } + + // Scale up the MaxCount to be multiple times above hot / cold threshold. + const unsigned MultiplyFactor = 3; + uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold); + uint64_t Numerator = Threshold * MultiplyFactor; + + // Make sure Threshold for warm counters is below the HotInstrThreshold. + if (!SetToHot && Threshold >= HotInstrThreshold) { + Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2; + } + + uint64_t Denominator = IFE.MaxCount; + if (Numerator <= Denominator) + return; + ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { + warn(toString(make_error(E))); + }); +} + +const uint64_t ColdPercentileIdx = 15; +const uint64_t HotPercentileIdx = 11; + +using sampleprof::FSDiscriminatorPass; + +// Internal options to set FSDiscriminatorPass. Used in merge and show +// commands. +static cl::opt FSDiscriminatorPassOption( + "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, + cl::desc("Zero out the discriminator bits for the FS discrimiantor " + "pass beyond this value. The enum values are defined in " + "Support/Discriminator.h"), + cl::values(clEnumVal(Base, "Use base discriminators only"), + clEnumVal(Pass1, "Use base and pass 1 discriminators"), + clEnumVal(Pass2, "Use base and pass 1-2 discriminators"), + clEnumVal(Pass3, "Use base and pass 1-3 discriminators"), + clEnumVal(PassLast, "Use all discriminator bits (default)"))); + +static unsigned getDiscriminatorMask() { + return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); +} + +/// Adjust the instr profile in \p WC based on the sample profile in +/// \p Reader. +static void +adjustInstrProfile(std::unique_ptr &WC, + std::unique_ptr &Reader, + unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, + unsigned InstrProfColdThreshold) { + // Function to its entry in instr profile. + StringMap InstrProfileMap; + StringMap StaticFuncMap; + InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); + + auto checkSampleProfileHasFUnique = [&Reader]() { + for (const auto &PD : Reader->getProfiles()) { + auto &FContext = PD.first; + if (FContext.toString().find(FunctionSamples::UniqSuffix) != + std::string::npos) { + return true; + } + } + return false; + }; + + bool SampleProfileHasFUnique = checkSampleProfileHasFUnique(); + + auto buildStaticFuncMap = [&StaticFuncMap, + SampleProfileHasFUnique](const StringRef Name) { + std::string Prefixes[] = {".cpp:", "cc:", ".c:", ".hpp:", ".h:"}; + size_t PrefixPos = StringRef::npos; + for (auto &Prefix : Prefixes) { + PrefixPos = Name.find_insensitive(Prefix); + if (PrefixPos == StringRef::npos) + continue; + PrefixPos += Prefix.size(); + break; + } + + if (PrefixPos == StringRef::npos) { + return; + } + + StringRef NewName = Name.drop_front(PrefixPos); + StringRef FName = Name.substr(0, PrefixPos - 1); + if (NewName.size() == 0) { + return; + } + + // This name should have a static linkage. + size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix); + bool ProfileHasFUnique = (PostfixPos != StringRef::npos); + + // If sample profile and instrumented profile do not agree on symbol + // uniqification. + if (SampleProfileHasFUnique != ProfileHasFUnique) { + // If instrumented profile uses -funique-internal-linakge-symbols, + // we need to trim the name. + if (ProfileHasFUnique) { + NewName = NewName.substr(0, PostfixPos); + } else { + // If sample profile uses -funique-internal-linakge-symbols, + // we build the map. + std::string NStr = + NewName.str() + getUniqueInternalLinkagePostfix(FName); + NewName = StringRef(NStr); + StaticFuncMap[NewName] = Name; + return; + } + } + + if (StaticFuncMap.find(NewName) == StaticFuncMap.end()) { + StaticFuncMap[NewName] = Name; + } else { + StaticFuncMap[NewName] = DuplicateNameStr; + } + }; + + // We need to flatten the SampleFDO profile as the InstrFDO + // profile does not have inlined callsite profiles. + // One caveat is the pre-inlined function -- their samples + // should be collapsed into the caller function. + // Here we do a DFS traversal to get the flatten profile + // info: the sum of entrycount and the max of maxcount. + // Here is the algorithm: + // recursive (FS, root_name) { + // name = FS->getName(); + // get samples for FS; + // if (InstrProf.find(name) { + // root_name = name; + // } else { + // if (name is in static_func map) { + // root_name = static_name; + // } + // } + // update the Map entry for root_name; + // for (subfs: FS) { + // recursive(subfs, root_name); + // } + // } + // + // Here is an example. + // + // SampleProfile: + // foo:12345:1000 + // 1: 1000 + // 2.1: 1000 + // 15: 5000 + // 4: bar:1000 + // 1: 1000 + // 2: goo:3000 + // 1: 3000 + // 8: bar:40000 + // 1: 10000 + // 2: goo:30000 + // 1: 30000 + // + // InstrProfile has two entries: + // foo + // bar.cc:bar + // + // After BuildMaxSampleMap, we should have the following in FlattenSampleMap: + // {"foo", {1000, 5000}} + // {"bar.cc:bar", {11000, 30000}} + // + // foo's has an entry count of 1000, and max body count of 5000. + // bar.cc:bar has an entry count of 11000 (sum two callsites of 1000 and + // 10000), and max count of 30000 (from the callsite in line 8). + // + // Note that goo's count will remain in bar.cc:bar() as it does not have an + // entry in InstrProfile. + DenseMap> FlattenSampleMap; + auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap, + &InstrProfileMap](const FunctionSamples &FS, + const StringRef &RootName) { + auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS, + const StringRef &RootName, + auto &BuildImpl) -> void { + const StringRef &Name = FS.getName(); + const StringRef *NewRootName = &RootName; + uint64_t EntrySample = FS.getHeadSamplesEstimate(); + uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true); + + auto It = InstrProfileMap.find(Name); + if (It != InstrProfileMap.end()) { + NewRootName = &Name; + } else { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second != DuplicateNameStr) { + NewRootName = &NewName->second; + } + } else { + // Here the EntrySample is of an inlined function, so we should not + // update the EntrySample in the map. + EntrySample = 0; + } + } + EntrySample += FlattenSampleMap[*NewRootName].first; + MaxBodySample = + std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample); + FlattenSampleMap[*NewRootName] = + std::make_pair(EntrySample, MaxBodySample); + + for (const auto &C : FS.getCallsiteSamples()) + for (const auto &F : C.second) + BuildImpl(F.second, *NewRootName, BuildImpl); + }; + BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl); + }; + + for (auto &PD : WC->Writer.getProfileData()) { + // Populate IPBuilder. + for (const auto &PDV : PD.getValue()) { + InstrProfRecord Record = PDV.second; + IPBuilder.addRecord(Record); + } + + // If a function has multiple entries in instr profile, skip it. + if (PD.getValue().size() != 1) + continue; + + // Initialize InstrProfileMap. + InstrProfRecord *R = &PD.getValue().begin()->second; + StringRef FullName = PD.getKey(); + InstrProfileMap[FullName] = InstrProfileEntry(R); + buildStaticFuncMap(FullName); + } + + for (auto &PD : Reader->getProfiles()) { + sampleprof::FunctionSamples &FS = PD.second; + BuildMaxSampleMap(FS, FS.getName()); + } + + ProfileSummary InstrPS = *IPBuilder.getSummary(); + ProfileSummary SamplePS = Reader->getSummary(); + + // Compute cold thresholds for instr profile and sample profile. + uint64_t HotSampleThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + SamplePS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) + .MinCount; + uint64_t ColdSampleThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + SamplePS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) + .MinCount; + uint64_t HotInstrThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + InstrPS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) + .MinCount; + uint64_t ColdInstrThreshold = + InstrProfColdThreshold + ? InstrProfColdThreshold + : ProfileSummaryBuilder::getEntryForPercentile( + InstrPS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) + .MinCount; + + // Find hot/warm functions in sample profile which is cold in instr profile + // and adjust the profiles of those functions in the instr profile. + for (const auto &E : FlattenSampleMap) { + uint64_t SampleMaxCount = std::max(E.second.first, E.second.second); + if (SampleMaxCount < ColdSampleThreshold) + continue; + const StringRef &Name = E.first; + auto It = InstrProfileMap.find(Name); + if (It == InstrProfileMap.end()) { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second == DuplicateNameStr) { + WithColor::warning() + << "Static function " << Name + << " has multiple promoted names, cannot adjust profile.\n"; + } + } + } + if (It == InstrProfileMap.end() || + It->second.MaxCount > ColdInstrThreshold || + It->second.NumEdgeCounters < SupplMinSizeThreshold) + continue; + bool SetToHot = SampleMaxCount >= HotSampleThreshold; + updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold, + ColdInstrThreshold, ZeroCounterThreshold); + } +} + +/// The main function to supplement instr profile with sample profile. +/// \Inputs contains the instr profile. \p SampleFilename specifies the +/// sample profile. \p OutputFilename specifies the output profile name. +/// \p OutputFormat specifies the output profile format. \p OutputSparse +/// specifies whether to generate sparse profile. \p SupplMinSizeThreshold +/// specifies the minimal size for the functions whose profile will be +/// adjusted. \p ZeroCounterThreshold is the threshold to check whether +/// a function contains too many zero counters and whether its profile +/// should be dropped. \p InstrProfColdThreshold is the user specified +/// cold threshold which will override the cold threshold got from the +/// instr profile summary. +static void supplementInstrProfile( + const WeightedFileVector &Inputs, StringRef SampleFilename, + StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, + unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, + unsigned InstrProfColdThreshold) { + if (OutputFilename.compare("-") == 0) + exitWithError("cannot write indexed profdata format to stdout"); + if (Inputs.size() != 1) + exitWithError("expect one input to be an instr profile"); + if (Inputs[0].Weight != 1) + exitWithError("expect instr profile doesn't have weight"); + + StringRef InstrFilename = Inputs[0].Filename; + + // Read sample profile. + LLVMContext Context; + auto ReaderOrErr = sampleprof::SampleProfileReader::create( + SampleFilename.str(), Context, FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithErrorCode(EC, SampleFilename); + auto Reader = std::move(ReaderOrErr.get()); + if (std::error_code EC = Reader->read()) + exitWithErrorCode(EC, SampleFilename); + + // Read instr profile. + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + auto WC = std::make_unique(OutputSparse, ErrorLock, + WriterErrorCodes); + loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get()); + if (WC->Errors.size() > 0) + exitWithError(std::move(WC->Errors[0].first), InstrFilename); + + adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold, + InstrProfColdThreshold); + writeInstrProfile(OutputFilename, OutputFormat, WC->Writer); +} + +/// Make a copy of the given function samples with all symbol names remapped +/// by the provided symbol remapper. +static sampleprof::FunctionSamples +remapSamples(const sampleprof::FunctionSamples &Samples, + SymbolRemapper &Remapper, sampleprof_error &Error) { + sampleprof::FunctionSamples Result; + Result.setName(Remapper(Samples.getName())); + Result.addTotalSamples(Samples.getTotalSamples()); + Result.addHeadSamples(Samples.getHeadSamples()); + for (const auto &BodySample : Samples.getBodySamples()) { + uint32_t MaskedDiscriminator = + BodySample.first.Discriminator & getDiscriminatorMask(); + Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, + BodySample.second.getSamples()); + for (const auto &Target : BodySample.second.getCallTargets()) { + Result.addCalledTargetSamples(BodySample.first.LineOffset, + MaskedDiscriminator, + Remapper(Target.first()), Target.second); + } + } + for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { + sampleprof::FunctionSamplesMap &Target = + Result.functionSamplesAt(CallsiteSamples.first); + for (const auto &Callsite : CallsiteSamples.second) { + sampleprof::FunctionSamples Remapped = + remapSamples(Callsite.second, Remapper, Error); + MergeResult(Error, + Target[std::string(Remapped.getName())].merge(Remapped)); + } + } + return Result; +} + +static sampleprof::SampleProfileFormat FormatMap[] = { + sampleprof::SPF_None, + sampleprof::SPF_Text, + sampleprof::SPF_Compact_Binary, + sampleprof::SPF_Ext_Binary, + sampleprof::SPF_GCC, + sampleprof::SPF_Binary}; + +static std::unique_ptr +getInputFileBuf(const StringRef &InputFile) { + if (InputFile == "") + return {}; + + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + return std::move(*BufOrError); +} + +static void populateProfileSymbolList(MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &PSL) { + if (!Buffer) + return; + + SmallVector SymbolVec; + StringRef Data = Buffer->getBuffer(); + Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + for (StringRef SymbolStr : SymbolVec) + PSL.add(SymbolStr.trim()); +} + +static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, + ProfileFormat OutputFormat, + MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &WriterList, + bool CompressAllSections, bool UseMD5, + bool GenPartialProfile) { + populateProfileSymbolList(Buffer, WriterList); + if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) + warn("Profile Symbol list is not empty but the output format is not " + "ExtBinary format. The list will be lost in the output. "); + + Writer.setProfileSymbolList(&WriterList); + + if (CompressAllSections) { + if (OutputFormat != PF_Ext_Binary) + warn("-compress-all-section is ignored. Specify -extbinary to enable it"); + else + Writer.setToCompressAllSections(); + } + if (UseMD5) { + if (OutputFormat != PF_Ext_Binary) + warn("-use-md5 is ignored. Specify -extbinary to enable it"); + else + Writer.setUseMD5(); + } + if (GenPartialProfile) { + if (OutputFormat != PF_Ext_Binary) + warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); + else + Writer.setPartialProfile(); + } +} + +static void +mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, + StringRef OutputFilename, ProfileFormat OutputFormat, + StringRef ProfileSymbolListFile, bool CompressAllSections, + bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile, + bool SampleMergeColdContext, bool SampleTrimColdContext, + bool SampleColdContextFrameDepth, FailureMode FailMode, + bool DropProfileSymbolList) { + using namespace sampleprof; + SampleProfileMap ProfileMap; + SmallVector, 5> Readers; + LLVMContext Context; + sampleprof::ProfileSymbolList WriterList; + std::optional ProfileIsProbeBased; + std::optional ProfileIsCS; + for (const auto &Input : Inputs) { + auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + continue; + } + + // We need to keep the readers around until after all the files are + // read so that we do not lose the function names stored in each + // reader's memory. The function names are needed to write out the + // merged profile map. + Readers.push_back(std::move(ReaderOrErr.get())); + const auto Reader = Readers.back().get(); + if (std::error_code EC = Reader->read()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + Readers.pop_back(); + continue; + } + + SampleProfileMap &Profiles = Reader->getProfiles(); + if (ProfileIsProbeBased && + ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) + exitWithError( + "cannot merge probe-based profile with non-probe-based profile"); + ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; + if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS) + exitWithError("cannot merge CS profile with non-CS profile"); + ProfileIsCS = FunctionSamples::ProfileIsCS; + for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end(); + I != E; ++I) { + sampleprof_error Result = sampleprof_error::success; + FunctionSamples Remapped = + Remapper ? remapSamples(I->second, *Remapper, Result) + : FunctionSamples(); + FunctionSamples &Samples = Remapper ? Remapped : I->second; + SampleContext FContext = Samples.getContext(); + MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight)); + if (Result != sampleprof_error::success) { + std::error_code EC = make_error_code(Result); + handleMergeWriterError(errorCodeToError(EC), Input.Filename, + FContext.toString()); + } + } + + if (!DropProfileSymbolList) { + std::unique_ptr ReaderList = + Reader->getProfileSymbolList(); + if (ReaderList) + WriterList.merge(*ReaderList); + } + } + + if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { + // Use threshold calculated from profile summary unless specified. + SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); + auto Summary = Builder.computeSummaryForProfiles(ProfileMap); + uint64_t SampleProfColdThreshold = + ProfileSummaryBuilder::getColdCountThreshold( + (Summary->getDetailedSummary())); + + // Trim and merge cold context profile using cold threshold above; + SampleContextTrimmer(ProfileMap) + .trimAndMergeColdContextProfiles( + SampleProfColdThreshold, SampleTrimColdContext, + SampleMergeColdContext, SampleColdContextFrameDepth, false); + } + + if (ProfileIsCS && GenCSNestedProfile) { + CSProfileConverter CSConverter(ProfileMap); + CSConverter.convertProfiles(); + ProfileIsCS = FunctionSamples::ProfileIsCS = false; + } + + auto WriterOrErr = + SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); + if (std::error_code EC = WriterOrErr.getError()) + exitWithErrorCode(EC, OutputFilename); + + auto Writer = std::move(WriterOrErr.get()); + // WriterList will have StringRef refering to string in Buffer. + // Make sure Buffer lives as long as WriterList. + auto Buffer = getInputFileBuf(ProfileSymbolListFile); + handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, + CompressAllSections, UseMD5, GenPartialProfile); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithErrorCode(std::move(EC)); +} + +static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { + StringRef WeightStr, FileName; + std::tie(WeightStr, FileName) = WeightedFilename.split(','); + + uint64_t Weight; + if (WeightStr.getAsInteger(10, Weight) || Weight < 1) + exitWithError("input weight must be a positive integer"); + + return {std::string(FileName), Weight}; +} + +static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { + StringRef Filename = WF.Filename; + uint64_t Weight = WF.Weight; + + // If it's STDIN just pass it on. + if (Filename == "-") { + WNI.push_back({std::string(Filename), Weight}); + return; + } + + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(Filename, Status); + if (!llvm::sys::fs::exists(Status)) + exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), + Filename); + // If it's a source file, collect it. + if (llvm::sys::fs::is_regular_file(Status)) { + WNI.push_back({std::string(Filename), Weight}); + return; + } + + if (llvm::sys::fs::is_directory(Status)) { + std::error_code EC; + for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; + F != E && !EC; F.increment(EC)) { + if (llvm::sys::fs::is_regular_file(F->path())) { + addWeightedInput(WNI, {F->path(), Weight}); + } + } + if (EC) + exitWithErrorCode(EC, Filename); + } +} + +static void parseInputFilenamesFile(MemoryBuffer *Buffer, + WeightedFileVector &WFV) { + if (!Buffer) + return; + + SmallVector Entries; + StringRef Data = Buffer->getBuffer(); + Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + for (const StringRef &FileWeightEntry : Entries) { + StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); + // Skip comments. + if (SanitizedEntry.startswith("#")) + continue; + // If there's no comma, it's an unweighted profile. + else if (!SanitizedEntry.contains(',')) + addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); + else + addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); + } +} + +static int merge_main(int argc, const char *argv[]) { + cl::list InputFilenames(cl::Positional, + cl::desc("")); + cl::list WeightedInputFilenames("weighted-input", + cl::desc(",")); + cl::opt InputFilenamesFile( + "input-files", cl::init(""), + cl::desc("Path to file containing newline-separated " + "[,] entries")); + cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), + cl::aliasopt(InputFilenamesFile)); + cl::opt DumpInputFileList( + "dump-input-file-list", cl::init(false), cl::Hidden, + cl::desc("Dump the list of input files and their weights, then exit")); + cl::opt RemappingFile("remapping-file", cl::value_desc("file"), + cl::desc("Symbol remapping file")); + cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), + cl::aliasopt(RemappingFile)); + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"))); + cl::opt OutputFormat( + cl::desc("Format of output profile"), cl::init(PF_Binary), + cl::values( + clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), + clEnumValN(PF_Compact_Binary, "compbinary", + "Compact binary encoding"), + clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), + clEnumValN(PF_Text, "text", "Text encoding"), + clEnumValN(PF_GCC, "gcc", + "GCC encoding (only meaningful for -sample)"))); + cl::opt FailureMode( + "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), + cl::values(clEnumValN(failIfAnyAreInvalid, "any", + "Fail if any profile is invalid."), + clEnumValN(failIfAllAreInvalid, "all", + "Fail only if all profiles are invalid."))); + cl::opt OutputSparse("sparse", cl::init(false), + cl::desc("Generate a sparse profile (only meaningful for -instr)")); + cl::opt NumThreads( + "num-threads", cl::init(0), + cl::desc("Number of merge threads to use (default: autodetect)")); + cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), + cl::aliasopt(NumThreads)); + cl::opt ProfileSymbolListFile( + "prof-sym-list", cl::init(""), + cl::desc("Path to file containing the list of function symbols " + "used to populate profile symbol list")); + cl::opt CompressAllSections( + "compress-all-sections", cl::init(false), cl::Hidden, + cl::desc("Compress all sections when writing the profile (only " + "meaningful for -extbinary)")); + cl::opt UseMD5( + "use-md5", cl::init(false), cl::Hidden, + cl::desc("Choose to use MD5 to represent string in name table (only " + "meaningful for -extbinary)")); + cl::opt SampleMergeColdContext( + "sample-merge-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Merge context sample profiles whose count is below cold threshold")); + cl::opt SampleTrimColdContext( + "sample-trim-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Trim context sample profiles whose count is below cold threshold")); + cl::opt SampleColdContextFrameDepth( + "sample-frame-depth-for-cold-context", cl::init(1), + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); + cl::opt GenPartialProfile( + "gen-partial-profile", cl::init(false), cl::Hidden, + cl::desc("Generate a partial profile (only meaningful for -extbinary)")); + cl::opt SupplInstrWithSample( + "supplement-instr-with-sample", cl::init(""), cl::Hidden, + cl::desc("Supplement an instr profile with sample profile, to correct " + "the profile unrepresentativeness issue. The sample " + "profile is the input of the flag. Output will be in instr " + "format (The flag only works with -instr)")); + cl::opt ZeroCounterThreshold( + "zero-counter-threshold", cl::init(0.7), cl::Hidden, + cl::desc("For the function which is cold in instr profile but hot in " + "sample profile, if the ratio of the number of zero counters " + "divided by the total number of counters is above the " + "threshold, the profile of the function will be regarded as " + "being harmful for performance and will be dropped.")); + cl::opt SupplMinSizeThreshold( + "suppl-min-size-threshold", cl::init(10), cl::Hidden, + cl::desc("If the size of a function is smaller than the threshold, " + "assume it can be inlined by PGO early inliner and it won't " + "be adjusted based on sample profile.")); + cl::opt InstrProfColdThreshold( + "instr-prof-cold-threshold", cl::init(0), cl::Hidden, + cl::desc("User specified cold threshold for instr profile which will " + "override the cold threshold got from profile summary. ")); + cl::opt GenCSNestedProfile( + "gen-cs-nested-profile", cl::Hidden, cl::init(false), + cl::desc("Generate nested function profiles for CSSPGO")); + cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Use the provided debug info to correlate the raw profile.")); + cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected.")); + cl::opt DropProfileSymbolList( + "drop-profile-symbol-list", cl::init(false), cl::Hidden, + cl::desc("Drop the profile symbol list when merging AutoFDO profiles " + "(only meaningful for -sample)")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + + WeightedFileVector WeightedInputs; + for (StringRef Filename : InputFilenames) + addWeightedInput(WeightedInputs, {std::string(Filename), 1}); + for (StringRef WeightedFilename : WeightedInputFilenames) + addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); + + // Make sure that the file buffer stays alive for the duration of the + // weighted input vector's lifetime. + auto Buffer = getInputFileBuf(InputFilenamesFile); + parseInputFilenamesFile(Buffer.get(), WeightedInputs); + + if (WeightedInputs.empty()) + exitWithError("no input files specified. See " + + sys::path::filename(argv[0]) + " -help"); + + if (DumpInputFileList) { + for (auto &WF : WeightedInputs) + outs() << WF.Weight << "," << WF.Filename << "\n"; + return 0; + } + + std::unique_ptr Remapper; + if (!RemappingFile.empty()) + Remapper = SymbolRemapper::create(RemappingFile); + + if (!SupplInstrWithSample.empty()) { + if (ProfileKind != instr) + exitWithError( + "-supplement-instr-with-sample can only work with -instr. "); + + supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, + OutputFormat, OutputSparse, SupplMinSizeThreshold, + ZeroCounterThreshold, InstrProfColdThreshold); + return 0; + } + + if (ProfileKind == instr) + mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), + OutputFilename, OutputFormat, OutputSparse, NumThreads, + FailureMode, ProfiledBinary); + else + mergeSampleProfile( + WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, + ProfileSymbolListFile, CompressAllSections, UseMD5, GenPartialProfile, + GenCSNestedProfile, SampleMergeColdContext, SampleTrimColdContext, + SampleColdContextFrameDepth, FailureMode, DropProfileSymbolList); + return 0; +} + +/// Computer the overlap b/w profile BaseFilename and profile TestFilename. +static void overlapInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + WriterContext Context(false, ErrorLock, WriterErrorCodes); + WeightedFile WeightedInput{BaseFilename, 1}; + OverlapStats Overlap; + Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); + if (E) + exitWithError(std::move(E), "error in getting profile count sums"); + if (Overlap.Base.CountSum < 1.0f) { + OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; + exit(0); + } + if (Overlap.Test.CountSum < 1.0f) { + OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; + exit(0); + } + loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context); + overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, + IsCS); + Overlap.dump(OS); +} + +namespace { +struct SampleOverlapStats { + SampleContext BaseName; + SampleContext TestName; + // Number of overlap units + uint64_t OverlapCount; + // Total samples of overlap units + uint64_t OverlapSample; + // Number of and total samples of units that only present in base or test + // profile + uint64_t BaseUniqueCount; + uint64_t BaseUniqueSample; + uint64_t TestUniqueCount; + uint64_t TestUniqueSample; + // Number of units and total samples in base or test profile + uint64_t BaseCount; + uint64_t BaseSample; + uint64_t TestCount; + uint64_t TestSample; + // Number of and total samples of units that present in at least one profile + uint64_t UnionCount; + uint64_t UnionSample; + // Weighted similarity + double Similarity; + // For SampleOverlapStats instances representing functions, weights of the + // function in base and test profiles + double BaseWeight; + double TestWeight; + + SampleOverlapStats() + : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0), + BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0), + BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0), + UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {} +}; +} // end anonymous namespace + +namespace { +struct FuncSampleStats { + uint64_t SampleSum; + uint64_t MaxSample; + uint64_t HotBlockCount; + FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {} + FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample, + uint64_t HotBlockCount) + : SampleSum(SampleSum), MaxSample(MaxSample), + HotBlockCount(HotBlockCount) {} +}; +} // end anonymous namespace + +namespace { +enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None }; + +// Class for updating merging steps for two sorted maps. The class should be +// instantiated with a map iterator type. +template class MatchStep { +public: + MatchStep() = delete; + + MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd) + : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter), + SecondEnd(SecondEnd), Status(MS_None) {} + + bool areBothFinished() const { + return (FirstIter == FirstEnd && SecondIter == SecondEnd); + } + + bool isFirstFinished() const { return FirstIter == FirstEnd; } + + bool isSecondFinished() const { return SecondIter == SecondEnd; } + + /// Advance one step based on the previous match status unless the previous + /// status is MS_None. Then update Status based on the comparison between two + /// container iterators at the current step. If the previous status is + /// MS_None, it means two iterators are at the beginning and no comparison has + /// been made, so we simply update Status without advancing the iterators. + void updateOneStep(); + + T getFirstIter() const { return FirstIter; } + + T getSecondIter() const { return SecondIter; } + + MatchStatus getMatchStatus() const { return Status; } + +private: + // Current iterator and end iterator of the first container. + T FirstIter; + T FirstEnd; + // Current iterator and end iterator of the second container. + T SecondIter; + T SecondEnd; + // Match status of the current step. + MatchStatus Status; +}; +} // end anonymous namespace + +template void MatchStep::updateOneStep() { + switch (Status) { + case MS_Match: + ++FirstIter; + ++SecondIter; + break; + case MS_FirstUnique: + ++FirstIter; + break; + case MS_SecondUnique: + ++SecondIter; + break; + case MS_None: + break; + } + + // Update Status according to iterators at the current step. + if (areBothFinished()) + return; + if (FirstIter != FirstEnd && + (SecondIter == SecondEnd || FirstIter->first < SecondIter->first)) + Status = MS_FirstUnique; + else if (SecondIter != SecondEnd && + (FirstIter == FirstEnd || SecondIter->first < FirstIter->first)) + Status = MS_SecondUnique; + else + Status = MS_Match; +} + +// Return the sum of line/block samples, the max line/block sample, and the +// number of line/block samples above the given threshold in a function +// including its inlinees. +static void getFuncSampleStats(const sampleprof::FunctionSamples &Func, + FuncSampleStats &FuncStats, + uint64_t HotThreshold) { + for (const auto &L : Func.getBodySamples()) { + uint64_t Sample = L.second.getSamples(); + FuncStats.SampleSum += Sample; + FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample); + if (Sample >= HotThreshold) + ++FuncStats.HotBlockCount; + } + + for (const auto &C : Func.getCallsiteSamples()) { + for (const auto &F : C.second) + getFuncSampleStats(F.second, FuncStats, HotThreshold); + } +} + +/// Predicate that determines if a function is hot with a given threshold. We +/// keep it separate from its callsites for possible extension in the future. +static bool isFunctionHot(const FuncSampleStats &FuncStats, + uint64_t HotThreshold) { + // We intentionally compare the maximum sample count in a function with the + // HotThreshold to get an approximate determination on hot functions. + return (FuncStats.MaxSample >= HotThreshold); +} + +namespace { +class SampleOverlapAggregator { +public: + SampleOverlapAggregator(const std::string &BaseFilename, + const std::string &TestFilename, + double LowSimilarityThreshold, double Epsilon, + const OverlapFuncFilters &FuncFilter) + : BaseFilename(BaseFilename), TestFilename(TestFilename), + LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon), + FuncFilter(FuncFilter) {} + + /// Detect 0-sample input profile and report to output stream. This interface + /// should be called after loadProfiles(). + bool detectZeroSampleProfile(raw_fd_ostream &OS) const; + + /// Write out function-level similarity statistics for functions specified by + /// options --function, --value-cutoff, and --similarity-cutoff. + void dumpFuncSimilarity(raw_fd_ostream &OS) const; + + /// Write out program-level similarity and overlap statistics. + void dumpProgramSummary(raw_fd_ostream &OS) const; + + /// Write out hot-function and hot-block statistics for base_profile, + /// test_profile, and their overlap. For both cases, the overlap HO is + /// calculated as follows: + /// Given the number of functions (or blocks) that are hot in both profiles + /// HCommon and the number of functions (or blocks) that are hot in at + /// least one profile HUnion, HO = HCommon / HUnion. + void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const; + + /// This function tries matching functions in base and test profiles. For each + /// pair of matched functions, it aggregates the function-level + /// similarity into a profile-level similarity. It also dump function-level + /// similarity information of functions specified by --function, + /// --value-cutoff, and --similarity-cutoff options. The program-level + /// similarity PS is computed as follows: + /// Given function-level similarity FS(A) for all function A, the + /// weight of function A in base profile WB(A), and the weight of function + /// A in test profile WT(A), compute PS(base_profile, test_profile) = + /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0 + /// meaning no-overlap. + void computeSampleProfileOverlap(raw_fd_ostream &OS); + + /// Initialize ProfOverlap with the sum of samples in base and test + /// profiles. This function also computes and keeps the sum of samples and + /// max sample counts of each function in BaseStats and TestStats for later + /// use to avoid re-computations. + void initializeSampleProfileOverlap(); + + /// Load profiles specified by BaseFilename and TestFilename. + std::error_code loadProfiles(); + + using FuncSampleStatsMap = + std::unordered_map; + +private: + SampleOverlapStats ProfOverlap; + SampleOverlapStats HotFuncOverlap; + SampleOverlapStats HotBlockOverlap; + std::string BaseFilename; + std::string TestFilename; + std::unique_ptr BaseReader; + std::unique_ptr TestReader; + // BaseStats and TestStats hold FuncSampleStats for each function, with + // function name as the key. + FuncSampleStatsMap BaseStats; + FuncSampleStatsMap TestStats; + // Low similarity threshold in floating point number + double LowSimilarityThreshold; + // Block samples above BaseHotThreshold or TestHotThreshold are considered hot + // for tracking hot blocks. + uint64_t BaseHotThreshold; + uint64_t TestHotThreshold; + // A small threshold used to round the results of floating point accumulations + // to resolve imprecision. + const double Epsilon; + std::multimap> + FuncSimilarityDump; + // FuncFilter carries specifications in options --value-cutoff and + // --function. + OverlapFuncFilters FuncFilter; + // Column offsets for printing the function-level details table. + static const unsigned int TestWeightCol = 15; + static const unsigned int SimilarityCol = 30; + static const unsigned int OverlapCol = 43; + static const unsigned int BaseUniqueCol = 53; + static const unsigned int TestUniqueCol = 67; + static const unsigned int BaseSampleCol = 81; + static const unsigned int TestSampleCol = 96; + static const unsigned int FuncNameCol = 111; + + /// Return a similarity of two line/block sample counters in the same + /// function in base and test profiles. The line/block-similarity BS(i) is + /// computed as follows: + /// For an offsets i, given the sample count at i in base profile BB(i), + /// the sample count at i in test profile BT(i), the sum of sample counts + /// in this function in base profile SB, and the sum of sample counts in + /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB - + /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap. + double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample, + const SampleOverlapStats &FuncOverlap) const; + + void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample, + uint64_t HotBlockCount); + + void getHotFunctions(const FuncSampleStatsMap &ProfStats, + FuncSampleStatsMap &HotFunc, + uint64_t HotThreshold) const; + + void computeHotFuncOverlap(); + + /// This function updates statistics in FuncOverlap, HotBlockOverlap, and + /// Difference for two sample units in a matched function according to the + /// given match status. + void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample, + uint64_t HotBlockCount, + SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status); + + /// This function updates statistics in FuncOverlap, HotBlockOverlap, and + /// Difference for unmatched callees that only present in one profile in a + /// matched caller function. + void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func, + SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status); + + /// This function updates sample overlap statistics of an overlap function in + /// base and test profile. It also calculates a function-internal similarity + /// FIS as follows: + /// For offsets i that have samples in at least one profile in this + /// function A, given BS(i) returned by computeBlockSimilarity(), compute + /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with + /// 0.0 meaning no overlap. + double computeSampleFunctionInternalOverlap( + const sampleprof::FunctionSamples &BaseFunc, + const sampleprof::FunctionSamples &TestFunc, + SampleOverlapStats &FuncOverlap); + + /// Function-level similarity (FS) is a weighted value over function internal + /// similarity (FIS). This function computes a function's FS from its FIS by + /// applying the weight. + double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const; + + /// The function-level similarity FS(A) for a function A is computed as + /// follows: + /// Compute a function-internal similarity FIS(A) by + /// computeSampleFunctionInternalOverlap(). Then, with the weight of + /// function A in base profile WB(A), and the weight of function A in test + /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A))) + /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap. + double + computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc, + const sampleprof::FunctionSamples *TestFunc, + SampleOverlapStats *FuncOverlap, + uint64_t BaseFuncSample, + uint64_t TestFuncSample); + + /// Profile-level similarity (PS) is a weighted aggregate over function-level + /// similarities (FS). This method weights the FS value by the function + /// weights in the base and test profiles for the aggregation. + double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const; +}; +} // end anonymous namespace + +bool SampleOverlapAggregator::detectZeroSampleProfile( + raw_fd_ostream &OS) const { + bool HaveZeroSample = false; + if (ProfOverlap.BaseSample == 0) { + OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n"; + HaveZeroSample = true; + } + if (ProfOverlap.TestSample == 0) { + OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n"; + HaveZeroSample = true; + } + return HaveZeroSample; +} + +double SampleOverlapAggregator::computeBlockSimilarity( + uint64_t BaseSample, uint64_t TestSample, + const SampleOverlapStats &FuncOverlap) const { + double BaseFrac = 0.0; + double TestFrac = 0.0; + if (FuncOverlap.BaseSample > 0) + BaseFrac = static_cast(BaseSample) / FuncOverlap.BaseSample; + if (FuncOverlap.TestSample > 0) + TestFrac = static_cast(TestSample) / FuncOverlap.TestSample; + return 1.0 - std::fabs(BaseFrac - TestFrac); +} + +void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample, + uint64_t TestSample, + uint64_t HotBlockCount) { + bool IsBaseHot = (BaseSample >= BaseHotThreshold); + bool IsTestHot = (TestSample >= TestHotThreshold); + if (!IsBaseHot && !IsTestHot) + return; + + HotBlockOverlap.UnionCount += HotBlockCount; + if (IsBaseHot) + HotBlockOverlap.BaseCount += HotBlockCount; + if (IsTestHot) + HotBlockOverlap.TestCount += HotBlockCount; + if (IsBaseHot && IsTestHot) + HotBlockOverlap.OverlapCount += HotBlockCount; +} + +void SampleOverlapAggregator::getHotFunctions( + const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc, + uint64_t HotThreshold) const { + for (const auto &F : ProfStats) { + if (isFunctionHot(F.second, HotThreshold)) + HotFunc.emplace(F.first, F.second); + } +} + +void SampleOverlapAggregator::computeHotFuncOverlap() { + FuncSampleStatsMap BaseHotFunc; + getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold); + HotFuncOverlap.BaseCount = BaseHotFunc.size(); + + FuncSampleStatsMap TestHotFunc; + getHotFunctions(TestStats, TestHotFunc, TestHotThreshold); + HotFuncOverlap.TestCount = TestHotFunc.size(); + HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount; + + for (const auto &F : BaseHotFunc) { + if (TestHotFunc.count(F.first)) + ++HotFuncOverlap.OverlapCount; + else + ++HotFuncOverlap.UnionCount; + } +} + +void SampleOverlapAggregator::updateOverlapStatsForFunction( + uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount, + SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) { + assert(Status != MS_None && + "Match status should be updated before updating overlap statistics"); + if (Status == MS_FirstUnique) { + TestSample = 0; + FuncOverlap.BaseUniqueSample += BaseSample; + } else if (Status == MS_SecondUnique) { + BaseSample = 0; + FuncOverlap.TestUniqueSample += TestSample; + } else { + ++FuncOverlap.OverlapCount; + } + + FuncOverlap.UnionSample += std::max(BaseSample, TestSample); + FuncOverlap.OverlapSample += std::min(BaseSample, TestSample); + Difference += + 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap); + updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount); +} + +void SampleOverlapAggregator::updateForUnmatchedCallee( + const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status) { + assert((Status == MS_FirstUnique || Status == MS_SecondUnique) && + "Status must be either of the two unmatched cases"); + FuncSampleStats FuncStats; + if (Status == MS_FirstUnique) { + getFuncSampleStats(Func, FuncStats, BaseHotThreshold); + updateOverlapStatsForFunction(FuncStats.SampleSum, 0, + FuncStats.HotBlockCount, FuncOverlap, + Difference, Status); + } else { + getFuncSampleStats(Func, FuncStats, TestHotThreshold); + updateOverlapStatsForFunction(0, FuncStats.SampleSum, + FuncStats.HotBlockCount, FuncOverlap, + Difference, Status); + } +} + +double SampleOverlapAggregator::computeSampleFunctionInternalOverlap( + const sampleprof::FunctionSamples &BaseFunc, + const sampleprof::FunctionSamples &TestFunc, + SampleOverlapStats &FuncOverlap) { + + using namespace sampleprof; + + double Difference = 0; + + // Accumulate Difference for regular line/block samples in the function. + // We match them through sort-merge join algorithm because + // FunctionSamples::getBodySamples() returns a map of sample counters ordered + // by their offsets. + MatchStep BlockIterStep( + BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(), + TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend()); + BlockIterStep.updateOneStep(); + while (!BlockIterStep.areBothFinished()) { + uint64_t BaseSample = + BlockIterStep.isFirstFinished() + ? 0 + : BlockIterStep.getFirstIter()->second.getSamples(); + uint64_t TestSample = + BlockIterStep.isSecondFinished() + ? 0 + : BlockIterStep.getSecondIter()->second.getSamples(); + updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap, + Difference, BlockIterStep.getMatchStatus()); + + BlockIterStep.updateOneStep(); + } + + // Accumulate Difference for callsite lines in the function. We match + // them through sort-merge algorithm because + // FunctionSamples::getCallsiteSamples() returns a map of callsite records + // ordered by their offsets. + MatchStep CallsiteIterStep( + BaseFunc.getCallsiteSamples().cbegin(), + BaseFunc.getCallsiteSamples().cend(), + TestFunc.getCallsiteSamples().cbegin(), + TestFunc.getCallsiteSamples().cend()); + CallsiteIterStep.updateOneStep(); + while (!CallsiteIterStep.areBothFinished()) { + MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus(); + assert(CallsiteStepStatus != MS_None && + "Match status should be updated before entering loop body"); + + if (CallsiteStepStatus != MS_Match) { + auto Callsite = (CallsiteStepStatus == MS_FirstUnique) + ? CallsiteIterStep.getFirstIter() + : CallsiteIterStep.getSecondIter(); + for (const auto &F : Callsite->second) + updateForUnmatchedCallee(F.second, FuncOverlap, Difference, + CallsiteStepStatus); + } else { + // There may be multiple inlinees at the same offset, so we need to try + // matching all of them. This match is implemented through sort-merge + // algorithm because callsite records at the same offset are ordered by + // function names. + MatchStep CalleeIterStep( + CallsiteIterStep.getFirstIter()->second.cbegin(), + CallsiteIterStep.getFirstIter()->second.cend(), + CallsiteIterStep.getSecondIter()->second.cbegin(), + CallsiteIterStep.getSecondIter()->second.cend()); + CalleeIterStep.updateOneStep(); + while (!CalleeIterStep.areBothFinished()) { + MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus(); + if (CalleeStepStatus != MS_Match) { + auto Callee = (CalleeStepStatus == MS_FirstUnique) + ? CalleeIterStep.getFirstIter() + : CalleeIterStep.getSecondIter(); + updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference, + CalleeStepStatus); + } else { + // An inlined function can contain other inlinees inside, so compute + // the Difference recursively. + Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap( + CalleeIterStep.getFirstIter()->second, + CalleeIterStep.getSecondIter()->second, + FuncOverlap); + } + CalleeIterStep.updateOneStep(); + } + } + CallsiteIterStep.updateOneStep(); + } + + // Difference reflects the total differences of line/block samples in this + // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to + // reflect the similarity between function profiles in [0.0f to 1.0f]. + return (2.0 - Difference) / 2; +} + +double SampleOverlapAggregator::weightForFuncSimilarity( + double FuncInternalSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const { + // Compute the weight as the distance between the function weights in two + // profiles. + double BaseFrac = 0.0; + double TestFrac = 0.0; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample; + double WeightDistance = std::fabs(BaseFrac - TestFrac); + + // Take WeightDistance into the similarity. + return FuncInternalSimilarity * (1 - WeightDistance); +} + +double +SampleOverlapAggregator::weightByImportance(double FuncSimilarity, + uint64_t BaseFuncSample, + uint64_t TestFuncSample) const { + + double BaseFrac = 0.0; + double TestFrac = 0.0; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample / 2.0; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample / 2.0; + return FuncSimilarity * (BaseFrac + TestFrac); +} + +double SampleOverlapAggregator::computeSampleFunctionOverlap( + const sampleprof::FunctionSamples *BaseFunc, + const sampleprof::FunctionSamples *TestFunc, + SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample, + uint64_t TestFuncSample) { + // Default function internal similarity before weighted, meaning two functions + // has no overlap. + const double DefaultFuncInternalSimilarity = 0; + double FuncSimilarity; + double FuncInternalSimilarity; + + // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap. + // In this case, we use DefaultFuncInternalSimilarity as the function internal + // similarity. + if (!BaseFunc || !TestFunc) { + FuncInternalSimilarity = DefaultFuncInternalSimilarity; + } else { + assert(FuncOverlap != nullptr && + "FuncOverlap should be provided in this case"); + FuncInternalSimilarity = computeSampleFunctionInternalOverlap( + *BaseFunc, *TestFunc, *FuncOverlap); + // Now, FuncInternalSimilarity may be a little less than 0 due to + // imprecision of floating point accumulations. Make it zero if the + // difference is below Epsilon. + FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon) + ? 0 + : FuncInternalSimilarity; + } + FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity, + BaseFuncSample, TestFuncSample); + return FuncSimilarity; +} + +void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { + using namespace sampleprof; + + std::unordered_map + BaseFuncProf; + const auto &BaseProfiles = BaseReader->getProfiles(); + for (const auto &BaseFunc : BaseProfiles) { + BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second)); + } + ProfOverlap.UnionCount = BaseFuncProf.size(); + + const auto &TestProfiles = TestReader->getProfiles(); + for (const auto &TestFunc : TestProfiles) { + SampleOverlapStats FuncOverlap; + FuncOverlap.TestName = TestFunc.second.getContext(); + assert(TestStats.count(FuncOverlap.TestName) && + "TestStats should have records for all functions in test profile " + "except inlinees"); + FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum; + + bool Matched = false; + const auto Match = BaseFuncProf.find(FuncOverlap.TestName); + if (Match == BaseFuncProf.end()) { + const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName]; + ++ProfOverlap.TestUniqueCount; + ProfOverlap.TestUniqueSample += FuncStats.SampleSum; + FuncOverlap.TestUniqueSample = FuncStats.SampleSum; + + updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount); + + double FuncSimilarity = computeSampleFunctionOverlap( + nullptr, nullptr, nullptr, 0, FuncStats.SampleSum); + ProfOverlap.Similarity += + weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum); + + ++ProfOverlap.UnionCount; + ProfOverlap.UnionSample += FuncStats.SampleSum; + } else { + ++ProfOverlap.OverlapCount; + + // Two functions match with each other. Compute function-level overlap and + // aggregate them into profile-level overlap. + FuncOverlap.BaseName = Match->second->getContext(); + assert(BaseStats.count(FuncOverlap.BaseName) && + "BaseStats should have records for all functions in base profile " + "except inlinees"); + FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum; + + FuncOverlap.Similarity = computeSampleFunctionOverlap( + Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample, + FuncOverlap.TestSample); + ProfOverlap.Similarity += + weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample, + FuncOverlap.TestSample); + ProfOverlap.OverlapSample += FuncOverlap.OverlapSample; + ProfOverlap.UnionSample += FuncOverlap.UnionSample; + + // Accumulate the percentage of base unique and test unique samples into + // ProfOverlap. + ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample; + ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample; + + // Remove matched base functions for later reporting functions not found + // in test profile. + BaseFuncProf.erase(Match); + Matched = true; + } + + // Print function-level similarity information if specified by options. + assert(TestStats.count(FuncOverlap.TestName) && + "TestStats should have records for all functions in test profile " + "except inlinees"); + if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff || + (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) || + (Matched && !FuncFilter.NameFilter.empty() && + FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) != + std::string::npos)) { + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + FuncOverlap.BaseWeight = + static_cast(FuncOverlap.BaseSample) / ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + FuncOverlap.TestWeight = + static_cast(FuncOverlap.TestSample) / ProfOverlap.TestSample; + FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap); + } + } + + // Traverse through functions in base profile but not in test profile. + for (const auto &F : BaseFuncProf) { + assert(BaseStats.count(F.second->getContext()) && + "BaseStats should have records for all functions in base profile " + "except inlinees"); + const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()]; + ++ProfOverlap.BaseUniqueCount; + ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; + + updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount); + + double FuncSimilarity = computeSampleFunctionOverlap( + nullptr, nullptr, nullptr, FuncStats.SampleSum, 0); + ProfOverlap.Similarity += + weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0); + + ProfOverlap.UnionSample += FuncStats.SampleSum; + } + + // Now, ProfSimilarity may be a little greater than 1 due to imprecision + // of floating point accumulations. Make it 1.0 if the difference is below + // Epsilon. + ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon) + ? 1 + : ProfOverlap.Similarity; + + computeHotFuncOverlap(); +} + +void SampleOverlapAggregator::initializeSampleProfileOverlap() { + const auto &BaseProf = BaseReader->getProfiles(); + for (const auto &I : BaseProf) { + ++ProfOverlap.BaseCount; + FuncSampleStats FuncStats; + getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); + ProfOverlap.BaseSample += FuncStats.SampleSum; + BaseStats.emplace(I.second.getContext(), FuncStats); + } + + const auto &TestProf = TestReader->getProfiles(); + for (const auto &I : TestProf) { + ++ProfOverlap.TestCount; + FuncSampleStats FuncStats; + getFuncSampleStats(I.second, FuncStats, TestHotThreshold); + ProfOverlap.TestSample += FuncStats.SampleSum; + TestStats.emplace(I.second.getContext(), FuncStats); + } + + ProfOverlap.BaseName = StringRef(BaseFilename); + ProfOverlap.TestName = StringRef(TestFilename); +} + +void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const { + using namespace sampleprof; + + if (FuncSimilarityDump.empty()) + return; + + formatted_raw_ostream FOS(OS); + FOS << "Function-level details:\n"; + FOS << "Base weight"; + FOS.PadToColumn(TestWeightCol); + FOS << "Test weight"; + FOS.PadToColumn(SimilarityCol); + FOS << "Similarity"; + FOS.PadToColumn(OverlapCol); + FOS << "Overlap"; + FOS.PadToColumn(BaseUniqueCol); + FOS << "Base unique"; + FOS.PadToColumn(TestUniqueCol); + FOS << "Test unique"; + FOS.PadToColumn(BaseSampleCol); + FOS << "Base samples"; + FOS.PadToColumn(TestSampleCol); + FOS << "Test samples"; + FOS.PadToColumn(FuncNameCol); + FOS << "Function name\n"; + for (const auto &F : FuncSimilarityDump) { + double OverlapPercent = + F.second.UnionSample > 0 + ? static_cast(F.second.OverlapSample) / F.second.UnionSample + : 0; + double BaseUniquePercent = + F.second.BaseSample > 0 + ? static_cast(F.second.BaseUniqueSample) / + F.second.BaseSample + : 0; + double TestUniquePercent = + F.second.TestSample > 0 + ? static_cast(F.second.TestUniqueSample) / + F.second.TestSample + : 0; + + FOS << format("%.2f%%", F.second.BaseWeight * 100); + FOS.PadToColumn(TestWeightCol); + FOS << format("%.2f%%", F.second.TestWeight * 100); + FOS.PadToColumn(SimilarityCol); + FOS << format("%.2f%%", F.second.Similarity * 100); + FOS.PadToColumn(OverlapCol); + FOS << format("%.2f%%", OverlapPercent * 100); + FOS.PadToColumn(BaseUniqueCol); + FOS << format("%.2f%%", BaseUniquePercent * 100); + FOS.PadToColumn(TestUniqueCol); + FOS << format("%.2f%%", TestUniquePercent * 100); + FOS.PadToColumn(BaseSampleCol); + FOS << F.second.BaseSample; + FOS.PadToColumn(TestSampleCol); + FOS << F.second.TestSample; + FOS.PadToColumn(FuncNameCol); + FOS << F.second.TestName.toString() << "\n"; + } +} + +void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const { + OS << "Profile overlap infomation for base_profile: " + << ProfOverlap.BaseName.toString() + << " and test_profile: " << ProfOverlap.TestName.toString() + << "\nProgram level:\n"; + + OS << " Whole program profile similarity: " + << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n"; + + assert(ProfOverlap.UnionSample > 0 && + "Total samples in two profile should be greater than 0"); + double OverlapPercent = + static_cast(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + double BaseUniquePercent = static_cast(ProfOverlap.BaseUniqueSample) / + ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + double TestUniquePercent = static_cast(ProfOverlap.TestUniqueSample) / + ProfOverlap.TestSample; + + OS << " Whole program sample overlap: " + << format("%.3f%%", OverlapPercent * 100) << "\n"; + OS << " percentage of samples unique in base profile: " + << format("%.3f%%", BaseUniquePercent * 100) << "\n"; + OS << " percentage of samples unique in test profile: " + << format("%.3f%%", TestUniquePercent * 100) << "\n"; + OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n" + << " total samples in test profile: " << ProfOverlap.TestSample << "\n"; + + assert(ProfOverlap.UnionCount > 0 && + "There should be at least one function in two input profiles"); + double FuncOverlapPercent = + static_cast(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount; + OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100) + << "\n"; + OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n"; + OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount + << "\n"; + OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount + << "\n"; +} + +void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap( + raw_fd_ostream &OS) const { + assert(HotFuncOverlap.UnionCount > 0 && + "There should be at least one hot function in two input profiles"); + OS << " Hot-function overlap: " + << format("%.3f%%", static_cast(HotFuncOverlap.OverlapCount) / + HotFuncOverlap.UnionCount * 100) + << "\n"; + OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n"; + OS << " hot functions unique in base profile: " + << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n"; + OS << " hot functions unique in test profile: " + << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n"; + + assert(HotBlockOverlap.UnionCount > 0 && + "There should be at least one hot block in two input profiles"); + OS << " Hot-block overlap: " + << format("%.3f%%", static_cast(HotBlockOverlap.OverlapCount) / + HotBlockOverlap.UnionCount * 100) + << "\n"; + OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n"; + OS << " hot blocks unique in base profile: " + << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n"; + OS << " hot blocks unique in test profile: " + << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n"; +} + +std::error_code SampleOverlapAggregator::loadProfiles() { + using namespace sampleprof; + + LLVMContext Context; + auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = BaseReaderOrErr.getError()) + exitWithErrorCode(EC, BaseFilename); + + auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = TestReaderOrErr.getError()) + exitWithErrorCode(EC, TestFilename); + + BaseReader = std::move(BaseReaderOrErr.get()); + TestReader = std::move(TestReaderOrErr.get()); + + if (std::error_code EC = BaseReader->read()) + exitWithErrorCode(EC, BaseFilename); + if (std::error_code EC = TestReader->read()) + exitWithErrorCode(EC, TestFilename); + if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) + exitWithError( + "cannot compare probe-based profile with non-probe-based profile"); + if (BaseReader->profileIsCS() != TestReader->profileIsCS()) + exitWithError("cannot compare CS profile with non-CS profile"); + + // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in + // profile summary. + ProfileSummary &BasePS = BaseReader->getSummary(); + ProfileSummary &TestPS = TestReader->getSummary(); + BaseHotThreshold = + ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary()); + TestHotThreshold = + ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary()); + + return std::error_code(); +} + +void overlapSampleProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + uint64_t SimilarityCutoff, raw_fd_ostream &OS) { + using namespace sampleprof; + + // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics + // report 2--3 places after decimal point in percentage numbers. + SampleOverlapAggregator OverlapAggr( + BaseFilename, TestFilename, + static_cast(SimilarityCutoff) / 1000000, 0.000005, FuncFilter); + if (std::error_code EC = OverlapAggr.loadProfiles()) + exitWithErrorCode(EC); + + OverlapAggr.initializeSampleProfileOverlap(); + if (OverlapAggr.detectZeroSampleProfile(OS)) + return; + + OverlapAggr.computeSampleProfileOverlap(OS); + + OverlapAggr.dumpProgramSummary(OS); + OverlapAggr.dumpHotFuncAndBlockOverlap(OS); + OverlapAggr.dumpFuncSimilarity(OS); +} + +static int overlap_main(int argc, const char *argv[]) { + cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt Output("output", cl::value_desc("output"), cl::init("-"), + cl::desc("Output file")); + cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); + cl::opt IsCS( + "cs", cl::init(false), + cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); + cl::opt ValueCutoff( + "value-cutoff", cl::init(-1), + cl::desc( + "Function level overlap information for every function (with calling " + "context for csspgo) in test " + "profile with max count value greater then the parameter value")); + cl::opt FuncNameFilter( + "function", + cl::desc("Function level overlap information for matching functions. For " + "CSSPGO this takes a a function name with calling context")); + cl::opt SimilarityCutoff( + "similarity-cutoff", cl::init(0), + cl::desc("For sample profiles, list function names (with calling context " + "for csspgo) for overlapped functions " + "with similarities below the cutoff (percentage times 10000).")); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"))); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); + + std::error_code EC; + raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); + if (EC) + exitWithErrorCode(EC, Output); + + if (ProfileKind == instr) + overlapInstrProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, + IsCS); + else + overlapSampleProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, + SimilarityCutoff, OS); + + return 0; +} + +namespace { +struct ValueSitesStats { + ValueSitesStats() + : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), + TotalNumValues(0) {} + uint64_t TotalNumValueSites; + uint64_t TotalNumValueSitesWithValueProfile; + uint64_t TotalNumValues; + std::vector ValueSitesHistogram; +}; +} // namespace + +static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, + ValueSitesStats &Stats, raw_fd_ostream &OS, + InstrProfSymtab *Symtab) { + uint32_t NS = Func.getNumValueSites(VK); + Stats.TotalNumValueSites += NS; + for (size_t I = 0; I < NS; ++I) { + uint32_t NV = Func.getNumValueDataForSite(VK, I); + std::unique_ptr VD = Func.getValueForSite(VK, I); + Stats.TotalNumValues += NV; + if (NV) { + Stats.TotalNumValueSitesWithValueProfile++; + if (NV > Stats.ValueSitesHistogram.size()) + Stats.ValueSitesHistogram.resize(NV, 0); + Stats.ValueSitesHistogram[NV - 1]++; + } + + uint64_t SiteSum = 0; + for (uint32_t V = 0; V < NV; V++) + SiteSum += VD[V].Count; + if (SiteSum == 0) + SiteSum = 1; + + for (uint32_t V = 0; V < NV; V++) { + OS << "\t[ " << format("%2u", I) << ", "; + if (Symtab == nullptr) + OS << format("%4" PRIu64, VD[V].Value); + else + OS << Symtab->getFuncName(VD[V].Value); + OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" + << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; + } + } +} + +static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, + ValueSitesStats &Stats) { + OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; + OS << " Total number of sites with values: " + << Stats.TotalNumValueSitesWithValueProfile << "\n"; + OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; + + OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; + for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { + if (Stats.ValueSitesHistogram[I] > 0) + OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; + } +} + +static int showInstrProfile(const std::string &Filename, bool ShowCounts, + uint32_t TopN, bool ShowIndirectCallTargets, + bool ShowMemOPSizes, bool ShowDetailedSummary, + std::vector DetailedSummaryCutoffs, + bool ShowAllFunctions, bool ShowCS, + uint64_t ValueCutoff, bool OnlyListBelow, + const std::string &ShowFunction, bool TextFormat, + bool ShowBinaryIds, bool ShowCovered, + bool ShowProfileVersion, ShowFormat SFormat, + raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for instr profiles"); + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for instr profiles"); + auto ReaderOrErr = InstrProfReader::create(Filename); + std::vector Cutoffs = std::move(DetailedSummaryCutoffs); + if (ShowDetailedSummary && Cutoffs.empty()) { + Cutoffs = ProfileSummaryBuilder::DefaultCutoffs; + } + InstrProfSummaryBuilder Builder(std::move(Cutoffs)); + if (Error E = ReaderOrErr.takeError()) + exitWithError(std::move(E), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + bool IsIRInstr = Reader->isIRLevelProfile(); + size_t ShownFunctions = 0; + size_t BelowCutoffFunctions = 0; + int NumVPKind = IPVK_Last - IPVK_First + 1; + std::vector VPStats(NumVPKind); + + auto MinCmp = [](const std::pair &v1, + const std::pair &v2) { + return v1.second > v2.second; + }; + + std::priority_queue, + std::vector>, + decltype(MinCmp)> + HottestFuncs(MinCmp); + + if (!TextFormat && OnlyListBelow) { + OS << "The list of functions with the maximum counter less than " + << ValueCutoff << ":\n"; + } + + // Add marker so that IR-level instrumentation round-trips properly. + if (TextFormat && IsIRInstr) + OS << ":ir\n"; + + for (const auto &Func : *Reader) { + if (Reader->isIRLevelProfile()) { + bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); + if (FuncIsCS != ShowCS) + continue; + } + bool Show = ShowAllFunctions || + (!ShowFunction.empty() && Func.Name.contains(ShowFunction)); + + bool doTextFormatDump = (Show && TextFormat); + + if (doTextFormatDump) { + InstrProfSymtab &Symtab = Reader->getSymtab(); + InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, + OS); + continue; + } + + assert(Func.Counts.size() > 0 && "function missing entry counter"); + Builder.addRecord(Func); + + if (ShowCovered) { + if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; })) + OS << Func.Name << "\n"; + continue; + } + + uint64_t FuncMax = 0; + uint64_t FuncSum = 0; + + auto PseudoKind = Func.getCountPseudoKind(); + if (PseudoKind != InstrProfRecord::NotPseudo) { + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + ++ShownFunctions; + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size(); + if (PseudoKind == InstrProfRecord::PseudoHot) + OS << " \n"; + else if (PseudoKind == InstrProfRecord::PseudoWarm) + OS << " \n"; + else + llvm_unreachable("Unknown PseudoKind"); + } + continue; + } + + for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { + FuncMax = std::max(FuncMax, Func.Counts[I]); + FuncSum += Func.Counts[I]; + } + + if (FuncMax < ValueCutoff) { + ++BelowCutoffFunctions; + if (OnlyListBelow) { + OS << " " << Func.Name << ": (Max = " << FuncMax + << " Sum = " << FuncSum << ")\n"; + } + continue; + } else if (OnlyListBelow) + continue; + + if (TopN) { + if (HottestFuncs.size() == TopN) { + if (HottestFuncs.top().second < FuncMax) { + HottestFuncs.pop(); + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + } else + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + + ++ShownFunctions; + + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size() << "\n"; + if (!IsIRInstr) + OS << " Function count: " << Func.Counts[0] << "\n"; + + if (ShowIndirectCallTargets) + OS << " Indirect Call Site Count: " + << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; + + uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); + if (ShowMemOPSizes && NumMemOPCalls > 0) + OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls + << "\n"; + + if (ShowCounts) { + OS << " Block counts: ["; + size_t Start = (IsIRInstr ? 0 : 1); + for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { + OS << (I == Start ? "" : ", ") << Func.Counts[I]; + } + OS << "]\n"; + } + + if (ShowIndirectCallTargets) { + OS << " Indirect Target Results:\n"; + traverseAllValueSites(Func, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget], OS, + &(Reader->getSymtab())); + } + + if (ShowMemOPSizes && NumMemOPCalls > 0) { + OS << " Memory Intrinsic Size Results:\n"; + traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, + nullptr); + } + } + } + if (Reader->hasError()) + exitWithError(Reader->getError(), Filename); + + if (TextFormat || ShowCovered) + return 0; + std::unique_ptr PS(Builder.getSummary()); + bool IsIR = Reader->isIRLevelProfile(); + OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end"); + if (IsIR) + OS << " entry_first = " << Reader->instrEntryBBEnabled(); + OS << "\n"; + if (ShowAllFunctions || !ShowFunction.empty()) + OS << "Functions shown: " << ShownFunctions << "\n"; + OS << "Total functions: " << PS->getNumFunctions() << "\n"; + if (ValueCutoff > 0) { + OS << "Number of functions with maximum count (< " << ValueCutoff + << "): " << BelowCutoffFunctions << "\n"; + OS << "Number of functions with maximum count (>= " << ValueCutoff + << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; + } + OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; + OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; + + if (TopN) { + std::vector> SortedHottestFuncs; + while (!HottestFuncs.empty()) { + SortedHottestFuncs.emplace_back(HottestFuncs.top()); + HottestFuncs.pop(); + } + OS << "Top " << TopN + << " functions with the largest internal block counts: \n"; + for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) + OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; + } + + if (ShownFunctions && ShowIndirectCallTargets) { + OS << "Statistics for indirect call sites profile:\n"; + showValueSitesStats(OS, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget]); + } + + if (ShownFunctions && ShowMemOPSizes) { + OS << "Statistics for memory intrinsic calls sizes profile:\n"; + showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); + } + + if (ShowDetailedSummary) { + OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; + OS << "Total count: " << PS->getTotalCount() << "\n"; + PS->printDetailedSummary(OS); + } + + if (ShowBinaryIds) + if (Error E = Reader->printBinaryIds(OS)) + exitWithError(std::move(E), Filename); + + if (ShowProfileVersion) + OS << "Profile version: " << Reader->getVersion() << "\n"; + return 0; +} + +static void showSectionInfo(sampleprof::SampleProfileReader *Reader, + raw_fd_ostream &OS) { + if (!Reader->dumpSectionInfo(OS)) { + WithColor::warning() << "-show-sec-info-only is only supported for " + << "sample profile in extbinary format and is " + << "ignored for other formats.\n"; + return; + } +} + +namespace { +struct HotFuncInfo { + std::string FuncName; + uint64_t TotalCount; + double TotalCountPercent; + uint64_t MaxCount; + uint64_t EntryCount; + + HotFuncInfo() + : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {} + + HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) + : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP), + MaxCount(MS), EntryCount(ES) {} +}; +} // namespace + +// Print out detailed information about hot functions in PrintValues vector. +// Users specify titles and offset of every columns through ColumnTitle and +// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same +// and at least 4. Besides, users can optionally give a HotFuncMetric string to +// print out or let it be an empty string. +static void dumpHotFunctionList(const std::vector &ColumnTitle, + const std::vector &ColumnOffset, + const std::vector &PrintValues, + uint64_t HotFuncCount, uint64_t TotalFuncCount, + uint64_t HotProfCount, uint64_t TotalProfCount, + const std::string &HotFuncMetric, + uint32_t TopNFunctions, raw_fd_ostream &OS) { + assert(ColumnOffset.size() == ColumnTitle.size() && + "ColumnOffset and ColumnTitle should have the same size"); + assert(ColumnTitle.size() >= 4 && + "ColumnTitle should have at least 4 elements"); + assert(TotalFuncCount > 0 && + "There should be at least one function in the profile"); + double TotalProfPercent = 0; + if (TotalProfCount > 0) + TotalProfPercent = static_cast(HotProfCount) / TotalProfCount * 100; + + formatted_raw_ostream FOS(OS); + FOS << HotFuncCount << " out of " << TotalFuncCount + << " functions with profile (" + << format("%.2f%%", + (static_cast(HotFuncCount) / TotalFuncCount * 100)) + << ") are considered hot functions"; + if (!HotFuncMetric.empty()) + FOS << " (" << HotFuncMetric << ")"; + FOS << ".\n"; + FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" + << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; + + for (size_t I = 0; I < ColumnTitle.size(); ++I) { + FOS.PadToColumn(ColumnOffset[I]); + FOS << ColumnTitle[I]; + } + FOS << "\n"; + + uint32_t Count = 0; + for (const auto &R : PrintValues) { + if (TopNFunctions && (Count++ == TopNFunctions)) + break; + FOS.PadToColumn(ColumnOffset[0]); + FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; + FOS.PadToColumn(ColumnOffset[1]); + FOS << R.MaxCount; + FOS.PadToColumn(ColumnOffset[2]); + FOS << R.EntryCount; + FOS.PadToColumn(ColumnOffset[3]); + FOS << R.FuncName << "\n"; + } +} + +static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, + ProfileSummary &PS, uint32_t TopN, + raw_fd_ostream &OS) { + using namespace sampleprof; + + const uint32_t HotFuncCutoff = 990000; + auto &SummaryVector = PS.getDetailedSummary(); + uint64_t MinCountThreshold = 0; + for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { + if (SummaryEntry.Cutoff == HotFuncCutoff) { + MinCountThreshold = SummaryEntry.MinCount; + break; + } + } + + // Traverse all functions in the profile and keep only hot functions. + // The following loop also calculates the sum of total samples of all + // functions. + std::multimap, + std::greater> + HotFunc; + uint64_t ProfileTotalSample = 0; + uint64_t HotFuncSample = 0; + uint64_t HotFuncCount = 0; + + for (const auto &I : Profiles) { + FuncSampleStats FuncStats; + const FunctionSamples &FuncProf = I.second; + ProfileTotalSample += FuncProf.getTotalSamples(); + getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold); + + if (isFunctionHot(FuncStats, MinCountThreshold)) { + HotFunc.emplace(FuncProf.getTotalSamples(), + std::make_pair(&(I.second), FuncStats.MaxSample)); + HotFuncSample += FuncProf.getTotalSamples(); + ++HotFuncCount; + } + } + + std::vector ColumnTitle{"Total sample (%)", "Max sample", + "Entry sample", "Function name"}; + std::vector ColumnOffset{0, 24, 42, 58}; + std::string Metric = + std::string("max sample >= ") + std::to_string(MinCountThreshold); + std::vector PrintValues; + for (const auto &FuncPair : HotFunc) { + const FunctionSamples &Func = *FuncPair.second.first; + double TotalSamplePercent = + (ProfileTotalSample > 0) + ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample + : 0; + PrintValues.emplace_back( + HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(), + TotalSamplePercent, FuncPair.second.second, + Func.getHeadSamplesEstimate())); + } + dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, + Profiles.size(), HotFuncSample, ProfileTotalSample, + Metric, TopN, OS); + + return 0; +} + +static int showSampleProfile(const std::string &Filename, bool ShowCounts, + uint32_t TopN, bool ShowAllFunctions, + bool ShowDetailedSummary, + const std::string &ShowFunction, + bool ShowProfileSymbolList, + bool ShowSectionInfoOnly, bool ShowHotFuncList, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for sample profiles"); + using namespace sampleprof; + LLVMContext Context; + auto ReaderOrErr = + SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithErrorCode(EC, Filename); + + auto Reader = std::move(ReaderOrErr.get()); + if (ShowSectionInfoOnly) { + showSectionInfo(Reader.get(), OS); + return 0; + } + + if (std::error_code EC = Reader->read()) + exitWithErrorCode(EC, Filename); + + if (ShowAllFunctions || ShowFunction.empty()) { + if (SFormat == ShowFormat::Json) + Reader->dumpJson(OS); + else + Reader->dump(OS); + } else { + if (SFormat == ShowFormat::Json) + exitWithError( + "the JSON format is supported only when all functions are to " + "be printed"); + + // TODO: parse context string to support filtering by contexts. + Reader->dumpFunctionProfile(StringRef(ShowFunction), OS); + } + + if (ShowProfileSymbolList) { + std::unique_ptr ReaderList = + Reader->getProfileSymbolList(); + ReaderList->dump(OS); + } + + if (ShowDetailedSummary) { + auto &PS = Reader->getSummary(); + PS.printSummary(OS); + PS.printDetailedSummary(OS); + } + + if (ShowHotFuncList || TopN) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); + + return 0; +} + +static int showMemProfProfile(const std::string &Filename, + const std::string &ProfiledBinary, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for MemProf"); + auto ReaderOr = llvm::memprof::RawMemProfReader::create( + Filename, ProfiledBinary, /*KeepNames=*/true); + if (Error E = ReaderOr.takeError()) + // Since the error can be related to the profile or the binary we do not + // pass whence. Instead additional context is provided where necessary in + // the error message. + exitWithError(std::move(E), /*Whence*/ ""); + + std::unique_ptr Reader( + ReaderOr.get().release()); + + Reader->printYAML(OS); + return 0; +} + +static int showDebugInfoCorrelation(const std::string &Filename, + bool ShowDetailedSummary, + bool ShowProfileSymbolList, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for debug info correlation"); + std::unique_ptr Correlator; + if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator)) + exitWithError(std::move(Err), Filename); + if (SFormat == ShowFormat::Yaml) { + if (auto Err = Correlator->dumpYaml(OS)) + exitWithError(std::move(Err), Filename); + return 0; + } + + if (auto Err = Correlator->correlateProfileData()) + exitWithError(std::move(Err), Filename); + + InstrProfSymtab Symtab; + if (auto Err = Symtab.create( + StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize()))) + exitWithError(std::move(Err), Filename); + + if (ShowProfileSymbolList) + Symtab.dumpNames(OS); + // TODO: Read "Profile Data Type" from debug info to compute and show how many + // counters the section holds. + if (ShowDetailedSummary) + OS << "Counters section size: 0x" + << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n"; + OS << "Found " << Correlator->getDataSize() << " functions\n"; + + return 0; +} + +static int show_main(int argc, const char *argv[]) { + cl::opt Filename(cl::Positional, cl::desc("")); + + cl::opt ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions")); + cl::opt SFormat( + "show-format", cl::init(ShowFormat::Text), + cl::desc("Emit output in the selected format if supported"), + cl::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); + // TODO: Consider replacing this with `--show-format=text-encoding`. + cl::opt TextFormat( + "text", cl::init(false), + cl::desc("Show instr profile data in text dump format")); + cl::opt JsonFormat( + "json", cl::desc("Show sample profile data in the JSON format " + "(deprecated, please use --show-format=json)")); + cl::opt ShowIndirectCallTargets( + "ic-targets", cl::init(false), + cl::desc("Show indirect call site target values for shown functions")); + cl::opt ShowMemOPSizes( + "memop-sizes", cl::init(false), + cl::desc("Show the profiled sizes of the memory intrinsic calls " + "for shown functions")); + cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), + cl::desc("Show detailed profile summary")); + cl::list DetailedSummaryCutoffs( + cl::CommaSeparated, "detailed-summary-cutoffs", + cl::desc( + "Cutoff percentages (times 10000) for generating detailed summary"), + cl::value_desc("800000,901000,999999")); + cl::opt ShowHotFuncList( + "hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions")); + cl::opt ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for every function")); + cl::opt ShowCS("showcs", cl::init(false), + cl::desc("Show context sensitive counts")); + cl::opt ShowFunction("function", + cl::desc("Details for matching functions")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), + clEnumVal(memory, "MemProf memory access profile"))); + cl::opt TopNFunctions( + "topn", cl::init(0), + cl::desc("Show the list of functions with the largest internal counts")); + cl::opt ValueCutoff( + "value-cutoff", cl::init(0), + cl::desc("Set the count value cutoff. Functions with the maximum count " + "less than this value will not be printed out. (Default is 0)")); + cl::opt OnlyListBelow( + "list-below-cutoff", cl::init(false), + cl::desc("Only output names of functions whose max count values are " + "below the cutoff value")); + cl::opt ShowProfileSymbolList( + "show-prof-sym-list", cl::init(false), + cl::desc("Show profile symbol list if it exists in the profile. ")); + cl::opt ShowSectionInfoOnly( + "show-sec-info-only", cl::init(false), + cl::desc("Show the information of each section in the sample profile. " + "The flag is only usable when the sample profile is in " + "extbinary format")); + cl::opt ShowBinaryIds("binary-ids", cl::init(false), + cl::desc("Show binary ids in the profile. ")); + cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Read and extract profile metadata from debug info and show " + "the functions it found.")); + cl::opt ShowCovered( + "covered", cl::init(false), + cl::desc("Show only the functions that have been executed.")); + cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected.")); + cl::opt ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. ")); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); + + if (Filename.empty() && DebugInfoFilename.empty()) + exitWithError( + "the positional argument '' is required unless '--" + + DebugInfoFilename.ArgStr + "' is provided"); + + if (Filename == OutputFilename) { + errs() << sys::path::filename(argv[0]) + << ": Input file name cannot be the same as the output file name!\n"; + return 1; + } + if (JsonFormat) + SFormat = ShowFormat::Json; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (ShowAllFunctions && !ShowFunction.empty()) + WithColor::warning() << "-function argument ignored: showing all functions\n"; + + if (!DebugInfoFilename.empty()) + return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, + ShowProfileSymbolList, SFormat, OS); + + if (ProfileKind == instr) + return showInstrProfile( + Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, + ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, + ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, + TextFormat, ShowBinaryIds, ShowCovered, ShowProfileVersion, SFormat, + OS); + if (ProfileKind == sample) + return showSampleProfile(Filename, ShowCounts, TopNFunctions, + ShowAllFunctions, ShowDetailedSummary, + ShowFunction, ShowProfileSymbolList, + ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); + return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + StringRef ProgName(sys::path::filename(argv[0])); + if (argc > 1) { + int (*func)(int, const char *[]) = nullptr; + + if (strcmp(argv[1], "merge") == 0) + func = merge_main; + else if (strcmp(argv[1], "show") == 0) + func = show_main; + else if (strcmp(argv[1], "overlap") == 0) + func = overlap_main; + + if (func) { + std::string Invocation(ProgName.str() + " " + argv[1]); + argv[1] = Invocation.c_str(); + return func(argc - 1, argv + 1); + } + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || + strcmp(argv[1], "--help") == 0) { + + errs() << "OVERVIEW: LLVM profile data tools\n\n" + << "USAGE: " << ProgName << " [args...]\n" + << "USAGE: " << ProgName << " -help\n\n" + << "See each individual command --help for more details.\n" + << "Available commands: merge, show, overlap\n"; + return 0; + } + } + + if (argc < 2) + errs() << ProgName << ": No command specified!\n"; + else + errs() << ProgName << ": Unknown command!\n"; + + errs() << "USAGE: " << ProgName << " [args...]\n"; + return 1; +} From 15013d13f3221b197fa473f48975f6ebf8275d4a Mon Sep 17 00:00:00 2001 From: Ikey Doherty Date: Sat, 3 Jun 2023 22:11:32 +0100 Subject: [PATCH 171/301] utils: Add FileCheck-16 from LLVM release/16.x Signed-off-by: Ikey Doherty --- utils/FileCheck-16.cpp | 895 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 895 insertions(+) create mode 100644 utils/FileCheck-16.cpp diff --git a/utils/FileCheck-16.cpp b/utils/FileCheck-16.cpp new file mode 100644 index 00000000000..6657a1aff39 --- /dev/null +++ b/utils/FileCheck-16.cpp @@ -0,0 +1,895 @@ +//===- FileCheck.cpp - Check that File's Contents match what is expected --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// FileCheck does a line-by line check of a file that validates whether it +// contains the expected content. This is useful for regression tests etc. +// +// This program exits with an exit status of 2 on error, exit status of 0 if +// the file matched the expected contents, and exit status of 1 if it did not +// contain the expected contents. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FileCheck/FileCheck.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +using namespace llvm; + +static cl::extrahelp FileCheckOptsEnv( + "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n" + "from the command line.\n"); + +static cl::opt + CheckFilename(cl::Positional, cl::desc(""), cl::Optional); + +static cl::opt + InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), + cl::init("-"), cl::value_desc("filename")); + +static cl::list CheckPrefixes( + "check-prefix", + cl::desc("Prefix to use from check file (defaults to 'CHECK')")); +static cl::alias CheckPrefixesAlias( + "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, + cl::NotHidden, + cl::desc( + "Alias for -check-prefix permitting multiple comma separated values")); + +static cl::list CommentPrefixes( + "comment-prefixes", cl::CommaSeparated, cl::Hidden, + cl::desc("Comma-separated list of comment prefixes to use from check file\n" + "(defaults to 'COM,RUN'). Please avoid using this feature in\n" + "LLVM's LIT-based test suites, which should be easier to\n" + "maintain if they all follow a consistent comment style. This\n" + "feature is meant for non-LIT test suites using FileCheck.")); + +static cl::opt NoCanonicalizeWhiteSpace( + "strict-whitespace", + cl::desc("Do not treat all horizontal whitespace as equivalent")); + +static cl::opt IgnoreCase( + "ignore-case", + cl::desc("Use case-insensitive matching")); + +static cl::list ImplicitCheckNot( + "implicit-check-not", + cl::desc("Add an implicit negative check with this pattern to every\n" + "positive check. This can be used to ensure that no instances of\n" + "this pattern occur which are not matched by a positive pattern"), + cl::value_desc("pattern")); + +static cl::list + GlobalDefines("D", cl::AlwaysPrefix, + cl::desc("Define a variable to be used in capture patterns."), + cl::value_desc("VAR=VALUE")); + +static cl::opt AllowEmptyInput( + "allow-empty", cl::init(false), + cl::desc("Allow the input file to be empty. This is useful when making\n" + "checks that some error message does not occur, for example.")); + +static cl::opt AllowUnusedPrefixes( + "allow-unused-prefixes", + cl::desc("Allow prefixes to be specified but not appear in the test.")); + +static cl::opt MatchFullLines( + "match-full-lines", cl::init(false), + cl::desc("Require all positive matches to cover an entire input line.\n" + "Allows leading and trailing whitespace if --strict-whitespace\n" + "is not also passed.")); + +static cl::opt EnableVarScope( + "enable-var-scope", cl::init(false), + cl::desc("Enables scope for regex variables. Variables with names that\n" + "do not start with '$' will be reset at the beginning of\n" + "each CHECK-LABEL block.")); + +static cl::opt AllowDeprecatedDagOverlap( + "allow-deprecated-dag-overlap", cl::init(false), + cl::desc("Enable overlapping among matches in a group of consecutive\n" + "CHECK-DAG directives. This option is deprecated and is only\n" + "provided for convenience as old tests are migrated to the new\n" + "non-overlapping CHECK-DAG implementation.\n")); + +static cl::opt Verbose( + "v", + cl::desc("Print directive pattern matches, or add them to the input dump\n" + "if enabled.\n")); + +static cl::opt VerboseVerbose( + "vv", + cl::desc("Print information helpful in diagnosing internal FileCheck\n" + "issues, or add it to the input dump if enabled. Implies\n" + "-v.\n")); + +// The order of DumpInputValue members affects their precedence, as documented +// for -dump-input below. +enum DumpInputValue { + DumpInputNever, + DumpInputFail, + DumpInputAlways, + DumpInputHelp +}; + +static cl::list DumpInputs( + "dump-input", + cl::desc("Dump input to stderr, adding annotations representing\n" + "currently enabled diagnostics. When there are multiple\n" + "occurrences of this option, the that appears earliest\n" + "in the list below has precedence. The default is 'fail'.\n"), + cl::value_desc("mode"), + cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"), + clEnumValN(DumpInputAlways, "always", "Always dump input"), + clEnumValN(DumpInputFail, "fail", "Dump input on failure"), + clEnumValN(DumpInputNever, "never", "Never dump input"))); + +// The order of DumpInputFilterValue members affects their precedence, as +// documented for -dump-input-filter below. +enum DumpInputFilterValue { + DumpInputFilterError, + DumpInputFilterAnnotation, + DumpInputFilterAnnotationFull, + DumpInputFilterAll +}; + +static cl::list DumpInputFilters( + "dump-input-filter", + cl::desc("In the dump requested by -dump-input, print only input lines of\n" + "kind plus any context specified by -dump-input-context.\n" + "When there are multiple occurrences of this option, the \n" + "that appears earliest in the list below has precedence. The\n" + "default is 'error' when -dump-input=fail, and it's 'all' when\n" + "-dump-input=always.\n"), + cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"), + clEnumValN(DumpInputFilterAnnotationFull, "annotation-full", + "Input lines with annotations"), + clEnumValN(DumpInputFilterAnnotation, "annotation", + "Input lines with starting points of annotations"), + clEnumValN(DumpInputFilterError, "error", + "Input lines with starting points of error " + "annotations"))); + +static cl::list DumpInputContexts( + "dump-input-context", cl::value_desc("N"), + cl::desc("In the dump requested by -dump-input, print input lines\n" + "before and input lines after any lines specified by\n" + "-dump-input-filter. When there are multiple occurrences of\n" + "this option, the largest specified has precedence. The\n" + "default is 5.\n")); + +typedef cl::list::const_iterator prefix_iterator; + + + + + + + +static void DumpCommandLine(int argc, char **argv) { + errs() << "FileCheck command line: "; + for (int I = 0; I < argc; I++) + errs() << " " << argv[I]; + errs() << "\n"; +} + +struct MarkerStyle { + /// The starting char (before tildes) for marking the line. + char Lead; + /// What color to use for this annotation. + raw_ostream::Colors Color; + /// A note to follow the marker, or empty string if none. + std::string Note; + /// Does this marker indicate inclusion by -dump-input-filter=error? + bool FiltersAsError; + MarkerStyle() {} + MarkerStyle(char Lead, raw_ostream::Colors Color, + const std::string &Note = "", bool FiltersAsError = false) + : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) { + assert((!FiltersAsError || !Note.empty()) && + "expected error diagnostic to have note"); + } +}; + +static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { + switch (MatchTy) { + case FileCheckDiag::MatchFoundAndExpected: + return MarkerStyle('^', raw_ostream::GREEN); + case FileCheckDiag::MatchFoundButExcluded: + return MarkerStyle('!', raw_ostream::RED, "error: no match expected", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchFoundButWrongLine: + return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchFoundButDiscarded: + return MarkerStyle('!', raw_ostream::CYAN, + "discard: overlaps earlier match"); + case FileCheckDiag::MatchFoundErrorNote: + // Note should always be overridden within the FileCheckDiag. + return MarkerStyle('!', raw_ostream::RED, + "error: unknown error after match", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchNoneAndExcluded: + return MarkerStyle('X', raw_ostream::GREEN); + case FileCheckDiag::MatchNoneButExpected: + return MarkerStyle('X', raw_ostream::RED, "error: no match found", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchNoneForInvalidPattern: + return MarkerStyle('X', raw_ostream::RED, + "error: match failed for invalid pattern", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchFuzzy: + return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match", + /*FiltersAsError=*/true); + } + llvm_unreachable_internal("unexpected match type"); +} + +static void DumpInputAnnotationHelp(raw_ostream &OS) { + OS << "The following description was requested by -dump-input=help to\n" + << "explain the input dump printed by FileCheck.\n" + << "\n" + << "Related command-line options:\n" + << "\n" + << " - -dump-input= enables or disables the input dump\n" + << " - -dump-input-filter= filters the input lines\n" + << " - -dump-input-context= adjusts the context of filtered lines\n" + << " - -v and -vv add more annotations\n" + << " - -color forces colors to be enabled both in the dump and below\n" + << " - -help documents the above options in more detail\n" + << "\n" + << "These options can also be set via FILECHECK_OPTS. For example, for\n" + << "maximum debugging output on failures:\n" + << "\n" + << " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n" + << "\n" + << "Input dump annotation format:\n" + << "\n"; + + // Labels for input lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; + OS << " labels line number L of the input file\n" + << " An extra space is added after each input line to represent" + << " the\n" + << " newline character\n"; + + // Labels for annotation lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; + OS << " labels the only match result for either (1) a pattern of type T" + << " from\n" + << " line L of the check file if L is an integer or (2) the" + << " I-th implicit\n" + << " pattern if L is \"imp\" followed by an integer " + << "I (index origin one)\n"; + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; + OS << " labels the Nth match result for such a pattern\n"; + + // Markers on annotation lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; + OS << " marks good match (reported if -v)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; + OS << " marks bad match, such as:\n" + << " - CHECK-NEXT on same line as previous match (error)\n" + << " - CHECK-NOT found (error)\n" + << " - CHECK-DAG overlapping match (discarded, reported if " + << "-vv)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; + OS << " marks search range when no match is found, such as:\n" + << " - CHECK-NEXT not found (error)\n" + << " - CHECK-NOT not found (success, reported if -vv)\n" + << " - CHECK-DAG not found after discarded matches (error)\n" + << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; + OS << " marks fuzzy match when no match is found\n"; + + // Elided lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "..."; + OS << " indicates elided input lines and annotations, as specified by\n" + << " -dump-input-filter and -dump-input-context\n"; + + // Colors. + OS << " - colors "; + WithColor(OS, raw_ostream::GREEN, true) << "success"; + OS << ", "; + WithColor(OS, raw_ostream::RED, true) << "error"; + OS << ", "; + WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; + OS << ", "; + WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; + OS << ", "; + WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; + OS << "\n"; +} + +/// An annotation for a single input line. +struct InputAnnotation { + /// The index of the match result across all checks + unsigned DiagIndex; + /// The label for this annotation. + std::string Label; + /// Is this the initial fragment of a diagnostic that has been broken across + /// multiple lines? + bool IsFirstLine; + /// What input line (one-origin indexing) this annotation marks. This might + /// be different from the starting line of the original diagnostic if + /// !IsFirstLine. + unsigned InputLine; + /// The column range (one-origin indexing, open end) in which to mark the + /// input line. If InputEndCol is UINT_MAX, treat it as the last column + /// before the newline. + unsigned InputStartCol, InputEndCol; + /// The marker to use. + MarkerStyle Marker; + /// Whether this annotation represents a good match for an expected pattern. + bool FoundAndExpectedMatch; +}; + +/// Get an abbreviation for the check type. +static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { + switch (Ty) { + case Check::CheckPlain: + if (Ty.getCount() > 1) + return "count"; + return "check"; + case Check::CheckNext: + return "next"; + case Check::CheckSame: + return "same"; + case Check::CheckNot: + return "not"; + case Check::CheckDAG: + return "dag"; + case Check::CheckLabel: + return "label"; + case Check::CheckEmpty: + return "empty"; + case Check::CheckComment: + return "com"; + case Check::CheckEOF: + return "eof"; + case Check::CheckBadNot: + return "bad-not"; + case Check::CheckBadCount: + return "bad-count"; + case Check::CheckMisspelled: + return "misspelled"; + case Check::CheckNone: + llvm_unreachable("invalid FileCheckType"); + } + llvm_unreachable("unknown FileCheckType"); +} + +static void +BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID, + const std::pair &ImpPatBufferIDRange, + const std::vector &Diags, + std::vector &Annotations, + unsigned &LabelWidth) { + struct CompareSMLoc { + bool operator()(const SMLoc &LHS, const SMLoc &RHS) const { + return LHS.getPointer() < RHS.getPointer(); + } + }; + // How many diagnostics does each pattern have? + std::map DiagCountPerPattern; + for (auto Diag : Diags) + ++DiagCountPerPattern[Diag.CheckLoc]; + // How many diagnostics have we seen so far per pattern? + std::map DiagIndexPerPattern; + // How many total diagnostics have we seen so far? + unsigned DiagIndex = 0; + // What's the widest label? + LabelWidth = 0; + for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; + ++DiagItr) { + InputAnnotation A; + A.DiagIndex = DiagIndex++; + + // Build label, which uniquely identifies this check result. + unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc); + auto CheckLineAndCol = + SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID); + llvm::raw_string_ostream Label(A.Label); + Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"; + if (CheckBufferID == CheckFileBufferID) + Label << CheckLineAndCol.first; + else if (ImpPatBufferIDRange.first <= CheckBufferID && + CheckBufferID < ImpPatBufferIDRange.second) + Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1); + else + llvm_unreachable("expected diagnostic's check location to be either in " + "the check file or for an implicit pattern"); + if (DiagCountPerPattern[DiagItr->CheckLoc] > 1) + Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++; + LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); + + A.Marker = GetMarker(DiagItr->MatchTy); + if (!DiagItr->Note.empty()) { + A.Marker.Note = DiagItr->Note; + // It's less confusing if notes that don't actually have ranges don't have + // markers. For example, a marker for 'with "VAR" equal to "5"' would + // seem to indicate where "VAR" matches, but the location we actually have + // for the marker simply points to the start of the match/search range for + // the full pattern of which the substitution is potentially just one + // component. + if (DiagItr->InputStartLine == DiagItr->InputEndLine && + DiagItr->InputStartCol == DiagItr->InputEndCol) + A.Marker.Lead = ' '; + } + if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) { + assert(!DiagItr->Note.empty() && + "expected custom note for MatchFoundErrorNote"); + A.Marker.Note = "error: " + A.Marker.Note; + } + A.FoundAndExpectedMatch = + DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; + + // Compute the mark location, and break annotation into multiple + // annotations if it spans multiple lines. + A.IsFirstLine = true; + A.InputLine = DiagItr->InputStartLine; + A.InputStartCol = DiagItr->InputStartCol; + if (DiagItr->InputStartLine == DiagItr->InputEndLine) { + // Sometimes ranges are empty in order to indicate a specific point, but + // that would mean nothing would be marked, so adjust the range to + // include the following character. + A.InputEndCol = + std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); + Annotations.push_back(A); + } else { + assert(DiagItr->InputStartLine < DiagItr->InputEndLine && + "expected input range not to be inverted"); + A.InputEndCol = UINT_MAX; + Annotations.push_back(A); + for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; + L <= E; ++L) { + // If a range ends before the first column on a line, then it has no + // characters on that line, so there's nothing to render. + if (DiagItr->InputEndCol == 1 && L == E) + break; + InputAnnotation B; + B.DiagIndex = A.DiagIndex; + B.Label = A.Label; + B.IsFirstLine = false; + B.InputLine = L; + B.Marker = A.Marker; + B.Marker.Lead = '~'; + B.Marker.Note = ""; + B.InputStartCol = 1; + if (L != E) + B.InputEndCol = UINT_MAX; + else + B.InputEndCol = DiagItr->InputEndCol; + B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; + Annotations.push_back(B); + } + } + } +} + +static unsigned FindInputLineInFilter( + DumpInputFilterValue DumpInputFilter, unsigned CurInputLine, + const std::vector::iterator &AnnotationBeg, + const std::vector::iterator &AnnotationEnd) { + if (DumpInputFilter == DumpInputFilterAll) + return CurInputLine; + for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd; + ++AnnotationItr) { + switch (DumpInputFilter) { + case DumpInputFilterAll: + llvm_unreachable("unexpected DumpInputFilterAll"); + break; + case DumpInputFilterAnnotationFull: + return AnnotationItr->InputLine; + case DumpInputFilterAnnotation: + if (AnnotationItr->IsFirstLine) + return AnnotationItr->InputLine; + break; + case DumpInputFilterError: + if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError) + return AnnotationItr->InputLine; + break; + } + } + return UINT_MAX; +} + +/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would +/// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either +/// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing. +static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines, + unsigned LabelWidth) { + if (ElidedLines.empty()) + return; + unsigned EllipsisLines = 3; + if (EllipsisLines < StringRef(ElidedLines).count('\n')) { + for (unsigned i = 0; i < EllipsisLines; ++i) { + WithColor(OS, raw_ostream::BLACK, /*Bold=*/true) + << right_justify(".", LabelWidth); + OS << '\n'; + } + } else + OS << ElidedLines; + ElidedLines.clear(); +} + +static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, + DumpInputFilterValue DumpInputFilter, + unsigned DumpInputContext, + StringRef InputFileText, + std::vector &Annotations, + unsigned LabelWidth) { + OS << "Input was:\n<<<<<<\n"; + + // Sort annotations. + llvm::sort(Annotations, + [](const InputAnnotation &A, const InputAnnotation &B) { + // 1. Sort annotations in the order of the input lines. + // + // This makes it easier to find relevant annotations while + // iterating input lines in the implementation below. FileCheck + // does not always produce diagnostics in the order of input + // lines due to, for example, CHECK-DAG and CHECK-NOT. + if (A.InputLine != B.InputLine) + return A.InputLine < B.InputLine; + // 2. Sort annotations in the temporal order FileCheck produced + // their associated diagnostics. + // + // This sort offers several benefits: + // + // A. On a single input line, the order of annotations reflects + // the FileCheck logic for processing directives/patterns. + // This can be helpful in understanding cases in which the + // order of the associated directives/patterns in the check + // file or on the command line either (i) does not match the + // temporal order in which FileCheck looks for matches for the + // directives/patterns (due to, for example, CHECK-LABEL, + // CHECK-NOT, or `--implicit-check-not`) or (ii) does match + // that order but does not match the order of those + // diagnostics along an input line (due to, for example, + // CHECK-DAG). + // + // On the other hand, because our presentation format presents + // input lines in order, there's no clear way to offer the + // same benefit across input lines. For consistency, it might + // then seem worthwhile to have annotations on a single line + // also sorted in input order (that is, by input column). + // However, in practice, this appears to be more confusing + // than helpful. Perhaps it's intuitive to expect annotations + // to be listed in the temporal order in which they were + // produced except in cases the presentation format obviously + // and inherently cannot support it (that is, across input + // lines). + // + // B. When diagnostics' annotations are split among multiple + // input lines, the user must track them from one input line + // to the next. One property of the sort chosen here is that + // it facilitates the user in this regard by ensuring the + // following: when comparing any two input lines, a + // diagnostic's annotations are sorted in the same position + // relative to all other diagnostics' annotations. + return A.DiagIndex < B.DiagIndex; + }); + + // Compute the width of the label column. + const unsigned char *InputFilePtr = InputFileText.bytes_begin(), + *InputFileEnd = InputFileText.bytes_end(); + unsigned LineCount = InputFileText.count('\n'); + if (InputFileEnd[-1] != '\n') + ++LineCount; + unsigned LineNoWidth = std::log10(LineCount) + 1; + // +3 below adds spaces (1) to the left of the (right-aligned) line numbers + // on input lines and (2) to the right of the (left-aligned) labels on + // annotation lines so that input lines and annotation lines are more + // visually distinct. For example, the spaces on the annotation lines ensure + // that input line numbers and check directive line numbers never align + // horizontally. Those line numbers might not even be for the same file. + // One space would be enough to achieve that, but more makes it even easier + // to see. + LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; + + // Print annotated input lines. + unsigned PrevLineInFilter = 0; // 0 means none so far + unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none + std::string ElidedLines; + raw_string_ostream ElidedLinesOS(ElidedLines); + ColorMode TheColorMode = + WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable; + if (TheColorMode == ColorMode::Enable) + ElidedLinesOS.enable_colors(true); + auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); + for (unsigned Line = 1; + InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; + ++Line) { + const unsigned char *InputFileLine = InputFilePtr; + + // Compute the previous and next line included by the filter. + if (NextLineInFilter < Line) + NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line, + AnnotationItr, AnnotationEnd); + assert(NextLineInFilter && "expected NextLineInFilter to be computed"); + if (NextLineInFilter == Line) + PrevLineInFilter = Line; + + // Elide this input line and its annotations if it's not within the + // context specified by -dump-input-context of an input line included by + // -dump-input-filter. However, in case the resulting ellipsis would occupy + // more lines than the input lines and annotations it elides, buffer the + // elided lines and annotations so we can print them instead. + raw_ostream *LineOS; + if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) && + (NextLineInFilter == UINT_MAX || + Line + DumpInputContext < NextLineInFilter)) + LineOS = &ElidedLinesOS; + else { + LineOS = &OS; + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); + } + + // Print right-aligned line number. + WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false, + TheColorMode) + << format_decimal(Line, LabelWidth) << ": "; + + // For the case where -v and colors are enabled, find the annotations for + // good matches for expected patterns in order to highlight everything + // else in the line. There are no such annotations if -v is disabled. + std::vector FoundAndExpectedMatches; + if (Req.Verbose && TheColorMode == ColorMode::Enable) { + for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; + ++I) { + if (I->FoundAndExpectedMatch) + FoundAndExpectedMatches.push_back(*I); + } + } + + // Print numbered line with highlighting where there are no matches for + // expected patterns. + bool Newline = false; + { + WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false, + /*BG=*/false, TheColorMode); + bool InMatch = false; + if (Req.Verbose) + COS.changeColor(raw_ostream::CYAN, true, true); + for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { + bool WasInMatch = InMatch; + InMatch = false; + for (auto M : FoundAndExpectedMatches) { + if (M.InputStartCol <= Col && Col < M.InputEndCol) { + InMatch = true; + break; + } + } + if (!WasInMatch && InMatch) + COS.resetColor(); + else if (WasInMatch && !InMatch) + COS.changeColor(raw_ostream::CYAN, true, true); + if (*InputFilePtr == '\n') { + Newline = true; + COS << ' '; + } else + COS << *InputFilePtr; + ++InputFilePtr; + } + } + *LineOS << '\n'; + unsigned InputLineWidth = InputFilePtr - InputFileLine; + + // Print any annotations. + while (AnnotationItr != AnnotationEnd && + AnnotationItr->InputLine == Line) { + WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true, + /*BG=*/false, TheColorMode); + // The two spaces below are where the ": " appears on input lines. + COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; + unsigned Col; + for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) + COS << ' '; + COS << AnnotationItr->Marker.Lead; + // If InputEndCol=UINT_MAX, stop at InputLineWidth. + for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; + ++Col) + COS << '~'; + const std::string &Note = AnnotationItr->Marker.Note; + if (!Note.empty()) { + // Put the note at the end of the input line. If we were to instead + // put the note right after the marker, subsequent annotations for the + // same input line might appear to mark this note instead of the input + // line. + for (; Col <= InputLineWidth; ++Col) + COS << ' '; + COS << ' ' << Note; + } + COS << '\n'; + ++AnnotationItr; + } + } + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); + + OS << ">>>>>>\n"; +} + +int main(int argc, char **argv) { + // Enable use of ANSI color codes because FileCheck is using them to + // highlight text. + llvm::sys::Process::UseANSIEscapeCodes(true); + + InitLLVM X(argc, argv); + cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, + "FILECHECK_OPTS"); + + // Select -dump-input* values. The -help documentation specifies the default + // value and which value to choose if an option is specified multiple times. + // In the latter case, the general rule of thumb is to choose the value that + // provides the most information. + DumpInputValue DumpInput = + DumpInputs.empty() + ? DumpInputFail + : *std::max_element(DumpInputs.begin(), DumpInputs.end()); + DumpInputFilterValue DumpInputFilter; + if (DumpInputFilters.empty()) + DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll + : DumpInputFilterError; + else + DumpInputFilter = + *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end()); + unsigned DumpInputContext = DumpInputContexts.empty() + ? 5 + : *std::max_element(DumpInputContexts.begin(), + DumpInputContexts.end()); + + if (DumpInput == DumpInputHelp) { + DumpInputAnnotationHelp(outs()); + return 0; + } + if (CheckFilename.empty()) { + errs() << " not specified\n"; + return 2; + } + + FileCheckRequest Req; + append_range(Req.CheckPrefixes, CheckPrefixes); + + append_range(Req.CommentPrefixes, CommentPrefixes); + + append_range(Req.ImplicitCheckNot, ImplicitCheckNot); + + bool GlobalDefineError = false; + for (StringRef G : GlobalDefines) { + size_t EqIdx = G.find('='); + if (EqIdx == std::string::npos) { + errs() << "Missing equal sign in command-line definition '-D" << G + << "'\n"; + GlobalDefineError = true; + continue; + } + if (EqIdx == 0) { + errs() << "Missing variable name in command-line definition '-D" << G + << "'\n"; + GlobalDefineError = true; + continue; + } + Req.GlobalDefines.push_back(G); + } + if (GlobalDefineError) + return 2; + + Req.AllowEmptyInput = AllowEmptyInput; + Req.AllowUnusedPrefixes = AllowUnusedPrefixes; + Req.EnableVarScope = EnableVarScope; + Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; + Req.Verbose = Verbose; + Req.VerboseVerbose = VerboseVerbose; + Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; + Req.MatchFullLines = MatchFullLines; + Req.IgnoreCase = IgnoreCase; + + if (VerboseVerbose) + Req.Verbose = true; + + FileCheck FC(Req); + if (!FC.ValidateCheckPrefixes()) + return 2; + + Regex PrefixRE = FC.buildCheckPrefixRegex(); + std::string REError; + if (!PrefixRE.isValid(REError)) { + errs() << "Unable to combine check-prefix strings into a prefix regular " + "expression! This is likely a bug in FileCheck's verification of " + "the check-prefix strings. Regular expression parsing failed " + "with the following error: " + << REError << "\n"; + return 2; + } + + SourceMgr SM; + + // Read the expected strings from the check file. + ErrorOr> CheckFileOrErr = + MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true); + if (std::error_code EC = CheckFileOrErr.getError()) { + errs() << "Could not open check file '" << CheckFilename + << "': " << EC.message() << '\n'; + return 2; + } + MemoryBuffer &CheckFile = *CheckFileOrErr.get(); + + SmallString<4096> CheckFileBuffer; + StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); + + unsigned CheckFileBufferID = + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + CheckFileText, CheckFile.getBufferIdentifier()), + SMLoc()); + + std::pair ImpPatBufferIDRange; + if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange)) + return 2; + + // Open the file to check and add it to SourceMgr. + ErrorOr> InputFileOrErr = + MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true); + if (InputFilename == "-") + InputFilename = ""; // Overwrite for improved diagnostic messages + if (std::error_code EC = InputFileOrErr.getError()) { + errs() << "Could not open input file '" << InputFilename + << "': " << EC.message() << '\n'; + return 2; + } + MemoryBuffer &InputFile = *InputFileOrErr.get(); + + if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { + errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; + DumpCommandLine(argc, argv); + return 2; + } + + SmallString<4096> InputFileBuffer; + StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); + + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + InputFileText, InputFile.getBufferIdentifier()), + SMLoc()); + + std::vector Diags; + int ExitCode = FC.checkInput(SM, InputFileText, + DumpInput == DumpInputNever ? nullptr : &Diags) + ? EXIT_SUCCESS + : 1; + if (DumpInput == DumpInputAlways || + (ExitCode == 1 && DumpInput == DumpInputFail)) { + errs() << "\n" + << "Input file: " << InputFilename << "\n" + << "Check file: " << CheckFilename << "\n" + << "\n" + << "-dump-input=help explains the following input dump.\n" + << "\n"; + std::vector Annotations; + unsigned LabelWidth; + BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags, + Annotations, LabelWidth); + DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext, + InputFileText, Annotations, LabelWidth); + } + + return ExitCode; +} From cdfcbbc50ab714f803b1cf44cfeaea27567c60b2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 5 Jun 2023 18:18:42 +0200 Subject: [PATCH 172/301] Fix tests race condition for fail_compilation/failcstuff4.{c,i} When compiling failcstuff4.c, the compiler generates a temporary failcstuff4.i for the preprocessed file, and removes that file after reading it. Too bad there's a failcstuff4.i under version control - rename it. I've seen according sporadic failures for the DMD testsuite, when the *.c is tested earlier, and the failcstuff4.i test later fails because the source file was removed from disk. --- tests/dmd/fail_compilation/{failcstuff4.i => failcstuff4b.i} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/dmd/fail_compilation/{failcstuff4.i => failcstuff4b.i} (62%) diff --git a/tests/dmd/fail_compilation/failcstuff4.i b/tests/dmd/fail_compilation/failcstuff4b.i similarity index 62% rename from tests/dmd/fail_compilation/failcstuff4.i rename to tests/dmd/fail_compilation/failcstuff4b.i index df26647a5c4..66ee7578947 100644 --- a/tests/dmd/fail_compilation/failcstuff4.i +++ b/tests/dmd/fail_compilation/failcstuff4b.i @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/failcstuff4.i(605): Error: invalid flag for line marker directive +fail_compilation/failcstuff4b.i(605): Error: invalid flag for line marker directive --- */ From f21241b66226f52beee96a18d50fba4f7a156f61 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 6 Jun 2023 21:21:34 +0200 Subject: [PATCH 173/301] Cirrus CI macOS arm64: Work around sporadic core.thread.fiber-shared failures (#4418) Not sure if it's just the *-shared variant; we'll see. --- .cirrus.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index d0e798040a0..9d9731a04ac 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -65,6 +65,9 @@ common_steps_template: &COMMON_STEPS_TEMPLATE excludes+='|^std.math.exponential(-shared)?$' # FIXME: failure excludes+='|^druntime-test-exceptions-debug$' + elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then + # FIXME: sporadic segfaults/bus errors with enabled optimizations + excludes+='|^core.thread.fiber-shared$' fi ctest -j$PARALLELISM --output-on-failure -E "$excludes" From 5b137768a0925f357195b6823772189ecbc38c26 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 5 Jun 2023 13:50:41 +0200 Subject: [PATCH 174/301] Run C preprocessor --- CMakeLists.txt | 1 + driver/archiver.cpp | 2 +- driver/cpreprocessor.cpp | 84 ++++++++++++++++++++++++++++++ driver/cpreprocessor.h | 8 +++ driver/linker-gcc.cpp | 3 +- driver/linker-msvc.cpp | 7 +-- driver/linker.cpp | 2 +- driver/main.cpp | 3 ++ driver/toobj.cpp | 2 +- driver/tool.cpp | 10 ++-- driver/tool.h | 6 ++- tests/dmd/compilable/stdcheaders.c | 1 - tests/dmd/compilable/test22809.c | 2 - tests/dmd/compilable/test23214.c | 1 - tests/dmd/compilable/test23583.c | 1 - tests/dmd/compilable/test23616.c | 1 - tests/dmd/runnable/initializer.c | 2 +- 17 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 driver/cpreprocessor.cpp create mode 100644 driver/cpreprocessor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e0ebfd8d9c5..e07b0a0e70b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -378,6 +378,7 @@ set(DRV_SRC driver/cl_options-llvm.cpp driver/codegenerator.cpp driver/configfile.cpp + driver/cpreprocessor.cpp driver/dcomputecodegenerator.cpp driver/exe_path.cpp driver/targetmachine.cpp diff --git a/driver/archiver.cpp b/driver/archiver.cpp index 95ba1dc131b..bf5ce937d14 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -352,7 +352,7 @@ int createStaticLibrary() { } // invoke external archiver - return executeToolAndWait(tool, args, global.params.verbose); + return executeToolAndWait(Loc(), tool, args, global.params.verbose); } const char *getPathToProducedStaticLibrary() { diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp new file mode 100644 index 00000000000..f6d95b9ba18 --- /dev/null +++ b/driver/cpreprocessor.cpp @@ -0,0 +1,84 @@ +#include "driver/cpreprocessor.h" + +#include "dmd/errors.h" +#include "driver/cl_options.h" +#include "driver/tool.h" + +namespace { +const char *getPathToImportc_h(const Loc &loc) { + // importc.h should be next to object.d + static const char *cached = nullptr; + if (!cached) { + cached = FileName::searchPath(global.path, "importc.h", false); + if (!cached) { + error(loc, "cannot find \"importc.h\" along import path"); + fatal(); + } + } + return cached; +} +} // anonymous namespace + +FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, + OutBuffer &defines) { + const char *importc_h = getPathToImportc_h(loc); + const char *ifilename = FileName::forceExt(csrcfile.toChars(), i_ext.ptr); + + const auto &triple = *global.params.targetTriple; + const bool isMSVC = triple.isWindowsMSVCEnvironment(); + +#if 0 //ifdef _WIN32 + // TODO: INCLUDE env var etc.? + windows::MsvcEnvironmentScope msvcEnv; + if (isMSVC) + msvcEnv.setup(); +#endif + + const std::string cc = getGcc(isMSVC ? "cl.exe" : "cc"); + std::vector args; + + if (!isMSVC) + appendTargetArgsForGcc(args); + + if (triple.isOSDarwin()) + args.push_back("-fno-blocks"); // disable blocks extension + + for (const auto &ccSwitch : opts::ccSwitches) { + args.push_back(ccSwitch); + } + // TODO: -Xcpp switches? + + if (isMSVC) { + args.push_back("/P"); // run preprocessor + args.push_back("/Zc:preprocessor"); // use the new conforming preprocessor + args.push_back("/PD"); // undocumented: print all macro definitions + args.push_back("/nologo"); + args.push_back(csrcfile.toChars()); + args.push_back((llvm::Twine("/FI") + importc_h).str()); + // preprocessed output file + args.push_back((llvm::Twine("/Fi") + ifilename).str()); + } else { // Posix + // merge #define's with output: + // https://gcc.gnu.org/onlinedocs/cpp/Invocation.html#index-dD + args.push_back("-dD"); + + // need to redefine some macros in importc.h + args.push_back("-Wno-builtin-macro-redefined"); + + args.push_back("-E"); // run preprocessor only + args.push_back("-include"); + args.push_back(importc_h); + args.push_back(csrcfile.toChars()); + args.push_back("-o"); + args.push_back(ifilename); + } + + const int status = executeToolAndWait(loc, cc, args, global.params.verbose); + if (status) { + errorSupplemental(loc, "C preprocessor failed for file '%s'", csrcfile.toChars()); + fatal(); + } + + ifile = true; + return FileName::create(ifilename); +} diff --git a/driver/cpreprocessor.h b/driver/cpreprocessor.h new file mode 100644 index 00000000000..ae0a2c588bc --- /dev/null +++ b/driver/cpreprocessor.h @@ -0,0 +1,8 @@ +#pragma once + +#include "dmd/common/outbuffer.h" +#include "dmd/globals.h" +#include "dmd/root/filename.h" + +FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, + OutBuffer &defines); diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 85bd2761891..f755258c95b 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -858,5 +858,6 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, logstr << "\n"; // FIXME where's flush ? // try to call linker - return executeToolAndWait(tool, argsBuilder->args, global.params.verbose); + return executeToolAndWait(Loc(), tool, argsBuilder->args, + global.params.verbose); } diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp index 5a07f885b18..43c162e6444 100644 --- a/driver/linker-msvc.cpp +++ b/driver/linker-msvc.cpp @@ -88,11 +88,6 @@ void addSanitizerLibs(std::vector &args) { int linkObjToBinaryMSVC(llvm::StringRef outputPath, const std::vector &defaultLibNames) { - if (!opts::ccSwitches.empty()) { - error(Loc(), "-Xcc is not supported for MSVC"); - fatal(); - } - #ifdef _WIN32 windows::MsvcEnvironmentScope msvcEnv; @@ -309,5 +304,5 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, #endif } - return executeToolAndWait(linker, args, global.params.verbose); + return executeToolAndWait(Loc(), linker, args, global.params.verbose); } diff --git a/driver/linker.cpp b/driver/linker.cpp index 76266379d29..b554822f688 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -323,7 +323,7 @@ int runProgram() { // Run executable int status = - executeToolAndWait(gExePath, opts::runargs, global.params.verbose); + executeToolAndWait(Loc(), gExePath, opts::runargs, global.params.verbose); if (status < 0) { #if defined(_MSC_VER) || defined(__MINGW32__) error(Loc(), "program received signal %d", -status); diff --git a/driver/main.cpp b/driver/main.cpp index 6d1c3441eb0..2b35dee08b9 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -29,6 +29,7 @@ #include "driver/cl_options_sanitizers.h" #include "driver/codegenerator.h" #include "driver/configfile.h" +#include "driver/cpreprocessor.h" #include "driver/dcomputecodegenerator.h" #include "driver/exe_path.h" #include "driver/ldc-version.h" @@ -1183,6 +1184,8 @@ int cppmain() { global.params.dllimport = DLLImport::none; } + global.preprocess = &runCPreprocessor; + // allocate the target abi gABI = TargetABI::getTarget(); diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 8e1ff6832f1..c03ddda0112 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -145,7 +145,7 @@ static void assemble(const std::string &asmpath, const std::string &objpath) { appendTargetArgsForGcc(args); // Run the compiler to assembly the program. - int R = executeToolAndWait(getGcc(), args, global.params.verbose); + int R = executeToolAndWait(Loc(), getGcc(), args, global.params.verbose); if (R) { error(Loc(), "Error while invoking external assembler."); fatal(); diff --git a/driver/tool.cpp b/driver/tool.cpp index b5ceb226a9e..d3f4cd150d7 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -76,7 +76,7 @@ std::string getProgram(const char *fallbackName, //////////////////////////////////////////////////////////////////////////////// -std::string getGcc() { return getProgram("cc", &gcc, "CC"); } +std::string getGcc(const char *fallback) { return getProgram(fallback, &gcc, "CC"); } //////////////////////////////////////////////////////////////////////////////// @@ -223,11 +223,11 @@ std::vector getFullArgs(const char *tool, //////////////////////////////////////////////////////////////////////////////// -int executeToolAndWait(const std::string &tool_, +int executeToolAndWait(const Loc &loc, const std::string &tool_, const std::vector &args, bool verbose) { const auto tool = findProgramByName(tool_); if (tool.empty()) { - error(Loc(), "cannot find program `%s`", tool_.c_str()); + error(loc, "cannot find program `%s`", tool_.c_str()); return -1; } @@ -249,9 +249,9 @@ int executeToolAndWait(const std::string &tool_, args::executeAndWait(std::move(fullArgs), rspEncoding, &errorMsg); if (status) { - error(Loc(), "%s failed with status: %d", tool.c_str(), status); + error(loc, "%s failed with status: %d", tool.c_str(), status); if (!errorMsg.empty()) { - errorSupplemental(Loc(), "message: %s", errorMsg.c_str()); + errorSupplemental(loc, "message: %s", errorMsg.c_str()); } } diff --git a/driver/tool.h b/driver/tool.h index d65d11ea79c..21457dd0648 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -19,11 +19,13 @@ #include "llvm/Support/CommandLine.h" +struct Loc; + namespace opts { extern llvm::cl::opt linker; } -std::string getGcc(); +std::string getGcc(const char *fallback = "cc"); void appendTargetArgsForGcc(std::vector &args); std::string getProgram(const char *fallbackName, @@ -37,7 +39,7 @@ std::vector getFullArgs(const char *tool, const std::vector &args, bool printVerbose); -int executeToolAndWait(const std::string &tool, +int executeToolAndWait(const Loc &loc, const std::string &tool, const std::vector &args, bool verbose = false); diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 5fef0eeff78..90c0339a841 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -1,6 +1,5 @@ /* Do a smoke test of the C Standard headers. * Many platforms do not support all the C Standard headers. - * DISABLED: LDC // FIXME: needs preprocessor */ #include diff --git a/tests/dmd/compilable/test22809.c b/tests/dmd/compilable/test22809.c index fcf3fbf4c4b..68752d50a04 100644 --- a/tests/dmd/compilable/test22809.c +++ b/tests/dmd/compilable/test22809.c @@ -15,7 +15,6 @@ int y = ((size_t)((char *)&((struct Foo *)1)->x - (char *)1)); _Static_assert(((size_t)((char *)&((struct Foo *)0)->y - (char *)0))==4, ""); -/* LDC FIXME: needs preprocessor (for including importc.h) // https://issues.dlang.org/show_bug.cgi?id=23584 int foo(float bar) @@ -28,4 +27,3 @@ void test23584() int i = foo(3.5); _Static_assert(foo(3.5) == 0x40600000, "1"); } -*/ diff --git a/tests/dmd/compilable/test23214.c b/tests/dmd/compilable/test23214.c index 92f35e8de76..9aa38bef542 100644 --- a/tests/dmd/compilable/test23214.c +++ b/tests/dmd/compilable/test23214.c @@ -1,4 +1,3 @@ // https://issues.dlang.org/show_bug.cgi?id=23214 -// DISABLED: LDC // FIXME - need to invoke C pre-processor typedef unsigned __int64 uintptr_t; diff --git a/tests/dmd/compilable/test23583.c b/tests/dmd/compilable/test23583.c index 60ca47cab94..480ceff1822 100644 --- a/tests/dmd/compilable/test23583.c +++ b/tests/dmd/compilable/test23583.c @@ -1,4 +1,3 @@ -// DISABLED: LDC // FIXME: needs preprocessor // https://issues.dlang.org/show_bug.cgi?id=23580 // https://issues.dlang.org/show_bug.cgi?id=23581 // https://issues.dlang.org/show_bug.cgi?id=23582 diff --git a/tests/dmd/compilable/test23616.c b/tests/dmd/compilable/test23616.c index 019cea90afc..4b9ebca4fe8 100644 --- a/tests/dmd/compilable/test23616.c +++ b/tests/dmd/compilable/test23616.c @@ -1,4 +1,3 @@ -// DISABLED: LDC // FIXME: needs preprocessor // https://issues.dlang.org/show_bug.cgi?id=23616 #if __has_extension(gnu_asm) diff --git a/tests/dmd/runnable/initializer.c b/tests/dmd/runnable/initializer.c index a3a04020e8d..19261dc11d8 100644 --- a/tests/dmd/runnable/initializer.c +++ b/tests/dmd/runnable/initializer.c @@ -1,4 +1,4 @@ -// DISABLED: LDC // FIXME: needs preprocessor for __LINE__ +// DISABLED: LDC // FIXME: needs support for importC special cases /* Test initializers */ From 1bf7e28c5933a76bf1c832ede98c9ac5b47a6f1c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 5 Jun 2023 17:25:43 +0200 Subject: [PATCH 175/301] Add -Xcpp CLI option for C preprocessor flags (-P with ldmd2) --- driver/cl_options.cpp | 22 ++++++++++++---------- driver/cl_options.h | 1 + driver/cpreprocessor.cpp | 4 +++- driver/ldmd.cpp | 10 ++++++++-- tests/dmd/compilable/cppflags.c | 1 - 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 6c308675de8..9fa44694ab5 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -400,16 +400,18 @@ cl::list cl::value_desc("linkerflag"), cl::cat(linkingCategory), cl::Prefix); -cl::list - ccSwitches("Xcc", cl::desc("Pass to GCC/Clang for linking"), - cl::value_desc("ccflag"), cl::cat(linkingCategory)); - -cl::opt - moduleDeps("deps", cl::ValueOptional, cl::ZeroOrMore, - cl::value_desc("filename"), - cl::desc("Write module dependencies to (only imports). " - "'-deps' alone prints module dependencies " - "(imports/file/version/debug/lib)")); +cl::list ccSwitches( + "Xcc", cl::value_desc("ccflag"), cl::cat(linkingCategory), + cl::desc("Pass to GCC/Clang for linking/preprocessing")); + +cl::list cppSwitches("Xcpp", cl::value_desc("cppflag"), + cl::desc("Pass to C preprocessor")); + +cl::opt moduleDeps( + "deps", cl::ValueOptional, cl::ZeroOrMore, cl::value_desc("filename"), + cl::desc("Write module dependencies to (only imports). " + "'-deps' alone prints module dependencies " + "(imports/file/version/debug/lib)")); cl::opt makeDeps("makedeps", cl::ValueOptional, cl::ZeroOrMore, diff --git a/driver/cl_options.h b/driver/cl_options.h index 8bf0866619a..b78a715e161 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -75,6 +75,7 @@ extern cl::opt makeDeps; extern cl::opt cacheDir; extern cl::list linkerSwitches; extern cl::list ccSwitches; +extern cl::list cppSwitches; extern cl::list includeModulePatterns; extern cl::opt m32bits; diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index f6d95b9ba18..791c4257eb0 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -46,7 +46,9 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, for (const auto &ccSwitch : opts::ccSwitches) { args.push_back(ccSwitch); } - // TODO: -Xcpp switches? + for (const auto &cppSwitch : opts::cppSwitches) { + args.push_back(cppSwitch); + } if (isMSVC) { args.push_back("/P"); // run preprocessor diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 4a9aaf63694..ba7f0ad9dd8 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -234,7 +234,9 @@ Where:\n\ #if 0 " -os= sets target operating system to \n" #endif -" -preview= enable an upcoming language change identified by 'name'\n\ +" -P=\n\ + pass preprocessorflag to C preprocessor\n\ + -preview= enable an upcoming language change identified by 'name'\n\ -preview=[h|help|?]\n\ list all upcoming language changes\n\ -profile profile runtime performance of generated code\n" @@ -707,7 +709,11 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, exit(EXIT_SUCCESS); } /* -L - * -defaultlib + */ + else if (p[1] == 'P') { + ldcArgs.push_back(concat("-Xcpp=", p + 2 + (p[2] == '=' ? 1 : 0))); + } + /* -defaultlib * -debuglib * -deps * -main diff --git a/tests/dmd/compilable/cppflags.c b/tests/dmd/compilable/cppflags.c index e5be469fe92..927463631fa 100644 --- a/tests/dmd/compilable/cppflags.c +++ b/tests/dmd/compilable/cppflags.c @@ -1,5 +1,4 @@ /* REQUIRED_ARGS: -P=-DABC=3 */ -// DISABLED: LDC // FIXME _Static_assert(ABC == 3, "1"); From cf74b3d555fd2cc903de838e297f55122b35fc71 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 5 Jun 2023 21:05:54 +0200 Subject: [PATCH 176/301] Windows: Prefer clang-cl.exe over cl.exe for C preprocessing --- driver/cpreprocessor.cpp | 51 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index 791c4257eb0..b3acc9e64d9 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -3,6 +3,7 @@ #include "dmd/errors.h" #include "driver/cl_options.h" #include "driver/tool.h" +#include "llvm/Support/Program.h" namespace { const char *getPathToImportc_h(const Loc &loc) { @@ -17,6 +18,29 @@ const char *getPathToImportc_h(const Loc &loc) { } return cached; } + +const std::string &getCC(bool isMSVC) { + static std::string cached; + if (cached.empty()) { + std::string fallback = "cc"; + if (isMSVC) { +#ifdef _WIN32 + // by default, prefer clang-cl.exe (if in PATH) over cl.exe + // (e.g., no echoing of source filename being preprocessed to stderr) + auto found = llvm::sys::findProgramByName("clang-cl.exe"); + if (found) { + fallback = found.get(); + } else { + fallback = "cl.exe"; + } +#else + fallback = "clang-cl"; +#endif + } + cached = getGcc(fallback.c_str()); + } + return cached; +} } // anonymous namespace FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, @@ -34,7 +58,7 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, msvcEnv.setup(); #endif - const std::string cc = getGcc(isMSVC ? "cl.exe" : "cc"); + const std::string &cc = getCC(isMSVC); std::vector args; if (!isMSVC) @@ -51,10 +75,29 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, } if (isMSVC) { - args.push_back("/P"); // run preprocessor - args.push_back("/Zc:preprocessor"); // use the new conforming preprocessor - args.push_back("/PD"); // undocumented: print all macro definitions args.push_back("/nologo"); + args.push_back("/P"); // preprocess only + + const bool isClangCl = llvm::StringRef(cc) +#if LDC_LLVM_VER >= 1300 + .contains_insensitive("clang-cl"); +#else + .contains_lower("clang-cl"); +#endif + + if (!isClangCl) { + args.push_back("/PD"); // print all macro definitions + args.push_back("/Zc:preprocessor"); // use the new conforming preprocessor + } else { + // print macro definitions (clang-cl doesn't support /PD - use clang's + // -dD) + args.push_back("-Xclang"); + args.push_back("-dD"); + + // need to redefine some macros in importc.h + args.push_back("-Wno-builtin-macro-redefined"); + } + args.push_back(csrcfile.toChars()); args.push_back((llvm::Twine("/FI") + importc_h).str()); // preprocessed output file From 6f4d96275660cd09e5f0e64ccd4c810336ae4fad Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 6 Jun 2023 16:14:46 +0200 Subject: [PATCH 177/301] Posix x86_64: Rename object.__va_list alias to object.__va_list_tag To overcome an importC issue for FreeBSD, where a C system header (/usr/include/sys/_types.h) defines a __va_list typedef, leading to a 'recursive alias declaration' error. --- gen/abi/x86-64.cpp | 4 ++-- runtime/druntime/src/__builtins.di | 15 +++++++-------- runtime/druntime/src/object.d | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index df2491f4a27..5bb4534e955 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -322,7 +322,7 @@ void X86_64TargetABI::rewriteVarargs(IrFuncTy &fty, /** * The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct * passed by reference. - * In druntime, the struct is aliased as object.__va_list; the actually used + * In druntime, the struct is aliased as object.__va_list_tag; the actually used * core.stdc.stdarg.va_list type is a __va_list_tag* pointer though to achieve * byref semantics. * This requires a little bit of compiler magic in the following @@ -375,7 +375,7 @@ Type *X86_64TargetABI::vaListType() { // using TypeIdentifier here is a bit wonky but works, as long as the name // is actually available in the scope (this is what DMD does, so if a better // solution is found there, this should be adapted). - return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")) + return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")) ->pointerTo(); } diff --git a/runtime/druntime/src/__builtins.di b/runtime/druntime/src/__builtins.di index 172c1b96f2a..94d0a8c7e01 100644 --- a/runtime/druntime/src/__builtins.di +++ b/runtime/druntime/src/__builtins.di @@ -21,12 +21,6 @@ module __builtins; alias va_list = imported!"core.stdc.stdarg".va_list; -version (Posix) -{ - version (X86_64) - alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag; -} - version (LDC) { // For some targets, __builtin_va_list resolves to __va_list. @@ -35,13 +29,13 @@ version (LDC) version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; - // Define a __va_list alias if the platform uses an elaborate type, as it + // Define a __va_list[_tag] alias if the platform uses an elaborate type, as it // is referenced from implicitly generated code for D-style variadics, etc. // LDC does not require people to manually import core.vararg like DMD does. version (X86_64) { version (Win64) {} else - public import core.internal.vararg.sysv_x64 : __va_list; + alias __va_list_tag = imported!"core.internal.vararg.sysv_x64".__va_list_tag; } else version (ARM_Any) { @@ -58,6 +52,11 @@ version (LDC) public import core.internal.vararg.aarch64 : __va_list; } } +else version (Posix) +{ + version (X86_64) + alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag; +} alias __builtin_va_start = imported!"core.stdc.stdarg".va_start; diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index c24effd58d4..62b0234a7dc 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -81,13 +81,13 @@ version (LDC) // note: there's a copy for importC in __builtins.di version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; - // Define a __va_list alias if the platform uses an elaborate type, as it + // Define a __va_list[_tag] alias if the platform uses an elaborate type, as it // is referenced from implicitly generated code for D-style variadics, etc. // LDC does not require people to manually import core.vararg like DMD does. version (X86_64) { version (Win64) {} else - public import core.internal.vararg.sysv_x64 : __va_list; + alias __va_list_tag = imported!"core.internal.vararg.sysv_x64".__va_list_tag; } else version (ARM_Any) { From 75c89a5e7151941d478ca2462f9ab90081c6821f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 6 Jun 2023 17:14:05 +0200 Subject: [PATCH 178/301] Slightly adapt compilable/stdcheaders.c for Linux AArch64 --- tests/dmd/compilable/stdcheaders.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 90c0339a841..4b7399d8e46 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -72,11 +72,13 @@ #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` #ifndef __FreeBSD__ // #includes complex.h +#if !(defined(__linux__) && defined(__aarch64__)) // /tmp/clang/lib/clang/15.0.3/include/tgmath.h(34): Error: named parameter required before `...` #include #endif #endif #endif #endif +#endif #ifndef __DMC__ #ifndef __linux__ From 3ba7ec155edb08911a07f881fc53adf8045930e5 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 7 Jun 2023 17:07:20 +0200 Subject: [PATCH 179/301] druntime: Fix importc.h for Linux AArch64 This fixes compilable/stdcheaders.c. --- runtime/druntime/src/importc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 5d50e541969..3db162120dd 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -123,9 +123,11 @@ // Ubuntu's assert.h uses this #define __PRETTY_FUNCTION__ __func__ +#ifndef __aarch64__ #define _Float128 long double #define __float128 long double #endif +#endif #if __APPLE__ #undef __SIZEOF_INT128__ From 32176ced563f79489cd06ce9ccbec89ad0c02c34 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 7 Jun 2023 15:20:28 +0200 Subject: [PATCH 180/301] Use proper temp files for preprocessed .i files I thought DMD would create the temp .i file in the same directory as the .c file, but it creates it in the current working dir. Create them in a dedicated new temp directory now, to prevent collisions (parallel compiler invocations in the same working dir etc. etc.). --- dmd/dmodule.d | 7 +++++++ dmd/root/file.d | 19 +++++++++++++++++++ driver/cpreprocessor.cpp | 32 ++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 4bd20cf1101..1900f368063 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -744,7 +744,14 @@ else scope (exit) { if (ifile) + { File.remove(filename.toChars()); // remove generated file +version (IN_LLVM) +{ + // and the parent directory for LDC (each .i gets its own temp dir) + File.removeDirectory(FileName.path(filename.toChars())); +} + } } if (global.preprocess && diff --git a/dmd/root/file.d b/dmd/root/file.d index 1fb105682ea..df9cf8ccdd0 100644 --- a/dmd/root/file.d +++ b/dmd/root/file.d @@ -201,6 +201,25 @@ nothrow: } } +version (IN_LLVM) +{ + extern (C++) static void removeDirectory(const(char)* name) + { + version (Posix) + { + .remove(name); + } + else version (Windows) + { + name.toDString.extendedPathThen!(p => RemoveDirectoryW(p.ptr)); + } + else + { + static assert(0); + } + } +} + /*************************************************** * Update file * diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index b3acc9e64d9..0e9ce48e24a 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -3,6 +3,8 @@ #include "dmd/errors.h" #include "driver/cl_options.h" #include "driver/tool.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Program.h" namespace { @@ -41,12 +43,32 @@ const std::string &getCC(bool isMSVC) { } return cached; } + +FileName getOutputPath(const Loc &loc, const char *csrcfile) { + llvm::SmallString<64> buffer; + + // 1) create a new temporary directory (e.g., `/tmp/itmp-ldc-10ecec`) + auto ec = llvm::sys::fs::createUniqueDirectory("itmp-ldc", buffer); + if (ec) { + error(loc, + "failed to create temporary directory for preprocessed .i file: %s\n%s", + buffer.c_str(), ec.message().c_str()); + fatal(); + } + + // 2) append the .c file name, replacing the extension with .i + llvm::sys::path::append(buffer, FileName::name(csrcfile)); + llvm::sys::path::replace_extension(buffer, i_ext.ptr); + + // the directory is removed (after the file) in Module.read() + + return FileName::create(buffer.c_str()); // allocates a copy +} } // anonymous namespace FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, OutBuffer &defines) { const char *importc_h = getPathToImportc_h(loc); - const char *ifilename = FileName::forceExt(csrcfile.toChars(), i_ext.ptr); const auto &triple = *global.params.targetTriple; const bool isMSVC = triple.isWindowsMSVCEnvironment(); @@ -58,6 +80,8 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, msvcEnv.setup(); #endif + FileName ipath = getOutputPath(loc, csrcfile.toChars()); + const std::string &cc = getCC(isMSVC); std::vector args; @@ -101,7 +125,7 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, args.push_back(csrcfile.toChars()); args.push_back((llvm::Twine("/FI") + importc_h).str()); // preprocessed output file - args.push_back((llvm::Twine("/Fi") + ifilename).str()); + args.push_back((llvm::Twine("/Fi") + ipath.toChars()).str()); } else { // Posix // merge #define's with output: // https://gcc.gnu.org/onlinedocs/cpp/Invocation.html#index-dD @@ -115,7 +139,7 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, args.push_back(importc_h); args.push_back(csrcfile.toChars()); args.push_back("-o"); - args.push_back(ifilename); + args.push_back(ipath.toChars()); } const int status = executeToolAndWait(loc, cc, args, global.params.verbose); @@ -125,5 +149,5 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, } ifile = true; - return FileName::create(ifilename); + return ipath; } From da225c51bc9cb95df35e2e050369021571113c93 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 7 Jun 2023 18:35:40 +0200 Subject: [PATCH 181/301] [add changelog entry] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c328259200..fda1a99c827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) +- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-Xcpp`. (#4417) + - Windows: Requires an MSVC environment set up for the target (`cl.exe` on `PATH`, `INCLUDE` environment variable). If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). #### Platform support From da050e5d9eb000cb4c021e6050e0586e0df1742c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 7 Jun 2023 20:31:32 +0200 Subject: [PATCH 182/301] Drop -Xcpp, adopt DMD's -P instead --- CHANGELOG.md | 2 +- driver/cl_options.cpp | 2 +- driver/ldmd.cpp | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda1a99c827..75f377eb97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) -- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-Xcpp`. (#4417) +- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - Windows: Requires an MSVC environment set up for the target (`cl.exe` on `PATH`, `INCLUDE` environment variable). If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). #### Platform support diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 9fa44694ab5..ac003542b41 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -404,7 +404,7 @@ cl::list ccSwitches( "Xcc", cl::value_desc("ccflag"), cl::cat(linkingCategory), cl::desc("Pass to GCC/Clang for linking/preprocessing")); -cl::list cppSwitches("Xcpp", cl::value_desc("cppflag"), +cl::list cppSwitches("P", cl::value_desc("cppflag"), cl::Prefix, cl::desc("Pass to C preprocessor")); cl::opt moduleDeps( diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index ba7f0ad9dd8..888b2efc5bd 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -709,11 +709,8 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, exit(EXIT_SUCCESS); } /* -L - */ - else if (p[1] == 'P') { - ldcArgs.push_back(concat("-Xcpp=", p + 2 + (p[2] == '=' ? 1 : 0))); - } - /* -defaultlib + * -P + * -defaultlib * -debuglib * -deps * -main From a80dd003cd41af83b2725fad6d73fea952dd9716 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 8 Jun 2023 02:39:38 +0200 Subject: [PATCH 183/301] Handle lambda mangle collisions by emitting lambdas with internal linkage (#4415) See https://issues.dlang.org/show_bug.cgi?id=23722. --- CHANGELOG.md | 1 + gen/tollvm.cpp | 15 ++++++++++----- tests/codegen/inputs/lambdas_dmd23722b.d | 16 ++++++++++++++++ tests/codegen/lambdas_dmd23722.d | 21 +++++++++++++++++++++ tests/codegen/lambdas_gh3648.d | 18 +++++++++--------- tests/codegen/lambdas_gh3648b.d | 12 ++++++------ 6 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 tests/codegen/inputs/lambdas_dmd23722b.d create mode 100644 tests/codegen/lambdas_dmd23722.d diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c328259200..b3aef984dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ #### Platform support #### Bug fixes +- Handle potential lambda mangle collisions across separately compiled object files (and the linker then silently picking an arbitrary implementation). Lambdas (and their nested global variables) are now internal to each referencing object file (`static` linkage in C). (#4415) # LDC 1.32.2 (2023-05-12) diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index d51cd0d54f9..06eab6d90d0 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -232,9 +232,13 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { if (hasWeakUDA(sym)) { linkage = LLGlobalValue::WeakAnyLinkage; } else { - // Function (incl. delegate) literals are emitted into each referencing - // compilation unit, so use linkonce_odr for all lambdas and all global - // variables they define. + /* Function (incl. delegate) literals are emitted into each referencing + * compilation unit, so use internal linkage for all lambdas and all global + * variables they define. + * This makes sure these symbols don't accidentally collide when linking + * object files compiled by different compiler invocations (lambda mangles + * aren't stable - see https://issues.dlang.org/show_bug.cgi?id=23722). + */ auto potentialLambda = sym; if (auto vd = sym->isVarDeclaration()) { if (vd->isDataseg()) @@ -242,7 +246,7 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { } if (potentialLambda->isFuncLiteralDeclaration()) { - linkage = LLGlobalValue::LinkOnceODRLinkage; + linkage = LLGlobalValue::InternalLinkage; } else if (sym->isInstantiated()) { linkage = templateLinkage; } @@ -309,8 +313,9 @@ void setVisibility(Dsymbol *sym, llvm::GlobalObject *obj) { } else { if (sym->isExport()) { obj->setVisibility(LLGlobalValue::DefaultVisibility); // overrides @hidden - } else if (!hasHiddenUDA) { + } else if (!obj->hasLocalLinkage() && !hasHiddenUDA) { // Hide with -fvisibility=hidden, or linkonce_odr etc. + // Note that symbols with local linkage cannot be hidden (LLVM assertion). // The Apple linker warns about hidden linkonce_odr symbols from object // files compiled with -linkonce-templates being folded with *public* // weak_odr symbols from non-linkonce-templates code (e.g., Phobos), so diff --git a/tests/codegen/inputs/lambdas_dmd23722b.d b/tests/codegen/inputs/lambdas_dmd23722b.d new file mode 100644 index 00000000000..45aedc6d72a --- /dev/null +++ b/tests/codegen/inputs/lambdas_dmd23722b.d @@ -0,0 +1,16 @@ +module lambdas_dmd23722b; + +struct A { + import core.stdc.stdio; + alias x = () { + printf("x\n"); + }; + alias y = () { + printf("y\n"); + }; +} + +// do_x should call A.x (and print "x") +void do_x() { + A.x(); +} diff --git a/tests/codegen/lambdas_dmd23722.d b/tests/codegen/lambdas_dmd23722.d new file mode 100644 index 00000000000..b311b730ea4 --- /dev/null +++ b/tests/codegen/lambdas_dmd23722.d @@ -0,0 +1,21 @@ +// Test that colliding lambda mangles don't lead to symbol collision during linking, +// see https://issues.dlang.org/show_bug.cgi?id=23722 + +// compile both modules separately, then link and check runtime output +// RUN: %ldc -c %S/inputs/lambdas_dmd23722b.d -of=%t_b%obj +// RUN: %ldc -I%S/inputs %s %t_b%obj -of=%t%exe +// RUN: %t%exe | FileCheck %s + +import lambdas_dmd23722b; + +// do_y should call A.y (and print "y") +void do_y() { + A.y(); +} + +void main() { + // CHECK: y + do_y(); // should print y + // CHECK-NEXT: x + do_x(); // should print x +} diff --git a/tests/codegen/lambdas_gh3648.d b/tests/codegen/lambdas_gh3648.d index 32ea4b2f7ce..e50a69e95ef 100644 --- a/tests/codegen/lambdas_gh3648.d +++ b/tests/codegen/lambdas_gh3648.d @@ -1,4 +1,4 @@ -// Tests that lambdas and contained globals are emitted as linkonce_odr. +// Tests that lambdas and contained globals are emitted with internal linkage. // RUN: %ldc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll @@ -25,10 +25,10 @@ void foo() }(123); } -// the global variables should be defined as linkonce_odr: -// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = linkonce_odr {{(hidden )?}}thread_local global -// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = linkonce_odr {{(hidden )?}}global -// CHECK: _D14lambdas_gh36483fooFZ__T9__lambda1TiZQnFiZ12lambda_templi{{.*}} = linkonce_odr {{(hidden )?}}global +// the global variables should be defined as internal: +// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = internal thread_local global +// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = internal global +// CHECK: _D14lambdas_gh36483fooFZ__T9__lambda1TiZQnFiZ12lambda_templi{{.*}} = internal global // foo() should only call two lambdas: // CHECK: define {{.*}}_D14lambdas_gh36483fooFZv @@ -36,11 +36,11 @@ void foo() // CHECK-NEXT: call {{.*}}__T9__lambda1 // CHECK-NEXT: ret void -// bar() should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__lambda5 +// bar() should be defined as internal: +// CHECK: define internal {{.*}}__lambda5 // bar_inlined() should NOT have made it to the .ll: // CHECK-NOT: define {{.*}}__lambda6 -// the template lambda instance should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__T9__lambda1 +// the template lambda instance should be defined as internal: +// CHECK: define internal {{.*}}__T9__lambda1 diff --git a/tests/codegen/lambdas_gh3648b.d b/tests/codegen/lambdas_gh3648b.d index 226a2042044..830bc535d04 100644 --- a/tests/codegen/lambdas_gh3648b.d +++ b/tests/codegen/lambdas_gh3648b.d @@ -1,4 +1,4 @@ -// Tests that *imported* lambdas and contained globals are emitted as linkonce_odr. +// Tests that *imported* lambdas and contained globals are emitted with internal linkage. // RUN: %ldc -output-ll -of=%t.ll %s -I%S && FileCheck %s < %t.ll @@ -10,17 +10,17 @@ void foo() bar_inlined(); } -// the global variables should be defined as linkonce_odr: -// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = linkonce_odr {{(hidden )?}}thread_local global -// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = linkonce_odr {{(hidden )?}}global +// the global variables should be defined as internal: +// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = internal thread_local global +// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = internal global // foo() should only call one lambda: // CHECK: define {{.*}}_D15lambdas_gh3648b3fooFZv // CHECK-NEXT: call {{.*}}__lambda5 // CHECK-NEXT: ret void -// bar() should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__lambda5 +// bar() should be defined as internal: +// CHECK: define internal {{.*}}__lambda5 // bar_inlined() should NOT have made it to the .ll: // CHECK-NOT: define {{.*}}__lambda6 From 04bcdc4d06fa6d921181b71f63a6e98a93ee66ed Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 7 Jun 2023 11:58:03 +0200 Subject: [PATCH 184/301] Set up MSVC environment for C preprocessing on Windows --- CHANGELOG.md | 2 +- dmd/vsoptions.d | 42 +++++++++++++++++++++++ dmd/vsoptions.h | 6 ++++ driver/cpreprocessor.cpp | 5 ++- driver/tool.cpp | 72 ++++++++++++++++++++++++++++------------ driver/tool.h | 2 +- 6 files changed, 103 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7939691fe44..286d441e053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - - Windows: Requires an MSVC environment set up for the target (`cl.exe` on `PATH`, `INCLUDE` environment variable). If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). + - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). #### Platform support diff --git a/dmd/vsoptions.d b/dmd/vsoptions.d index 2a363ceeed9..afb235ffa48 100644 --- a/dmd/vsoptions.d +++ b/dmd/vsoptions.d @@ -486,6 +486,21 @@ public: return FileName.exists(proposed) ? proposed : null; } +version (IN_LLVM) +{ + const(char)* getVCIncludeDir() const + { + const(char)* proposed; + + if (VCToolsInstallDir !is null) + proposed = FileName.combine(VCToolsInstallDir, "include"); + else if (VCInstallDir !is null) + proposed = FileName.combine(VCInstallDir, "include"); + + return FileName.exists(proposed) ? proposed : null; + } +} + /** * get the path to the universal CRT libraries * Params: @@ -546,6 +561,33 @@ version (IN_LLVM) {} else return null; } +version (IN_LLVM) +{ + const(char)* getSDKIncludePath() const + { + if (WindowsSdkDir) + { + alias umExists = returnDirIfContainsFile!"um"; // subdir in this case + + const sdk = FileName.combine(WindowsSdkDir.toDString, "include"); + if (WindowsSdkVersion) + { + if (auto p = umExists(sdk, WindowsSdkVersion.toDString)) // SDK 10.0 + return p; + } + // purely speculative + if (auto p = umExists(sdk, "win8")) // SDK 8.0 + return p; + if (auto p = umExists(sdk, "winv6.3")) // SDK 8.1 + return p; + if (auto p = umExists(sdk)) // SDK 7.1 or earlier + return p; + } + + return null; + } +} + private: extern(D): diff --git a/dmd/vsoptions.h b/dmd/vsoptions.h index fc63e1c1b9b..b39601bf92c 100644 --- a/dmd/vsoptions.h +++ b/dmd/vsoptions.h @@ -25,8 +25,14 @@ struct VSOptions void initialize(); const char *getVCBinDir(bool x64, const char *&addpath) const; const char *getVCLibDir(bool x64) const; +#if IN_LLVM + const char *getVCIncludeDir() const; +#endif const char *getUCRTLibPath(bool x64) const; const char *getSDKLibPath(bool x64) const; +#if IN_LLVM + const char *getSDKIncludePath() const; +#endif }; #endif // _WIN32 diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index 0e9ce48e24a..b603a4b22c5 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -73,11 +73,10 @@ FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, const auto &triple = *global.params.targetTriple; const bool isMSVC = triple.isWindowsMSVCEnvironment(); -#if 0 //ifdef _WIN32 - // TODO: INCLUDE env var etc.? +#ifdef _WIN32 windows::MsvcEnvironmentScope msvcEnv; if (isMSVC) - msvcEnv.setup(); + msvcEnv.setup(/*forPreprocessingOnly=*/true); #endif FileName ipath = getOutputPath(loc, csrcfile.toChars()); diff --git a/driver/tool.cpp b/driver/tool.cpp index d3f4cd150d7..c6e43b13ab1 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -265,7 +265,10 @@ int executeToolAndWait(const Loc &loc, const std::string &tool_, namespace windows { namespace { +VSOptions vsOptions; // cache, as this can be expensive + bool setupMsvcEnvironmentImpl( + bool forPreprocessingOnly, std::vector> *rollback) { const bool x64 = global.params.targetTriple->isArch64Bit(); @@ -280,19 +283,13 @@ bool setupMsvcEnvironmentImpl( const auto begin = std::chrono::steady_clock::now(); - VSOptions vsOptions; - vsOptions.initialize(); - if (!vsOptions.VSInstallDir) - return false; - - llvm::SmallVector libPaths; - if (auto vclibdir = vsOptions.getVCLibDir(x64)) - libPaths.push_back(vclibdir); - if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64)) - libPaths.push_back(ucrtlibdir); - if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) - libPaths.push_back(sdklibdir); + if (!vsOptions.VSInstallDir) { + vsOptions.initialize(); + if (!vsOptions.VSInstallDir) + return false; + } + // PATH llvm::SmallVector binPaths; const char *secondaryBindir = nullptr; if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { @@ -300,20 +297,52 @@ bool setupMsvcEnvironmentImpl( if (secondaryBindir) binPaths.push_back(secondaryBindir); } - - const bool success = libPaths.size() == 3 && !binPaths.empty(); - if (!success) + if (binPaths.empty()) return false; + llvm::SmallVector includePaths; + llvm::SmallVector libPaths; + if (forPreprocessingOnly) { + // INCLUDE + if (auto vcincludedir = vsOptions.getVCIncludeDir()) { + includePaths.push_back(vcincludedir); + } else { + return false; + } + if (auto sdkincludedir = vsOptions.getSDKIncludePath()) { + includePaths.push_back(FileName::combine(sdkincludedir, "ucrt")); + includePaths.push_back(FileName::combine(sdkincludedir, "shared")); + includePaths.push_back(FileName::combine(sdkincludedir, "um")); + includePaths.push_back(FileName::combine(sdkincludedir, "winrt")); + includePaths.push_back(FileName::combine(sdkincludedir, "cppwinrt")); + } else { + return false; + } + } else { + // LIB + if (auto vclibdir = vsOptions.getVCLibDir(x64)) + libPaths.push_back(vclibdir); + if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64)) + libPaths.push_back(ucrtlibdir); + if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) + libPaths.push_back(sdklibdir); + + if (libPaths.size() != 3) + return false; + } + if (!rollback) // check for availability only return true; if (global.params.verbose) message("Prepending to environment variables:"); - const auto preprendToEnvVar = + const auto prependToEnvVar = [rollback](const char *key, const wchar_t *wkey, const llvm::SmallVectorImpl &entries) { + if (entries.empty()) + return; + wchar_t *originalValue = _wgetenv(wkey); llvm::SmallString<256> head; @@ -343,8 +372,9 @@ bool setupMsvcEnvironmentImpl( }; rollback->reserve(2); - preprendToEnvVar("LIB", L"LIB", libPaths); - preprendToEnvVar("PATH", L"PATH", binPaths); + prependToEnvVar("INCLUDE", L"INCLUDE", includePaths); + prependToEnvVar("LIB", L"LIB", libPaths); + prependToEnvVar("PATH", L"PATH", binPaths); if (global.params.verbose) { const auto end = std::chrono::steady_clock::now(); @@ -357,11 +387,11 @@ bool setupMsvcEnvironmentImpl( } } // anonymous namespace -bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(nullptr); } +bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(false, nullptr); } -bool MsvcEnvironmentScope::setup() { +bool MsvcEnvironmentScope::setup(bool forPreprocessingOnly) { rollback.clear(); - return setupMsvcEnvironmentImpl(&rollback); + return setupMsvcEnvironmentImpl(forPreprocessingOnly, &rollback); } MsvcEnvironmentScope::~MsvcEnvironmentScope() { diff --git a/driver/tool.h b/driver/tool.h index 21457dd0648..c6f0cc35a17 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -53,7 +53,7 @@ struct MsvcEnvironmentScope { // Tries to set up the MSVC environment variables for the current process and // returns true if successful. The original environment is restored on // destruction. - bool setup(); + bool setup(bool forPreprocessingOnly = false); ~MsvcEnvironmentScope(); From 7a625251a6c41620dac83e066b6de23ad5cb63eb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 9 Jun 2023 16:14:23 +0200 Subject: [PATCH 185/301] Trace preprocessing times --- driver/cpreprocessor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index b603a4b22c5..25947fd1266 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -2,6 +2,7 @@ #include "dmd/errors.h" #include "driver/cl_options.h" +#include "driver/timetrace.h" #include "driver/tool.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -68,6 +69,8 @@ FileName getOutputPath(const Loc &loc, const char *csrcfile) { FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, OutBuffer &defines) { + TimeTraceScope timeScope("Preprocess C file", csrcfile.toChars()); + const char *importc_h = getPathToImportc_h(loc); const auto &triple = *global.params.targetTriple; From 483b23595abe91a752ce0c53fc93a981cba5353b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 9 Jun 2023 20:27:39 +0200 Subject: [PATCH 186/301] Windows: Cache some more MSVC environment stuff --- driver/tool.cpp | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/driver/tool.cpp b/driver/tool.cpp index c6e43b13ab1..806e365996e 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -265,8 +265,6 @@ int executeToolAndWait(const Loc &loc, const std::string &tool_, namespace windows { namespace { -VSOptions vsOptions; // cache, as this can be expensive - bool setupMsvcEnvironmentImpl( bool forPreprocessingOnly, std::vector> *rollback) { @@ -283,26 +281,31 @@ bool setupMsvcEnvironmentImpl( const auto begin = std::chrono::steady_clock::now(); + static VSOptions vsOptions; // cache, as this can be expensive if (!vsOptions.VSInstallDir) { vsOptions.initialize(); if (!vsOptions.VSInstallDir) return false; } - // PATH - llvm::SmallVector binPaths; - const char *secondaryBindir = nullptr; - if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { - binPaths.push_back(bindir); - if (secondaryBindir) - binPaths.push_back(secondaryBindir); + // cache the environment variable prefixes too + static llvm::SmallVector binPaths; + static llvm::SmallVector includePaths; + static llvm::SmallVector libPaths; + + if (binPaths.empty()) { + // PATH + const char *secondaryBindir = nullptr; + if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { + binPaths.push_back(bindir); + if (secondaryBindir) + binPaths.push_back(secondaryBindir); + } else { + return false; + } } - if (binPaths.empty()) - return false; - llvm::SmallVector includePaths; - llvm::SmallVector libPaths; - if (forPreprocessingOnly) { + if (forPreprocessingOnly && includePaths.empty()) { // INCLUDE if (auto vcincludedir = vsOptions.getVCIncludeDir()) { includePaths.push_back(vcincludedir); @@ -316,9 +319,12 @@ bool setupMsvcEnvironmentImpl( includePaths.push_back(FileName::combine(sdkincludedir, "winrt")); includePaths.push_back(FileName::combine(sdkincludedir, "cppwinrt")); } else { + includePaths.clear(); return false; } - } else { + } + + if (!forPreprocessingOnly && libPaths.empty()) { // LIB if (auto vclibdir = vsOptions.getVCLibDir(x64)) libPaths.push_back(vclibdir); @@ -327,8 +333,10 @@ bool setupMsvcEnvironmentImpl( if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) libPaths.push_back(sdklibdir); - if (libPaths.size() != 3) + if (libPaths.size() != 3) { + libPaths.clear(); return false; + } } if (!rollback) // check for availability only From 81cf699b4c3d1673f3422e30bbe331adbe30aba9 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 9 Jun 2023 20:30:12 +0200 Subject: [PATCH 187/301] Prefer lazy imports for LDC-specific parts in __builtins.di --- runtime/druntime/src/__builtins.di | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/runtime/druntime/src/__builtins.di b/runtime/druntime/src/__builtins.di index 94d0a8c7e01..81c004da7a5 100644 --- a/runtime/druntime/src/__builtins.di +++ b/runtime/druntime/src/__builtins.di @@ -135,8 +135,6 @@ version (DigitalMars) } else version (LDC) { - import ldc.intrinsics; - double __builtin_inf()() { return double.infinity; } float __builtin_inff()() { return float.infinity; } real __builtin_infl()() { return real.infinity; } @@ -145,19 +143,18 @@ else version (LDC) alias __builtin_huge_valf = __builtin_inff; alias __builtin_huge_vall = __builtin_infl; - alias __builtin_fabs = llvm_fabs!double; - alias __builtin_fabsf = llvm_fabs!float; - alias __builtin_fabsl = llvm_fabs!real; + alias __builtin_fabs = imported!"ldc.intrinsics".llvm_fabs!double; + alias __builtin_fabsf = imported!"ldc.intrinsics".llvm_fabs!float; + alias __builtin_fabsl = imported!"ldc.intrinsics".llvm_fabs!real; - alias __builtin_bswap16 = llvm_bswap!ushort; - alias __builtin_bswap32 = llvm_bswap!uint; - alias __builtin_bswap64 = llvm_bswap!ulong; + alias __builtin_bswap16 = imported!"ldc.intrinsics".llvm_bswap!ushort; + alias __builtin_bswap32 = imported!"ldc.intrinsics".llvm_bswap!uint; + alias __builtin_bswap64 = imported!"ldc.intrinsics".llvm_bswap!ulong; int __builtin_constant_p(T)(T exp) { return 0; } - alias __builtin_expect = llvm_expect!long; + alias __builtin_expect = imported!"ldc.intrinsics".llvm_expect!long; void* __builtin_assume_aligned()(const void* p, size_t align_, ...) { return cast(void*)p; } void __builtin_assume(T)(lazy T arg) { } - import core.int128 : Cent; - alias __uint128_t = Cent; + alias __uint128_t = imported!"core.int128".Cent; } From 94216ca7e27b1088e3c1436b1e5c2a3e9b8d14c9 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 10 Jun 2023 17:41:00 +0200 Subject: [PATCH 188/301] Lazily IR-declare all function fwd declarations (#4420) Instead of eagerly IR-declaring all such functions in root modules being compiled. This might reduce compile times in some cases, and is a cheap way to improve the situation wrt. issue #2782. --- gen/declarations.cpp | 4 +++- tests/codegen/attr_callingconvention.d | 6 +++--- tests/compilable/gh2782.d | 4 ++++ tests/compilable/inputs/gh2782b.d | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 tests/compilable/gh2782.d create mode 100644 tests/compilable/inputs/gh2782b.d diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 040bb862e58..585a33e7b84 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -284,7 +284,9 @@ class CodegenVisitor : public Visitor { void visit(FuncDeclaration *decl) override { // don't touch function aliases, they don't contribute any new symbols - if (!decl->skipCodegen() && !decl->isFuncAliasDeclaration()) { + if (!decl->skipCodegen() && !decl->isFuncAliasDeclaration() && + // skip fwd declarations (IR-declared lazily) + decl->fbody) { DtoDefineFunction(decl); } } diff --git a/tests/codegen/attr_callingconvention.d b/tests/codegen/attr_callingconvention.d index 84873fdca26..2d127fb8d40 100644 --- a/tests/codegen/attr_callingconvention.d +++ b/tests/codegen/attr_callingconvention.d @@ -59,9 +59,6 @@ void fooinvokesexternCfoofoofoo() ////////////////////////////////////////////////////////// /// Forward-declared function calls and invokes: -// CHECK-LABEL: declare x86_vectorcallcc void @{{.*}}forward_declared_function -@callingConvention("vectorcall") void forward_declared_function(); - // CHECK-LABEL: define{{.*}} @{{.*}}attr_callingconvention34foocalls_forward_declared_function void foocalls_forward_declared_function() { @@ -69,6 +66,9 @@ void foocalls_forward_declared_function() forward_declared_function(); } +// CHECK-LABEL: declare x86_vectorcallcc void @{{.*}}forward_declared_function +@callingConvention("vectorcall") void forward_declared_function(); + // CHECK-LABEL: define{{.*}} @{{.*}}attr_callingconvention36fooinvokes_forward_declared_function void fooinvokes_forward_declared_function() { diff --git a/tests/compilable/gh2782.d b/tests/compilable/gh2782.d new file mode 100644 index 00000000000..a80cecfd394 --- /dev/null +++ b/tests/compilable/gh2782.d @@ -0,0 +1,4 @@ +// RUN: %ldc -c -singleobj %s %S/inputs/gh2782b.d + +struct S {} +extern(C) void foo(S); diff --git a/tests/compilable/inputs/gh2782b.d b/tests/compilable/inputs/gh2782b.d new file mode 100644 index 00000000000..3fea3c80447 --- /dev/null +++ b/tests/compilable/inputs/gh2782b.d @@ -0,0 +1,2 @@ +struct S {} +extern(C) void foo(S); From 83fe601e61bc5b5c8d894c05f74abd77abc796ff Mon Sep 17 00:00:00 2001 From: Dennis Date: Sat, 10 Jun 2023 19:32:52 +0200 Subject: [PATCH 189/301] Fix 23979 - ICE on failed alias this attempt on pointer expression (dlang/dmd!15300) --- dmd/opover.d | 6 ++++-- tests/dmd/compilable/test23979.d | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/compilable/test23979.d diff --git a/dmd/opover.d b/dmd/opover.d index d7b90d7635f..771287691ad 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -416,9 +416,11 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * op(e1.aliasthis) */ //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - e.e1 = resolveAliasThis(sc, e.e1, true); - if (e.e1) + if (auto e1 = resolveAliasThis(sc, e.e1, true)) + { + e.e1 = e1; continue; + } break; } break; diff --git a/tests/dmd/compilable/test23979.d b/tests/dmd/compilable/test23979.d new file mode 100644 index 00000000000..f7eb2c554ee --- /dev/null +++ b/tests/dmd/compilable/test23979.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -o- +// https://issues.dlang.org/show_bug.cgi?id=23979 +// Issue 23979 - ICE on failed alias this attempt on pointer expression + +class A {} + +void h() +{ + const auto classPtr = SPtr.init; + assert(!__traits(compiles, *classPtr)); +} + +struct SPtr +{ + A ptr() { return A.init; } + alias ptr this; +} From 84208add3f2a5d879ef5a44682395e7b68cf5f44 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sat, 10 Jun 2023 20:15:55 +0200 Subject: [PATCH 190/301] Fix 23978 - ICE: EscapeBy[] is malloced, but contains GC-allocated objects (dlang/dmd!15302) --- dmd/escape.d | 24 +----------------------- tests/dmd/compilable/test23978.d | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 tests/dmd/compilable/test23978.d diff --git a/dmd/escape.d b/dmd/escape.d index 4f1edaa4d05..ce6947e5722 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -93,22 +93,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, bool isMutable; // true if reference to mutable } - /* Store escapeBy as static data escapeByStorage so we can keep reusing the same - * arrays rather than reallocating them. - */ - __gshared EscapeBy[] escapeByStorage; - auto escapeBy = escapeByStorage; - if (escapeBy.length < len) - { - auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof); - // Clear the new section - memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof); - escapeBy = newPtr[0 .. len]; - escapeByStorage = escapeBy; - } - else - escapeBy = escapeBy[0 .. len]; - + auto escapeBy = new EscapeBy[len]; const paramLength = tf.parameterList.length; // Fill in escapeBy[] with arguments[], ethis, and outerVars[] @@ -228,13 +213,6 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, escape(i, eb, false); } - /* Reset the arrays in escapeBy[] so we can reuse them next time through - */ - foreach (ref eb; escapeBy) - { - eb.er.reset(); - } - return errors; } diff --git a/tests/dmd/compilable/test23978.d b/tests/dmd/compilable/test23978.d new file mode 100644 index 00000000000..cc30f728dee --- /dev/null +++ b/tests/dmd/compilable/test23978.d @@ -0,0 +1,30 @@ +// REQUIRED_ARGS: -preview=dip1021 -lowmem +// https://issues.dlang.org/show_bug.cgi?id=23978 + +// Note: this is a memory corruption bug. +// Memory returned by `GC.realloc` retains references to old memory in it, +// mostly because of the smallarray optimization for `Array(T)`. +// If this fails again, it might not be consistent, so try running it multiple times. + +class LUBench { } +void lup(ulong , ulong , int , int = 1) +{ + new LUBench; +} +void lup_3200(ulong iters, ulong flops) +{ + lup(iters, flops, 3200); +} +void raytrace() +{ + struct V + { + float x, y, z; + auto normalize() { } + struct Tid { } + auto spawnLinked() { } + string[] namesByTid; + class MessageBox { } + auto cross() { } + } +} From 29bf2d302c28bff314b8b9b8b34dd492b15c9133 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 11 Jun 2023 00:50:48 +0200 Subject: [PATCH 191/301] Add changelog entry for lazy IR-declaration of function fwd declarations (#4422) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 286d441e053..3b6e4e252e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file path to stderr during preprocessing). + - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). +- Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420) #### Platform support From cbaf54dc5f2811011f73a54d05b58cd5293abdca Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Sat, 10 Jun 2023 20:09:53 +0200 Subject: [PATCH 192/301] Fix 23986 - ICE: dip1021 asserts on `typeof(null)` parameter --- dmd/escape.d | 3 +++ tests/dmd/compilable/test23986.d | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/dmd/compilable/test23986.d diff --git a/dmd/escape.d b/dmd/escape.d index ce6947e5722..0ab730637ef 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -2475,6 +2475,9 @@ bool isReferenceToMutable(Type t) } break; + case Tnull: + return false; + default: assert(0); } diff --git a/tests/dmd/compilable/test23986.d b/tests/dmd/compilable/test23986.d new file mode 100644 index 00000000000..6bbf1c2aeaa --- /dev/null +++ b/tests/dmd/compilable/test23986.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: -preview=dip1021 -o- +// https://issues.dlang.org/show_bug.cgi?id=23986 +// dip1021 asserts on `typeof(null)` parameter +@safe: + +void f(typeof(null) obj, int* x) {} + +void g() +{ + f(null, null); +} From 5654d522b316e95edd737687f115c0c94cd2c26b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 11 Jun 2023 16:52:47 +0200 Subject: [PATCH 193/301] [CI: Switch back to Ubuntu 20 and LLVM v16.0.0] --- .github/workflows/supported_llvm_versions.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 1ce366e1b50..ff5d9c2f822 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -15,10 +15,10 @@ jobs: fail-fast: false matrix: include: - - job_name: Ubuntu 22.04, LLVM 16, latest LDC beta - os: ubuntu-22.04 + - job_name: Ubuntu 20.04, LLVM 16, latest LDC beta + os: ubuntu-20.04 host_dc: ldc-beta - llvm_version: 16.0.3 + llvm_version: 16.0.0 - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta @@ -81,12 +81,7 @@ jobs: set -eux sudo apt-get update # Don't use latest gdb v10+ from Ubuntu toolchain PPA with regressions, use official v9 - version='${{ matrix.llvm_version }}' - if [[ "$version" =~ ^1[6-9]\. ]]; then # LLVM 16.0.3+ - sudo apt-get install gdb llvm - else - sudo apt-get install gdb=9.1-0ubuntu1 llvm - fi + sudo apt-get install gdb=9.1-0ubuntu1 llvm - name: Try to restore cached LLVM uses: actions/cache@v2 @@ -104,8 +99,6 @@ jobs: version='${{ matrix.llvm_version }}' if [[ '${{ runner.os }}' == macOS ]]; then suffix='x86_64-apple-darwin' - elif [[ "$version" =~ ^1[6-9]\. ]]; then # LLVM 16.0.3+ - suffix='x86_64-linux-gnu-ubuntu-22.04' # LLVM 13.0.1+ elif [[ "$version" =~ ^1[3-9]\. ]]; then suffix='x86_64-linux-gnu-ubuntu-18.04' # LLVM 13.0.1+ else From 1075fb6abf90a3f23eed59d2f86b02029776771f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 11 Jun 2023 17:07:36 +0200 Subject: [PATCH 194/301] CMake: Adapt to changed compiler-rt libs base dir with LLVM 16 --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0ebfd8d9c5..f3b36080cc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -782,7 +782,11 @@ else() endif() set(LDC_INSTALL_LLVM_RUNTIME_LIBS ${LDC_INSTALL_LLVM_RUNTIME_LIBS_DEFAULT} CACHE BOOL "Copy/install LLVM compiler-rt libraries (ASan, libFuzzer, ...) from LLVM/Clang into LDC lib dir when available.") function(copy_compilerrt_lib llvm_lib_name ldc_lib_name fixup_dylib) - set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_BASE_STRING}/lib/${llvm_lib_name}) + if(LDC_LLVM_VER LESS 1600) + set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_BASE_STRING}/lib/${llvm_lib_name}) + else() + set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_MAJOR}/lib/${llvm_lib_name}) + endif() if(EXISTS ${llvm_lib_path}) message(STATUS "-- - ${llvm_lib_path} --> ${ldc_lib_name}") set(ldc_lib_path ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/${ldc_lib_name}) From 67b73a55f09713244e2212b9ad832e239c740cde Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 11 Jun 2023 17:42:36 +0200 Subject: [PATCH 195/301] [slightly simplify dibuilder.cpp again] --- gen/dibuilder.cpp | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index f7068294dd9..f4a57b63c29 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -44,6 +44,13 @@ namespace cl = llvm::cl; using LLMetadata = llvm::Metadata; +#if LDC_LLVM_VER >= 1600 +namespace llvm { + template using Optional = std::optional; + inline constexpr std::nullopt_t None = std::nullopt; +} +#endif + static cl::opt emitColumnInfo( "gcolumn-info", cl::ZeroOrMore, cl::Hidden, cl::desc("Include column numbers in line debug infos. Defaults to " @@ -372,11 +379,7 @@ DIType DIBuilder::CreateEnumType(TypeEnum *type) { DIType DIBuilder::CreatePointerType(TypePointer *type) { // TODO: The addressspace is important for dcompute targets. See e.g. // https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00326.html -#if LDC_LLVM_VER < 1600 const llvm::Optional DWARFAddressSpace = llvm::None; -#else - const std::optional DWARFAddressSpace = std::nullopt; -#endif const auto name = processDIName(type->toPrettyChars(true)); @@ -734,11 +737,7 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type) { } DISubroutineType DIBuilder::CreateEmptyFunctionType() { -#if LDC_LLVM_VER < 1600 auto paramsArray = DBuilder.getOrCreateTypeArray(llvm::None); -#else - auto paramsArray = DBuilder.getOrCreateTypeArray(std::nullopt); -#endif return DBuilder.createSubroutineType(paramsArray); } @@ -782,16 +781,9 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { return nullptr; if (t->ty == TY::Tnull) { // display null as void* -#if LDC_LLVM_VER < 1600 return DBuilder.createPointerType( CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, /* DWARFAddressSpace */ llvm::None, "typeof(null)"); -#else - return DBuilder.createPointerType( - CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, - /* DWARFAddressSpace */ std::nullopt, "typeof(null)"); -#endif - } if (auto te = t->isTypeEnum()) return CreateEnumType(te); @@ -813,14 +805,8 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { const auto aggregateDIType = CreateCompositeType(t); const auto name = (tc->sym->toPrettyChars(true) + llvm::StringRef("*")).str(); -#if LDC_LLVM_VER < 1600 return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, llvm::None, processDIName(name)); -#else - return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, - std::nullopt, processDIName(name)); -#endif - } if (auto tf = t->isTypeFunction()) return CreateFunctionType(tf); From 26f08df3638260f93b4ccd0a08ef511e8c3d5828 Mon Sep 17 00:00:00 2001 From: Teodor Dutu Date: Tue, 13 Jun 2023 13:10:47 +0300 Subject: [PATCH 196/301] Move lowerings to `_d_array{setassign,assign_{l,r}}` to a `LoweredAssignExp` AST node (dlang/dmd!15295) Move lowerings to `_d_array{setassign,assign_{l,r}}` to a `LoweredAssignExp` AST node Signed-off-by: Razvan Nitu Merged-on-behalf-of: unknown --- dmd/canthrow.d | 2 +- dmd/dinterpret.d | 26 +++++++++++--------------- dmd/expression.d | 13 +++++-------- dmd/expressionsem.d | 3 +++ tests/dmd/runnable/test23959.d | 30 ++++++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 tests/dmd/runnable/test23959.d diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 7dfec8a043a..e0e473d6ff0 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -118,7 +118,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { auto sd = ts.sym; const id = ce.f.ident; - if (sd.postblit && isArrayConstructionOrAssign(id)) + if (sd.postblit && isArrayConstruction(id)) { checkFuncThrows(ce, sd.postblit); return; diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index 4ef6a392073..70d012b14e8 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -3891,7 +3891,7 @@ public: newval = copyLiteral(newval).copy(); assignInPlace(oldval, newval); } - else if (wantCopy && e.op == EXP.assign) + else if (wantCopy && (e.op == EXP.assign || e.op == EXP.loweredAssignExp)) { // Currently postblit/destructor calls on static array are done // in the druntime internal functions so they don't appear in AST. @@ -4300,7 +4300,7 @@ public: rb.newval = newval; rb.refCopy = wantRef || cow; rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue(); - rb.needsDtor = sd && sd.dtor && e.op == EXP.assign; + rb.needsDtor = sd && sd.dtor && (e.op == EXP.assign || e.op == EXP.loweredAssignExp); if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound)) return ex; @@ -4774,12 +4774,11 @@ public: result = CTFEExp.voidexp; return; } - else if (isArrayConstructionOrAssign(fd.ident)) + else if (isArrayConstruction(fd.ident)) { - // In expressionsem.d, the following lowerings were performed: - // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`. - // * `ea = eb` to `_d_array{,setassign,assign_l,assign_r}(ea[], eb)`. - // The following code will rewrite them back to `ea = eb` and + // In expressionsem.d, `T[x] ea = eb;` was lowered to: + // `_d_array{,set}ctor(ea[], eb[]);`. + // The following code will rewrite it back to `ea = eb` and // then interpret that expression. if (fd.ident == Id._d_arrayctor) @@ -4792,17 +4791,14 @@ public: ea = ea.isCastExp.e1; Expression eb = (*e.arguments)[1]; - if (eb.isCastExp() && fd.ident != Id._d_arraysetctor) + if (eb.isCastExp() && fd.ident == Id._d_arrayctor) eb = eb.isCastExp.e1; - Expression rewrittenExp; - if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor) - rewrittenExp = new ConstructExp(e.loc, ea, eb); - else - rewrittenExp = new AssignExp(e.loc, ea, eb); + ConstructExp ce = new ConstructExp(e.loc, ea, eb); + ce.type = ea.type; - rewrittenExp.type = ea.type; - result = interpret(rewrittenExp, istate); + ce.type = ea.type; + result = interpret(ce, istate); return; } diff --git a/dmd/expression.d b/dmd/expression.d index 067d22fe130..c30023cb3d5 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -7390,23 +7390,20 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag } /** - * Verify if the given identifier is any of - * _d_array{ctor,setctor,setassign,assign_l, assign_r}. + * Verify if the given identifier is _d_array{,set}ctor. * * Params: * id = the identifier to verify * * Returns: - * `true` if the identifier corresponds to a construction of assignement - * runtime hook, `false` otherwise. + * `true` if the identifier corresponds to a construction runtime hook, + * `false` otherwise. */ -bool isArrayConstructionOrAssign(const Identifier id) +bool isArrayConstruction(const Identifier id) { import dmd.id : Id; - return id == Id._d_arrayctor || id == Id._d_arraysetctor || - id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || - id == Id._d_arraysetassign; + return id == Id._d_arrayctor || id == Id._d_arraysetctor; } /****************************** diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index bdad3a27262..3bcf3f66211 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -10308,6 +10308,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (global.params.verbose) message("lowered %s =>\n %s", ae.toChars(), res.toChars()); + res = new LoweredAssignExp(ae, res); + res.type = ae.type; + return res; } diff --git a/tests/dmd/runnable/test23959.d b/tests/dmd/runnable/test23959.d new file mode 100644 index 00000000000..32883736d44 --- /dev/null +++ b/tests/dmd/runnable/test23959.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=23959; + +struct ST() +{ + int i; + this(this) {} +} + +alias S = ST!(); + +void poison() +{ + static S g; + auto s = g; +} + +S[1] sa; + +void fun(S[] values...) +{ + sa[] = values; +} + +int main() +{ + fun(S(1)); + assert(sa[0].i); + + return 0; +} From fb2f281ad0713c466c0baacfaa689f3a2623a68f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 14 Jun 2023 07:42:57 +0200 Subject: [PATCH 197/301] [stable] Fix compilable/stdcheaders.c for Linux AArch64 (dlang/dmd!15320) * Linux AArch64: Don't define _Float128/__float128 in importc.h For LDC CI, on Ubuntu 20.04, this fixed: ``` /usr/include/aarch64-linux-gnu/bits/floatn.h(80): Error: illegal combination of type specifiers /usr/include/aarch64-linux-gnu/bits/floatn.h(80): Error: illegal type combination ``` when testing `compilable/stdcheaders.c`. * Linux AArch64: Don't include tgmath.h in compilable/stdcheaders.c --- runtime/druntime/src/importc.h | 2 ++ tests/dmd/compilable/stdcheaders.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 66c4eb7aa8c..0b2f4c6958c 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -137,9 +137,11 @@ // Ubuntu's assert.h uses this #define __PRETTY_FUNCTION__ __func__ +#ifndef __aarch64__ #define _Float128 long double #define __float128 long double #endif +#endif #if __APPLE__ #undef __SIZEOF_INT128__ diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index ea193c36e40..d89bf72fff6 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -65,10 +65,12 @@ #ifndef __DMC__ // no tgmath.h #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` +#if !(defined(__linux__) && defined(__aarch64__)) // /tmp/clang/lib/clang/15.0.3/include/tgmath.h(34): Error: named parameter required before `...` #include #endif #endif #endif +#endif #ifndef __DMC__ #ifndef __linux__ From 3e2156fa68b2ad2e58286c842f1cba71aff0460b Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 16 Jun 2023 00:26:07 +0200 Subject: [PATCH 198/301] Don't output files on LLVM errors. (#4425) Fix issue where an LLVM error or warning during IR passes (e.g. --fwarn-stack-size) does not abort outputting files. The object file would be added to the cache, and subsequent repeated LDC execution would fetch the object file from the cache, effectively swallowing the error. --- driver/codegenerator.cpp | 13 ++++++-- driver/toobj.cpp | 55 +++++++++++++++++++++++++++------ tests/driver/fwarn-stack-size.d | 6 ++++ 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index e277d00f75e..e15cfe22822 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -377,15 +377,24 @@ void CodeGenerator::writeMLIRModule(mlir::OwningModuleRef *module, const auto llpath = replaceExtensionWith(mlir_ext, filename); Logger::println("Writting MLIR to %s\n", llpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream aos(llpath, errinfo, llvm::sys::fs::OF_None); + llvm::ToolOutputFile aos(llpath, errinfo, llvm::sys::fs::OF_None); - if (aos.has_error()) { + if (aos.os().has_error()) { error(Loc(), "Cannot write MLIR file '%s': %s", llpath.c_str(), errinfo.message().c_str()); fatal(); } // module->print(aos); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println( + "Aborting because of errors/warnings during bitcode LLVM passes"); + fatal(); + } + + aos.keep(); } } diff --git a/driver/toobj.cpp b/driver/toobj.cpp index c03ddda0112..b25a8773cce 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Program.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -93,7 +94,7 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, } std::error_code errinfo; - llvm::raw_fd_ostream out(filename, errinfo, llvm::sys::fs::OF_None); + llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); if (errinfo) { error(Loc(), "cannot write file '%s': %s", filename, errinfo.message().c_str()); @@ -119,8 +120,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, if (Target.addPassesToEmitFile( Passes, - out, // Output file - nullptr, // DWO output file + out.os(), // Output file + nullptr, // DWO output file // Always generate assembly for ptx as it is an assembly format // The PTX backend fails if we pass anything else. (cb == ComputeBackend::NVPTX) ? CGFT_AssemblyFile : fileType, @@ -129,6 +130,14 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, } Passes.run(m); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } + + out.keep(); } } @@ -349,6 +358,15 @@ void writeModule(llvm::Module *m, const char *filename) { runDLLImportRelocationPass(*gTargetMachine, *m); } + // Check if there are any errors before writing files. + // Note: LLVM passes can add new warnings/errors (warnings become errors with + // `-w`) such that we reach here with errors that did not trigger earlier + // termination of the compiler. + if (global.errors) { + Logger::println("Aborting because of errors"); + fatal(); + } + // Everything beyond this point is writing file(s) to disk. ::TimeTraceScope timeScope("Write file(s)", filename); @@ -371,8 +389,8 @@ void writeModule(llvm::Module *m, const char *filename) { : replaceExtensionWith(bc_ext, filename); Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::OF_None); - if (bos.has_error()) { + llvm::ToolOutputFile bos(bcpath.c_str(), errinfo, llvm::sys::fs::OF_None); + if (bos.os().has_error()) { error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(), errinfo.message().c_str()); fatal(); @@ -390,11 +408,20 @@ void writeModule(llvm::Module *m, const char *filename) { auto moduleSummaryIndex = buildModuleSummaryIndex( *m, /* function freq callback */ nullptr, &PSI); - llvm::WriteBitcodeToFile(M, bos, true, &moduleSummaryIndex, + llvm::WriteBitcodeToFile(M, bos.os(), true, &moduleSummaryIndex, /* generate ThinLTO hash */ true); } else { - llvm::WriteBitcodeToFile(M, bos); + llvm::WriteBitcodeToFile(M, bos.os()); } + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println( + "Aborting because of errors/warnings during bitcode LLVM passes"); + fatal(); + } + + bos.keep(); } // write LLVM IR @@ -402,14 +429,22 @@ void writeModule(llvm::Module *m, const char *filename) { const auto llpath = replaceExtensionWith(ll_ext, filename); Logger::println("Writing LLVM IR to: %s\n", llpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream aos(llpath.c_str(), errinfo, llvm::sys::fs::OF_None); - if (aos.has_error()) { + llvm::ToolOutputFile aos(llpath.c_str(), errinfo, llvm::sys::fs::OF_None); + if (aos.os().has_error()) { error(Loc(), "cannot write LLVM IR file '%s': %s", llpath.c_str(), errinfo.message().c_str()); fatal(); } AssemblyAnnotator annotator(m->getDataLayout()); - m->print(aos, &annotator); + m->print(aos.os(), &annotator); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } + + aos.keep(); } const bool writeObj = outputObj && !emitBitcodeAsObjectFile; diff --git a/tests/driver/fwarn-stack-size.d b/tests/driver/fwarn-stack-size.d index 1ed5184faff..9b7e4fdb4ef 100644 --- a/tests/driver/fwarn-stack-size.d +++ b/tests/driver/fwarn-stack-size.d @@ -7,6 +7,12 @@ // RUN: not %ldc -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// Test that IR caching does not hide the warning-error in a second compilation run +// RUN: not %ldc -cache=%t-dir -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// RUN: not %ldc -cache=%t-dir -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// Test that indeed the IR cache does not exist +// RUN: not %prunecache -f %t-dir --max-bytes=1 + module fwarnstacksize; void small_stack() From 3eba39eda10ce7268b0e308f22b78449d257bafe Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 18 Jun 2023 12:24:40 +0200 Subject: [PATCH 199/301] Fix 23982 - segfault when printing scope inference failure (dlang/dmd!15329) --- dmd/escape.d | 2 +- tests/dmd/fail_compilation/test23982.d | 36 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/test23982.d diff --git a/dmd/escape.d b/dmd/escape.d index 0ab730637ef..5704ff92b3d 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -481,7 +481,7 @@ bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Pa const byRef = param.isReference() && !(param.storageClass & STC.scope_) && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef - scope e = new AssignExp(arg.loc, firstArg, arg); + auto e = new AssignExp(arg.loc, firstArg, arg); return checkAssignEscape(sc, e, gag, byRef); } diff --git a/tests/dmd/fail_compilation/test23982.d b/tests/dmd/fail_compilation/test23982.d new file mode 100644 index 00000000000..f8eee238ed8 --- /dev/null +++ b/tests/dmd/fail_compilation/test23982.d @@ -0,0 +1,36 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test23982.d(35): Error: scope variable `a` assigned to non-scope parameter `a` calling `foo2` +fail_compilation/test23982.d(26): which is not `scope` because of `b = a` +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=23982 +// Issue 23982 - segfault when printing scope inference failure +@safe: + +struct B() +{ + this(int* a) + { + this.a = a; + } + int* a; +} + +class C() +{ + int* foo2(int* a) + { + auto b = B!()(a); + return b.a; + } +} + +void main() +{ + scope int* a; + C!() c; + c.foo2(a); +} From e81136f222525d03c726ee18bea3ef363a3a73d7 Mon Sep 17 00:00:00 2001 From: Teodor Dutu Date: Mon, 19 Jun 2023 12:57:19 +0300 Subject: [PATCH 200/301] Fix Issue 23965 - Disable deprecation checks for DRuntime hooks (dlang/dmd!15315) Lowerings to runtime hooks can be made from deprecated contexts, which results in incorrect deprecation messages of the deprecated context calling an undeprecated hook. Since hooks aren't going to be deprecated, this check may be disabled. Signed-off-by: Teodor Dutu --- dmd/dsymbolsem.d | 37 ++++++++++++++++++++++++++++++++ tests/dmd/compilable/test23965.d | 11 ++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/dmd/compilable/test23965.d diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 7333c656b8f..7bc0dea0bc0 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -5892,6 +5892,31 @@ void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) }); } +/****************************************************** + * Verifies if the given Identifier is a DRuntime hook. It uses the hooks + * defined in `id.d`. + * + * Params: + * id = Identifier to verify + * Returns: + * true if `id` is a DRuntime hook + * false otherwise + */ +private bool isDRuntimeHook(Identifier id) +{ + return id == Id._d_HookTraceImpl || + id == Id._d_newclassT || id == Id._d_newclassTTrace || + id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace || + id == Id._d_newThrowable || id == Id._d_delThrowable || + id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || + id == Id._d_arraysetassign || id == Id._d_arraysetctor || + id == Id._d_arrayctor || + id == Id._d_arraysetlengthTImpl || id == Id._d_arraysetlengthT || + id == Id._d_arraysetlengthTTrace || + id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace || + id == Id._d_arrayappendcTXImpl; +} + void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList) { //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc); @@ -6371,8 +6396,20 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList tempinst.deferred = &deferred; //printf("Run semantic3 on %s\n", toChars()); + + /* https://issues.dlang.org/show_bug.cgi?id=23965 + * DRuntime hooks are not deprecated, but may be used for deprecated + * types. Deprecations are disabled while analysing hooks to avoid + * spurious error messages. + */ + auto saveUseDeprecated = global.params.useDeprecated; + if (sc.isDeprecated() && isDRuntimeHook(tempinst.name)) + global.params.useDeprecated = DiagnosticReporting.off; + tempinst.trySemantic3(sc2); + global.params.useDeprecated = saveUseDeprecated; + for (size_t i = 0; i < deferred.length; i++) { //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars()); diff --git a/tests/dmd/compilable/test23965.d b/tests/dmd/compilable/test23965.d new file mode 100644 index 00000000000..36e4e428a85 --- /dev/null +++ b/tests/dmd/compilable/test23965.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23965 +// REQUIRED_ARGS: -de +deprecated: + +struct S {} + +void fun() +{ + S[] arr; + arr ~= S(); +} From 3a5c53a05e0239ea5b4f103ac2835634c6ee3971 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 21 Jun 2023 13:36:09 +0300 Subject: [PATCH 201/301] Fix Issue 22729 - ICE: Invalid array access for invalid interface definition (dlang/dmd!15337) --- dmd/semantic2.d | 7 +++++ tests/dmd/fail_compilation/fail22729.d | 39 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/dmd/fail_compilation/fail22729.d diff --git a/dmd/semantic2.d b/dmd/semantic2.d index 440e4cbc8e7..5f340eddc8c 100644 --- a/dmd/semantic2.d +++ b/dmd/semantic2.d @@ -659,6 +659,13 @@ private extern(C++) final class Semantic2Visitor : Visitor { foreach (base; cd.interfaces) { + // https://issues.dlang.org/show_bug.cgi?id=22729 + // interfaces that have errors or that + // inherit from interfaces that have errors + // might have an uninitialized vtable + if (!base.sym.vtbl.length) + continue; + // first entry is ClassInfo reference auto methods = base.sym.vtbl[base.sym.vtblOffset .. $]; diff --git a/tests/dmd/fail_compilation/fail22729.d b/tests/dmd/fail_compilation/fail22729.d new file mode 100644 index 00000000000..38bbfeeed2b --- /dev/null +++ b/tests/dmd/fail_compilation/fail22729.d @@ -0,0 +1,39 @@ +// https://issues.dlang.org/show_bug.cgi?id=22729 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail22729.d(12): Error: field `getChildAtPosition` not allowed in interface +--- +*/ + +interface ContainerFunctionSetI +{ + Tuple!(WidgetI) getChildAtPosition; +} + +interface WidgetI : ContainerFunctionSetI +{ +} + +class Form : WidgetI +{ +} + +template Tuple(Specs) +{ + enum areCompatibleTuples(Tup2)(Tuple tup1, Tup2 tup2) + { + tup1.field == tup2; + } + + struct Tuple + { + Specs field; + + bool opEquals(R)(R) if (areCompatibleTuples!R) + { + } + + } +} From 89a0b7a74285bf183febf1ce51883234707560c1 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 21 Jun 2023 17:01:26 +0300 Subject: [PATCH 202/301] Fix Issue 21025 - Segfault for function contract -preview=dip1021 (dlang/dmd!15338) --- dmd/escape.d | 2 +- tests/dmd/fail_compilation/test21025.d | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/test21025.d diff --git a/dmd/escape.d b/dmd/escape.d index 5704ff92b3d..b4b0fd92472 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -166,7 +166,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe())) + if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) return; if (!gag) diff --git a/tests/dmd/fail_compilation/test21025.d b/tests/dmd/fail_compilation/test21025.d new file mode 100644 index 00000000000..40b3a96823d --- /dev/null +++ b/tests/dmd/fail_compilation/test21025.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=21025 +// REQUIRED_ARGS: -preview=dip1021 + +/* +TEST_OUTPUT: +--- +fail_compilation/test21025.d(15): Error: variable `r` cannot be read at compile time +fail_compilation/test21025.d(15): called from here: `binaryFun(r, r)` +fail_compilation/test21025.d(24): Error: none of the overloads of template `test21025.uniq` are callable using argument types `!()(void[])` +fail_compilation/test21025.d(14): Candidate is: `uniq()(int[] r)` +--- +*/ + +void uniq()(int[] r) +if (binaryFun(r, r)) {} + +bool binaryFun(T, U)(T, U) +{ + return true; +} + +void generateStatements() +{ + uniq([]); +} From d4f2bed3a45688b32434d429e39c22dea5a9ffda Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 23 Jun 2023 00:10:26 +0200 Subject: [PATCH 203/301] Remove non-existing fwd decls from llvmhelpers.h (#4428) --- gen/llvmhelpers.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index 13ee25d3108..7dbacbefda6 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -294,6 +294,3 @@ DValue *makeVarDValue(Type *type, VarDeclaration *vd, bool toInPlaceConstruction(DLValue *lhs, Expression *rhs); std::string llvmTypeToString(LLType *type); - -void emitLifetimeStart(llvm::Value *size, llvm::Value *addr); -void emitLifetimeEnd(llvm::Value *size, llvm::Value *addr); From 4045835ae1353d4c6419f66bc0c682ef5cb0fbb7 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 25 Jun 2023 12:59:30 -0700 Subject: [PATCH 204/301] fix Issue 23768 - ImportC: ICE on nested C initializer (dlang/dmd!15344) --- dmd/initsem.d | 14 ++++++++--- tests/dmd/compilable/test23786.c | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/dmd/compilable/test23786.c diff --git a/dmd/initsem.d b/dmd/initsem.d index 893d2a627c3..c21a41dc708 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -582,7 +582,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ Initializer visitC(CInitializer ci) { - //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars()); + //printf("CInitializer::semantic() tx: %s t: %s ci: %s\n", (tx ? tx.toChars() : "".ptr), t.toChars(), ci.toChars()); /* Rewrite CInitializer into ExpInitializer, ArrayInitializer, or StructInitializer */ t = t.toBasetype(); @@ -767,7 +767,6 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return err(); } const nfields = sd.fields.length; - size_t fieldi = 0; for (size_t index = 0; index < ci.initializerList.length; ) @@ -804,6 +803,12 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ { if (fieldi == nfields) break; + if (index == 0 && ci.initializerList.length == 1 && di.initializer.isCInitializer()) + { + ci = di.initializer.isCInitializer(); + continue; + } + VarDeclaration field; while (1) // skip field if it overlaps with previously seen fields { @@ -951,10 +956,13 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return initializerSemantic(ai, sc, tx, needInterpret); } else if (ExpInitializer ei = isBraceExpression()) + { return visitExp(ei); + } else { - assert(0); + error(ci.loc, "unrecognized C initializer `%s`", ci.toChars()); + return err(); } } diff --git a/tests/dmd/compilable/test23786.c b/tests/dmd/compilable/test23786.c new file mode 100644 index 00000000000..55082d11b3b --- /dev/null +++ b/tests/dmd/compilable/test23786.c @@ -0,0 +1,41 @@ +// https://issues.dlang.org/show_bug.cgi?id=23768 + +#include + +typedef struct { + union { + struct { + int o; + } f; + }; +} T; + +void f(void) { + T data = (T) { + {.f = {.o = 0}} + }; +} + +/***************/ + +typedef struct { + union { + struct { + struct { double o; } f; + }; + }; +} S; + +_Static_assert(sizeof(S) == 8, "1"); + +int main() +{ + S data = (S) { + {{.f = {.o = 3}}} + }; + assert(data.f.o == 3); + S s; + s.f.o = 4; + assert(s.f.o == 4); + return 0; +} From aacb8809df666a82895e1ae2507d56071f8566a2 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Tue, 27 Jun 2023 09:27:13 +0300 Subject: [PATCH 205/301] Fix Issue 24013 - [REG 2.103.0] address of a __traits(getOverloads) item is not converted to a delegate anymore (dlang/dmd!15347) --- dmd/expressionsem.d | 7 ++++++ tests/dmd/compilable/test24013.d | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/dmd/compilable/test24013.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 3bcf3f66211..f39ac0509d0 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -1189,6 +1189,13 @@ L1: */ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) { + // https://issues.dlang.org/show_bug.cgi?id=24013 + // traits(getOverloads) inserts an alias to select the overload. + // When searching for the right this we need to use the aliased + // overload/function, not the alias. + outerFunc = outerFunc.toAliasFunc(); + calledFunc = calledFunc.toAliasFunc(); + auto thisAd = outerFunc.isMemberLocal(); if (!thisAd) return false; diff --git a/tests/dmd/compilable/test24013.d b/tests/dmd/compilable/test24013.d new file mode 100644 index 00000000000..132ada61c8e --- /dev/null +++ b/tests/dmd/compilable/test24013.d @@ -0,0 +1,43 @@ +// https://issues.dlang.org/show_bug.cgi?id=24013 + +struct PropDescriptor(T) +{ + void define(T delegate() aGetter, string aName) {} +} + +enum Get; + +mixin template PropertyPublisherImpl() +{ + void collectPublicationsFromPairs(T)() + { + foreach (member; __traits(derivedMembers, T)) + foreach (overload; __traits(getOverloads, T, member)) + { + alias Attributes = __traits(getAttributes, overload); + static if (Attributes.length != 0) + { + auto descriptor = new PropDescriptor!size_t; + auto dg = &overload; + descriptor.define(dg, member); + } + } + } +} + +void main() +{ + class Bar + { + size_t _field; + mixin PropertyPublisherImpl; + this() + { + collectPublicationsFromPairs!Bar; + } + @Get size_t field() + { + return _field; + } + } +} From 08c337107642aa5622d42ad35cce0e2c62c60d24 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 29 Jun 2023 23:50:12 +0200 Subject: [PATCH 206/301] [NFC] cleanup plugins.cpp a little --- driver/plugins.cpp | 47 ++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/driver/plugins.cpp b/driver/plugins.cpp index 6820da5ea41..137dfc89851 100644 --- a/driver/plugins.cpp +++ b/driver/plugins.cpp @@ -9,6 +9,9 @@ // // Implements functionality related to plugins (`-plugin=...`). // +// Note: plugins can be LLVM-plugins (to be registered with the pass manager), +// or dlang-plugins for semantic analysis. +// //===----------------------------------------------------------------------===// #include "driver/plugins.h" @@ -38,10 +41,24 @@ cl::list pluginFiles("plugin", cl::CommaSeparated, } // anonymous namespace +/// Loads all plugins for the legacy pass manaager. The static constructor of +/// each plugin should take care of the plugins registering themself with the +/// rest of LDC/LLVM. +void loadAllPluginsLegacyPM() { + for (auto &filename : pluginFiles) { + std::string errorString; + if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), + &errorString)) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + errorString.c_str()); + } + } +} + #if LDC_LLVM_VER >= 1400 namespace { -llvm::SmallVector plugins; +llvm::SmallVector llvm_plugins; } /// Loads all plugins for the new pass manager. These plugins will need to be /// added When building the optimization pipeline. @@ -53,42 +70,28 @@ void loadAllPluginsNewPM() { llvm::toString(plugin.takeError()).c_str()); continue; } - plugins.emplace_back(plugin.get()); + llvm_plugins.emplace_back(plugin.get()); } } void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) { - for (auto &plugin : plugins) { + for (auto &plugin : llvm_plugins) { plugin.registerPassBuilderCallbacks(PB); } } -#endif // LDC_LLVM_VER >= 1400 - -/// Loads all plugins for the legacy pass manaager. The static constructor of -/// each plugin should take care of the plugins registering themself with the -/// rest of LDC/LLVM. -void loadAllPluginsLegacyPM() { - for (auto &filename : pluginFiles) { - std::string errorString; - if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), - &errorString)) { - error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), - errorString.c_str()); - } - } -} - -#if LDC_LLVM_VER >= 1400 void loadAllPlugins() { if (opts::isUsingLegacyPassManager()) loadAllPluginsLegacyPM(); else loadAllPluginsNewPM(); } -#else + +#else // LDC_LLVM_VER >= 1400 + void loadAllPlugins() { loadAllPluginsLegacyPM(); } void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} -#endif + +#endif // LDC_LLVM_VER >= 1400 #else // #if LDC_ENABLE_PLUGINS From e86ef5ba221e4ef9cedc87b6606532b0eb799e73 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 30 Jun 2023 00:49:51 +0200 Subject: [PATCH 207/301] Add support for semantic analysis (dlang frontend) plugins. The plugin's runSemanticAnalysis(Module) function is called for each module, after all other semantic analysis steps (also after DCompute SemA). --- driver/plugins.cpp | 123 ++++++++++++++++++++++-------- gen/semantic.d | 7 +- tests/lit.site.cfg.in | 3 + tests/plugins/basic_sema_plugin.d | 23 ++++++ tests/plugins/lit.local.cfg | 11 +++ 5 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 tests/plugins/basic_sema_plugin.d diff --git a/driver/plugins.cpp b/driver/plugins.cpp index 137dfc89851..2cb30f23979 100644 --- a/driver/plugins.cpp +++ b/driver/plugins.cpp @@ -20,6 +20,7 @@ #include "dmd/errors.h" #include "dmd/globals.h" +#include "dmd/module.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" @@ -39,19 +40,56 @@ cl::list pluginFiles("plugin", cl::CommaSeparated, cl::desc("Pass plugins to load."), cl::value_desc("dynamic_library.so,lib2.so")); +struct SemaPlugin { + llvm::sys::DynamicLibrary library; + void (*runSemanticAnalysis)(Module *); + + SemaPlugin(const llvm::sys::DynamicLibrary &library) + : library(library), runSemanticAnalysis(nullptr) {} +}; + +llvm::SmallVector sema_plugins; + } // anonymous namespace -/// Loads all plugins for the legacy pass manaager. The static constructor of -/// each plugin should take care of the plugins registering themself with the +// Tries to load plugin as SemanticAnalysis. Returns true on 'success', i.e. no +// further attempts needed. +bool loadSemanticAnalysisPlugin(const std::string &filename) { + std::string errorString; + auto library = llvm::sys::DynamicLibrary::getPermanentLibrary( + filename.c_str(), &errorString); + if (!library.isValid()) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + errorString.c_str()); + return true; // No success, but no need to try loading again as LLVM plugin. + } + + SemaPlugin plugin{library}; + + // SemanticAnalysis plugins need to export the `runSemanticAnalysis` function. + void *runSemanticAnalysisFnPtr = + library.getAddressOfSymbol("runSemanticAnalysis"); + + // If the symbol isn't found, this is probably an LLVM plugin. + if (!runSemanticAnalysisFnPtr) + return false; + + plugin.runSemanticAnalysis = + reinterpret_cast(runSemanticAnalysisFnPtr); + + sema_plugins.push_back(std::move(plugin)); + return true; +} + +/// Loads plugin for the legacy pass manager. The static constructor of +/// the plugin should take care of the plugins registering themself with the /// rest of LDC/LLVM. -void loadAllPluginsLegacyPM() { - for (auto &filename : pluginFiles) { - std::string errorString; - if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), - &errorString)) { - error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), - errorString.c_str()); - } +void loadLLVMPluginLegacyPM(const std::string &filename) { + std::string errorString; + if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), + &errorString)) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + errorString.c_str()); } } @@ -59,43 +97,64 @@ void loadAllPluginsLegacyPM() { namespace { llvm::SmallVector llvm_plugins; + +/// Loads plugin for the new pass manager. The plugin will need to be +/// added explicitly when building the optimization pipeline. +void loadLLVMPluginNewPM(const std::string &filename) { + + auto plugin = llvm::PassPlugin::Load(filename); + if (!plugin) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + llvm::toString(plugin.takeError()).c_str()); + return; + } + llvm_plugins.emplace_back(plugin.get()); } -/// Loads all plugins for the new pass manager. These plugins will need to be -/// added When building the optimization pipeline. -void loadAllPluginsNewPM() { + +} // anonymous namespace + +#endif // LDC_LLVM_VER >= 1400 + +void loadLLVMPlugin(const std::string &filename) { +#if LDC_LLVM_VER >= 1400 + if (opts::isUsingLegacyPassManager()) + loadLLVMPluginLegacyPM(filename); + else + loadLLVMPluginNewPM(filename); +#else + loadLLVMPluginLegacyPM(filename); +#endif +} + +void loadAllPlugins() { for (auto &filename : pluginFiles) { - auto plugin = llvm::PassPlugin::Load(filename); - if (!plugin) { - error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), - llvm::toString(plugin.takeError()).c_str()); - continue; - } - llvm_plugins.emplace_back(plugin.get()); + // First attempt to load plugin as SemanticAnalysis plugin. If unsuccesfull, + // load as LLVM plugin. + auto success = loadSemanticAnalysisPlugin(filename); + if (!success) + loadLLVMPlugin(filename); } } + void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) { +#if LDC_LLVM_VER >= 1400 for (auto &plugin : llvm_plugins) { plugin.registerPassBuilderCallbacks(PB); } +#endif } -void loadAllPlugins() { - if (opts::isUsingLegacyPassManager()) - loadAllPluginsLegacyPM(); - else - loadAllPluginsNewPM(); +void runAllSemanticAnalysisPlugins(Module *m) { + for (auto &plugin : sema_plugins) { + assert(plugin.runSemanticAnalysis); + plugin.runSemanticAnalysis(m); + } } -#else // LDC_LLVM_VER >= 1400 - -void loadAllPlugins() { loadAllPluginsLegacyPM(); } -void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} - -#endif // LDC_LLVM_VER >= 1400 - #else // #if LDC_ENABLE_PLUGINS void loadAllPlugins() {} void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} +void runAllSemanticAnalysisPlugins(Module *m) {} #endif // LDC_ENABLE_PLUGINS diff --git a/gen/semantic.d b/gen/semantic.d index 359f75a943c..9ea4f454972 100644 --- a/gen/semantic.d +++ b/gen/semantic.d @@ -15,13 +15,16 @@ import dmd.dmodule; extern(C++) void dcomputeSemanticAnalysis(Module m); extern(C) int hasComputeAttr(Dsymbol m); +extern(C++) void runAllSemanticAnalysisPlugins(Module m); extern(C++) void extraLDCSpecificSemanticAnalysis(ref Modules modules) { foreach(m; modules[]) { - if (hasComputeAttr(m)) + if (hasComputeAttr(m)) { dcomputeSemanticAnalysis(m); + } + + runAllSemanticAnalysisPlugins(m); } - } diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index 44dac1a1c3c..c35a61c320c 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -21,6 +21,7 @@ config.timetrace2txt_bin = "@TIMETRACE2TXT_BIN@" config.ldc2_bin_dir = "@LDC2_BIN_DIR@" config.ldc2_lib_dir = "@LDC2_LIB_DIR@" config.ldc2_runtime_dir = "@RUNTIME_DIR@" +config.ldc2_source_dir = "@PROJECT_SOURCE_DIR@" config.test_source_root = "@TESTS_IR_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_version = @LDC_LLVM_VER@ @@ -161,6 +162,8 @@ config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) ) config.substitutions.append( ('%runtimedir', config.ldc2_runtime_dir ) ) +print(platform.system()) + # Add platform-dependent file extension substitutions if (platform.system() == 'Windows'): # add LDC lib dir to the path so app will be able to find jit.dll diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d new file mode 100644 index 00000000000..1cf010a8498 --- /dev/null +++ b/tests/plugins/basic_sema_plugin.d @@ -0,0 +1,23 @@ +// REQUIRES: Plugins + +// RUN: split-file %s %t --leading-lines +// RUN: %ldc %t/plugin.d %plugin_compile_flags -of=%t/plugin%so +// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d + +//--- plugin.d +import dmd.dmodule : Module; +import dmd.errors; +import dmd.location; + +extern(C) void runSemanticAnalysis(Module m) { + if (m.md) { + warning(m.md.loc, "It works!"); + } +} + +//--- testcase.d +// CHECK: testcase.d([[@LINE+1]]): Warning: It works! +module testcase; +int testfunction(int i) { + return i * 2; +} diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 06c2097c00e..9b1ae7fa6ff 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -6,3 +6,14 @@ if (config.plugins_supported): config.available_features.add('Plugins') config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config') config.environment['LLVM_VERSION'] = str(config.llvm_version) + + plugin_compile_flags = [ '-I' + config.ldc2_source_dir, + '--d-version=IN_LLVM', + '-J' + config.ldc2_source_dir + '/dmd/res', + '--shared', + ] + if (platform.system() == 'Darwin'): + plugin_compile_flags.append('-L-Wl,-undefined,dynamic_lookup') + config.substitutions.append( ('%plugin_compile_flags', " ".join(plugin_compile_flags) ) ) + + From 9cf7d91e4887767d200afe4b992b20ecef112346 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 30 Jun 2023 01:01:17 +0200 Subject: [PATCH 208/301] fixups --- driver/plugins.cpp | 15 ++++++--------- tests/lit.site.cfg.in | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/driver/plugins.cpp b/driver/plugins.cpp index 2cb30f23979..75744ed7907 100644 --- a/driver/plugins.cpp +++ b/driver/plugins.cpp @@ -9,7 +9,7 @@ // // Implements functionality related to plugins (`-plugin=...`). // -// Note: plugins can be LLVM-plugins (to be registered with the pass manager), +// Note: plugins can be LLVM-plugins (to be registered with the pass manager) // or dlang-plugins for semantic analysis. // //===----------------------------------------------------------------------===// @@ -44,8 +44,9 @@ struct SemaPlugin { llvm::sys::DynamicLibrary library; void (*runSemanticAnalysis)(Module *); - SemaPlugin(const llvm::sys::DynamicLibrary &library) - : library(library), runSemanticAnalysis(nullptr) {} + SemaPlugin(const llvm::sys::DynamicLibrary &library, + void (*runSemanticAnalysis)(Module *)) + : library(library), runSemanticAnalysis(runSemanticAnalysis) {} }; llvm::SmallVector sema_plugins; @@ -64,8 +65,6 @@ bool loadSemanticAnalysisPlugin(const std::string &filename) { return true; // No success, but no need to try loading again as LLVM plugin. } - SemaPlugin plugin{library}; - // SemanticAnalysis plugins need to export the `runSemanticAnalysis` function. void *runSemanticAnalysisFnPtr = library.getAddressOfSymbol("runSemanticAnalysis"); @@ -74,10 +73,8 @@ bool loadSemanticAnalysisPlugin(const std::string &filename) { if (!runSemanticAnalysisFnPtr) return false; - plugin.runSemanticAnalysis = - reinterpret_cast(runSemanticAnalysisFnPtr); - - sema_plugins.push_back(std::move(plugin)); + sema_plugins.emplace_back( + library, reinterpret_cast(runSemanticAnalysisFnPtr)); return true; } diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index c35a61c320c..db6350e4542 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -162,8 +162,6 @@ config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) ) config.substitutions.append( ('%runtimedir', config.ldc2_runtime_dir ) ) -print(platform.system()) - # Add platform-dependent file extension substitutions if (platform.system() == 'Windows'): # add LDC lib dir to the path so app will be able to find jit.dll From 33f5291970b04d1e928fb4b65b23677ecbb97b18 Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Tue, 4 Jul 2023 11:12:14 +0300 Subject: [PATCH 209/301] Fix Issue 23966 - [REG2.102] Cannot use traits(getAttributes) with overloaded template (dlang/dmd!15353) --- dmd/func.d | 3 ++- dmd/traits.d | 2 +- tests/dmd/compilable/cppmangle.d | 1 - tests/dmd/compilable/test23966.d | 19 +++++++++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/dmd/compilable/test23966.d diff --git a/dmd/func.d b/dmd/func.d index 8e11ab1bb4c..d0aa9cae1b3 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -1968,7 +1968,8 @@ extern (C++) class FuncDeclaration : Declaration overloadApply(cast() this, (Dsymbol s) { auto f = s.isFuncDeclaration(); - if (!f) + auto td = s.isTemplateDeclaration(); + if (!f && !td) return 0; if (result) { diff --git a/dmd/traits.d b/dmd/traits.d index 0f363536d8e..21ec3ab0e18 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -1223,7 +1223,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) // @@@DEPRECATION 2.100.2 if (auto td = s.isTemplateDeclaration()) { - if (td.overnext || td.funcroot) + if (td.overnext || td.overroot) { deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not overload sets such as: `%s`", td.ident.toChars()); deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from"); diff --git a/tests/dmd/compilable/cppmangle.d b/tests/dmd/compilable/cppmangle.d index fc74c944cad..264b374dd55 100644 --- a/tests/dmd/compilable/cppmangle.d +++ b/tests/dmd/compilable/cppmangle.d @@ -528,7 +528,6 @@ version (CppMangle_Itanium) static assert(basic_ostream!(char, char_traits!char).food.mangleof == "_ZNSo4foodEv"); static assert(basic_iostream!(char, char_traits!char).fooe.mangleof == "_ZNSd4fooeEv"); - static assert(func_18957_2.mangleof == `_Z12func_18957_2PSaIiE`); static assert(func_18957_2!(allocator!int).mangleof == `_Z12func_18957_2ISaIiEET_PS1_`); static assert(func_20413.mangleof == `_Z10func_20413St4pairIifES_IfiE`); diff --git a/tests/dmd/compilable/test23966.d b/tests/dmd/compilable/test23966.d new file mode 100644 index 00000000000..71aa8ca229e --- /dev/null +++ b/tests/dmd/compilable/test23966.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=23966 +module test23966; + +@("gigi") +void fun() {} +@("mimi") +void fun(int) {} +@("hihi") +void fun(int, int) {} +@("bibi") +void fun()(int, ulong) {} + +void main() +{ + static foreach (t; __traits(getOverloads, test23966, "fun", true)) + static foreach(attr; __traits(getAttributes, t)) + {} + +} From f77870cee30039b84ba32713a9f709c4ae934f6d Mon Sep 17 00:00:00 2001 From: Dmytro Katyukha Date: Wed, 5 Jul 2023 14:00:45 +0300 Subject: [PATCH 210/301] ImportC: Issue 24022 - Error: attribute `__anonymous` is used as a type (dlang/dmd!15365) * Implement test case for Issue 24022 * [FIX] Issue 24022 Bug investigation info ====================== Currently (before this fix) on attempt to use `enum` declared as `typedef enum {A=1} E;` as type of argument in D function, it (`enum`) will be resolved as `kind=attribute` on during semantic analysis for D ([typesem.d](https://github.com/dlang/dmd/blob/aacb8809df666a82895e1ae2507d56071f8566a2/compiler/src/dmd/typesem.d#L1504)), but it have to be resolved as type. The `enum` declared this way is handled by this [code](https://github.com/dlang/dmd/blob/aacb8809df666a82895e1ae2507d56071f8566a2/compiler/src/dmd/cparse.d#L1907), and `declareTag` returns the attribute instead of type, but later, there is [code that create alias for attribute](https://github.com/dlang/dmd/blob/aacb8809df666a82895e1ae2507d56071f8566a2/compiler/src/dmd/cparse.d#L1918C29-L1918C79), though, it seems, that alias have to be created to type, instead of attribute. Investigating [declareTag](https://github.com/dlang/dmd/blob/aacb8809df666a82895e1ae2507d56071f8566a2/compiler/src/dmd/cparse.d#L1715) function shows that the returned value changed by call to [applySpecifier](https://github.com/dlang/dmd/blob/aacb8809df666a82895e1ae2507d56071f8566a2/compiler/src/dmd/cparse.d#L5234), that applies `AlignDeclaration` when `if (!specifier.packalign.isDefault())`. Fix info ======== With this commit the alias will be created to TypeTag, instead of attribute produced by declared tag. Also, the code simplified, because there is no more need for special handling of enums in modified piece of code. --- dmd/cparse.d | 10 ++++----- tests/dmd/compilable/imports/imp24022.c | 5 +++++ tests/dmd/compilable/test24022.d | 30 +++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 tests/dmd/compilable/imports/imp24022.c create mode 100644 tests/dmd/compilable/test24022.d diff --git a/dmd/cparse.d b/dmd/cparse.d index 9b7db1f33f4..b45985b8861 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -1909,14 +1909,12 @@ final class CParser(AST) : Parser!AST if (tt.id || tt.tok == TOK.enum_) { if (!tt.id && id) + /* This applies for enums declared as + * typedef enum {A} E; + */ tt.id = id; Specifier spec; - auto stag = declareTag(tt, spec); - if (tt.tok == TOK.enum_) - { - isalias = false; - s = new AST.AliasDeclaration(token.loc, id, stag); - } + declareTag(tt, spec); } } if (isalias) diff --git a/tests/dmd/compilable/imports/imp24022.c b/tests/dmd/compilable/imports/imp24022.c new file mode 100644 index 00000000000..b65e4e155bf --- /dev/null +++ b/tests/dmd/compilable/imports/imp24022.c @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +typedef enum { + A = 1, + B, +} E; diff --git a/tests/dmd/compilable/test24022.d b/tests/dmd/compilable/test24022.d new file mode 100644 index 00000000000..f499636f126 --- /dev/null +++ b/tests/dmd/compilable/test24022.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +// EXTRA_FILES: imports/imp24022.c +import imports.imp24022; + +auto some_d_func(E v) { + return v; +} + +auto some_d_other_func() { + const struct R { + E r; + this(in E vparam) { r = vparam; } + } + return R(A); +} + +void main(string[] args) { + E expected = E.A; + E res = some_d_func(A); + assert (res == A); + assert (res == expected); + + res = some_d_func(E.B); + assert (res == B); + assert (res == E.B); + + auto res2 = some_d_other_func(); + assert (res2.r == A); + assert (res2.r == expected); +} From f1b38d770d61943b82145649f9a2212124b73093 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Wed, 5 Jul 2023 15:49:01 +0200 Subject: [PATCH 211/301] Add visitor test plugin --- tests/plugins/lit.local.cfg | 1 + tests/plugins/visitor_example.d | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/plugins/visitor_example.d diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 9b1ae7fa6ff..a856482ce54 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -11,6 +11,7 @@ if (config.plugins_supported): '--d-version=IN_LLVM', '-J' + config.ldc2_source_dir + '/dmd/res', '--shared', + '--defaultlib=', ] if (platform.system() == 'Darwin'): plugin_compile_flags.append('-L-Wl,-undefined,dynamic_lookup') diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d new file mode 100644 index 00000000000..0754e0c37d9 --- /dev/null +++ b/tests/plugins/visitor_example.d @@ -0,0 +1,51 @@ +// REQUIRES: Plugins + +// RUN: split-file %s %t --leading-lines +// RUN: %ldc -g %t/plugin.d %plugin_compile_flags -of=%t/plugin%so +// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d + +//--- plugin.d +import dmd.dmodule; +import dmd.errors; +import dmd.location; +import dmd.visitor; +import dmd.declaration; +import dmd.dsymbol; + +extern(C++) class MyVisitor : SemanticTimeTransitiveVisitor { + alias visit = SemanticTimeTransitiveVisitor.visit; + + override void visit(VarDeclaration vd) { + if (vd.aliasTuple) + warning(vd.loc, "It works!"); + } +} + +extern(C) void runSemanticAnalysis(Module m) { + scope v = new MyVisitor(); + if (!m.members) + return; + m.members.foreachDsymbol((s) { + s.accept(v); + }); +} + +//--- testcase.d +alias AliasSeq(TList...) = TList; + +int i = 0; +struct A { + ~this() { + i *= 2; + } +} + +void main() { + { + // CHECK: testcase.d([[@LINE+1]]): Warning: + AliasSeq!(A, A) params; + i = 1; + } + + assert(i == 4); +} From 425254098bacedc259eece5e40c085a5e310813f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 5 Jul 2023 12:19:09 -0700 Subject: [PATCH 212/301] fix Issue 24026 - ImportC: ICE on nested C initializer 2 (dlang/dmd!15375) --- dmd/initsem.d | 25 ++++++++++--- .../dmd/{compilable => runnable}/test23786.c | 35 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) rename tests/dmd/{compilable => runnable}/test23786.c (53%) diff --git a/dmd/initsem.d b/dmd/initsem.d index c21a41dc708..054ca766868 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -769,10 +769,13 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ const nfields = sd.fields.length; size_t fieldi = 0; + Loop1: for (size_t index = 0; index < ci.initializerList.length; ) { - auto di = ci.initializerList[index]; - auto dlist = di.designatorList; + CInitializer cprev; + L1: + DesigInit di = ci.initializerList[index]; + Designators* dlist = di.designatorList; if (dlist) { const length = (*dlist).length; @@ -795,9 +798,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ si.addInit(id, di.initializer); ++fieldi; ++index; - break; + continue Loop1; } } + if (cprev) + { + /* The peeling didn't work, so unpeel it + */ + ci = cprev; + di = ci.initializerList[index]; + goto L2; + } + error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); + return err(); } else { @@ -805,10 +818,14 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ break; if (index == 0 && ci.initializerList.length == 1 && di.initializer.isCInitializer()) { + /* Try peeling off this set of { } and see if it works + */ + cprev = ci; ci = di.initializer.isCInitializer(); - continue; + goto L1; } + L2: VarDeclaration field; while (1) // skip field if it overlaps with previously seen fields { diff --git a/tests/dmd/compilable/test23786.c b/tests/dmd/runnable/test23786.c similarity index 53% rename from tests/dmd/compilable/test23786.c rename to tests/dmd/runnable/test23786.c index 55082d11b3b..3cf62063232 100644 --- a/tests/dmd/compilable/test23786.c +++ b/tests/dmd/runnable/test23786.c @@ -1,7 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=23768 -#include - typedef struct { union { struct { @@ -28,14 +26,41 @@ typedef struct { _Static_assert(sizeof(S) == 8, "1"); -int main() +void test23768() { S data = (S) { {{.f = {.o = 3}}} }; - assert(data.f.o == 3); + __check(data.f.o == 3); S s; s.f.o = 4; - assert(s.f.o == 4); + __check(s.f.o == 4); +} + +/**************************/ +// https://issues.dlang.org/show_bug.cgi?id=24026 + +struct A +{ + int type; +}; + +struct E +{ + struct A action; +}; + +void test24026() +{ + struct E entry = {{ .type = 1 }}; + __check(entry.action.type == 1); +} + +/**************************/ + +int main() +{ + test23768(); + test24026(); return 0; } From c1e895e105b735146ac50ec420fc45a8179c34a9 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Wed, 5 Jul 2023 23:16:10 +0200 Subject: [PATCH 213/301] fixup non-plugin build --- driver/plugins.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/driver/plugins.cpp b/driver/plugins.cpp index 75744ed7907..713d8bee806 100644 --- a/driver/plugins.cpp +++ b/driver/plugins.cpp @@ -150,6 +150,8 @@ void runAllSemanticAnalysisPlugins(Module *m) { #else // #if LDC_ENABLE_PLUGINS +class Module; + void loadAllPlugins() {} void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} void runAllSemanticAnalysisPlugins(Module *m) {} From 89f43fd5b71a002330e51397e5ba73ef31061fa5 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Wed, 5 Jul 2023 23:43:20 +0200 Subject: [PATCH 214/301] fixups --- tests/plugins/lit.local.cfg | 20 ++++++++++++++++++++ tests/plugins/visitor_example.d | 11 +++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index a856482ce54..4d4c70576e8 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -1,6 +1,12 @@ +import lit.formats +import lit.util import os +import sys import platform +import string import re +import subprocess +import glob if (config.plugins_supported): config.available_features.add('Plugins') @@ -17,4 +23,18 @@ if (config.plugins_supported): plugin_compile_flags.append('-L-Wl,-undefined,dynamic_lookup') config.substitutions.append( ('%plugin_compile_flags', " ".join(plugin_compile_flags) ) ) + # Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler + # Be conservative: only set it when host LDC and just-built LDC have identical versions + command = [config.ldc2_bin, '--version'] + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)" + text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" + text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" + m = re.compile('LDC - the LLVM D compiler \((.*)\)').match(text1) + ldc_version = m.group(1) + m3 = re.compile(' built with.* \((.*)\)').match(text3) + host_version = m3.group(1) + if (ldc_version == host_version): + config.available_features.add('ABI_compatible_with_host_D') + diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index 0754e0c37d9..e96ed0252f1 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -1,4 +1,5 @@ // REQUIRES: Plugins +// REQUIRES: ABI_compatible_with_host_D // RUN: split-file %s %t --leading-lines // RUN: %ldc -g %t/plugin.d %plugin_compile_flags -of=%t/plugin%so @@ -16,8 +17,14 @@ extern(C++) class MyVisitor : SemanticTimeTransitiveVisitor { alias visit = SemanticTimeTransitiveVisitor.visit; override void visit(VarDeclaration vd) { - if (vd.aliasTuple) - warning(vd.loc, "It works!"); + if (vd.aliasTuple) { + vd.aliasTuple.foreachVar((s) { + auto vardecl = s.isVarDeclaration(); + if (vardecl && vardecl.type.needsDestruction()) { + warning(vardecl.loc, "It works!"); + } + }); + } } } From 0bba0b4030ead380f507982a5e2a71c79823c7db Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Thu, 6 Jul 2023 09:20:15 +0000 Subject: [PATCH 215/301] Fix Issue 23890 - "Warning: cannot inline function" in core.lifetime (dlang/dmd!15186) * Fix Issue 23890 - "Warning: cannot inline function" in core.lifetime * druntime/test/profile: Divorce test from exact Druntime source numbers Allow lines to move around in core/lifetime without affecting this test. --- runtime/druntime/src/core/lifetime.d | 6 +++++- runtime/druntime/test/profile/Makefile | 16 +++++++++++++++- .../test/profile/myprofilegc.log.freebsd.32.exp | 1 - .../test/profile/myprofilegc.log.freebsd.64.exp | 1 - .../test/profile/myprofilegc.log.linux.32.exp | 1 - .../test/profile/myprofilegc.log.linux.64.exp | 1 - .../test/profile/myprofilegc.log.osx.32.exp | 1 - .../test/profile/myprofilegc.log.osx.64.exp | 1 - 8 files changed, 20 insertions(+), 8 deletions(-) diff --git a/runtime/druntime/src/core/lifetime.d b/runtime/druntime/src/core/lifetime.d index 5e339c041d1..2745d54e105 100644 --- a/runtime/druntime/src/core/lifetime.d +++ b/runtime/druntime/src/core/lifetime.d @@ -1570,7 +1570,11 @@ template forward(args...) alias fwd = arg; // (r)value else - @property auto fwd(){ pragma(inline, true); return move(arg); } + @property auto fwd() + { + version (DigitalMars) { /* @@BUG 23890@@ */ } else pragma(inline, true); + return move(arg); + } } alias Result = AliasSeq!(); diff --git a/runtime/druntime/test/profile/Makefile b/runtime/druntime/test/profile/Makefile index fff967a9172..fefa05f9a8c 100644 --- a/runtime/druntime/test/profile/Makefile +++ b/runtime/druntime/test/profile/Makefile @@ -5,6 +5,18 @@ TESTS:=profile profilegc both DIFF:=diff GREP:=grep +ifeq (freebsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (openbsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (netbsd,$(OS)) + SHELL=/usr/pkg/bin/bash +else ifeq (dragonflybsd,$(OS)) + SHELL=/usr/local/bin/bash +else + SHELL=/bin/bash +endif + .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) @@ -25,7 +37,9 @@ $(ROOT)/profilegc.done: $(ROOT)/%.done: $(ROOT)/% @echo Testing $* @rm -f $(ROOT)/myprofilegc.log $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/myprofilegc.log - $(QUIET)$(DIFF) myprofilegc.log.$(OS).$(MODEL).exp $(ROOT)/myprofilegc.log + $(QUIET)$(DIFF) \ + <($(GREP) -vF 'core.' myprofilegc.log.$(OS).$(MODEL).exp) \ + <($(GREP) -vF 'core.' $(ROOT)/myprofilegc.log) @touch $@ $(ROOT)/both.done: DFLAGS+=-profile -profile=gc diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp index 4faa76ae777..4c5494b7e11 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 From ad2c70f899857c0392b716c78e68bfec271c8efa Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 7 Jul 2023 12:16:28 +0200 Subject: [PATCH 216/301] GHA: Use LDC-LLVM v16 --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ede1951c39..d0d059fff50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,8 @@ concurrency: cancel-in-progress: true env: - CLANG_VERSION: 15.0.6 - LLVM_VERSION: 15.0.7 + CLANG_VERSION: 16.0.0 + LLVM_VERSION: c12d3509 jobs: build-native: From 6de151e489f624c3758bf493dfae9be8173f7cb6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 7 Jul 2023 14:29:52 +0200 Subject: [PATCH 217/301] Adapt some lit-tests to LLVM 16 --- tests/driver/fwarn-stack-size.d | 2 +- tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp | 9 +++++---- tests/sanitizers/msan_uninitialized.d | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/driver/fwarn-stack-size.d b/tests/driver/fwarn-stack-size.d index 1ed5184faff..f0aa124db93 100644 --- a/tests/driver/fwarn-stack-size.d +++ b/tests/driver/fwarn-stack-size.d @@ -14,7 +14,7 @@ void small_stack() byte[100] a; } -// CHECK: warning: stack frame size {{.*}} exceeds limit (200) in function {{.*}}14fwarnstacksize9big_stack +// CHECK: warning: {{(:0:0: )?}}stack frame size {{.*}} exceeds limit (200) in function {{.*}}14fwarnstacksize9big_stack void big_stack() { byte[1000] b; diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp index 065b3b086a1..cbdde8bbb30 100644 --- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp @@ -58,6 +58,9 @@ bool FuncEntryCallPass::runOnFunction(Function &F) { return true; } + +#if LLVM_VERSION < 1400 // legacy pass manager + static void addFuncEntryCallPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { PM.add(new FuncEntryCallPass()); @@ -67,9 +70,8 @@ static RegisterStandardPasses RegisterFuncEntryCallPass0(PassManagerBuilder::EP_EnabledOnOptLevel0, addFuncEntryCallPass); +#else // LLVM 14+ - -#if LLVM_VERSION >= 1400 // Implementation of plugin for the new passmanager #include "llvm/IR/PassManager.h" @@ -114,5 +116,4 @@ llvmGetPassPluginInfo() { }; } -#endif - +#endif // LLVM 14+ diff --git a/tests/sanitizers/msan_uninitialized.d b/tests/sanitizers/msan_uninitialized.d index 407d20f7e8f..f7048d5f60f 100644 --- a/tests/sanitizers/msan_uninitialized.d +++ b/tests/sanitizers/msan_uninitialized.d @@ -8,7 +8,7 @@ // CHECK: MemorySanitizer: use-of-uninitialized-value -// CHECK: Uninitialized value was created by an allocation of {{.*}} in the stack frame of function '_Dmain' +// CHECK: Uninitialized value was created by an allocation of {{.*}} in the stack frame // CHECK-NEXT: #0 {{.* in (_Dmain|D main) .*}}msan_uninitialized.d:[[@LINE+1]] int main() { From e4e36f5dc07855343615849f89b9361e3d4b80f7 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Jul 2023 12:27:22 +0200 Subject: [PATCH 218/301] [little dcompute fixup for LLVM 16] --- gen/tollvm.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index d51cd0d54f9..a6e885e0756 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -581,9 +581,7 @@ LLType *stripAddrSpaces(LLType *t) if (!pt) return t; -#if LDC_LLVM_VER >= 1600 - return getVoidPtrType(); -#elif LDC_LLVM_VER >= 1400 +#if LDC_LLVM_VER >= 1400 if (pt->isOpaque()) return getVoidPtrType(); else { From a0da286cc6d997f48b752e52a6707f72951e426e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Jul 2023 13:15:40 +0200 Subject: [PATCH 219/301] Android: Adapt llvm-config.in to LLVM 16 --- .../3-build-cross/android-llvm-config.in | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/actions/3-build-cross/android-llvm-config.in b/.github/actions/3-build-cross/android-llvm-config.in index 34060eb4948..d2cc7bb4d97 100644 --- a/.github/actions/3-build-cross/android-llvm-config.in +++ b/.github/actions/3-build-cross/android-llvm-config.in @@ -45,7 +45,7 @@ prefix=@LLVM_INSTALL_DIR@ has_rtti=NO CPPFLAGS="-I${prefix}/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" CFLAGS="${CPPFLAGS} ${CFLAGS}" -CXXFLAGS="${CFLAGS} -std=c++14 -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables" +CXXFLAGS="${CFLAGS} -std=c++17 -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables" if [ "$has_rtti" != "YES" ]; then CXXFLAGS="$CXXFLAGS -fno-rtti"; fi LDFLAGS="-L${prefix}/lib" LIBFILE="${prefix}/lib/libLLVM-$version.so" @@ -53,28 +53,29 @@ LIBFILE="${prefix}/lib/libLLVM-$version.so" components="aarch64 aarch64asmparser aarch64codegen aarch64desc aarch64disassembler aarch64info aarch64utils \ aggressiveinstcombine all all-targets analysis arm armasmparser armcodegen armdesc armdisassembler arminfo armutils \ asmparser asmprinter binaryformat bitreader bitstreamreader bitwriter cfguard codegen core coroutines coverage \ -debuginfocodeview debuginfodwarf debuginfogsym debuginfomsf debuginfopdb demangle dlltooldriver dwarflinker dwp \ -engine executionengine extensions filecheck frontendopenacc frontendopenmp fuzzercli fuzzmutate globalisel instcombine \ -instrumentation interfacestub interpreter ipo irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler \ +debuginfocodeview debuginfodwarf debuginfogsym debuginfologicalview debuginfomsf debuginfopdb demangle dlltooldriver dwarflinker dwarflinkerparallel dwp \ +engine executionengine extensions filecheck frontendhlsl frontendopenacc frontendopenmp fuzzercli fuzzmutate globalisel instcombine \ +instrumentation interfacestub interpreter ipo irprinter irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler \ mcjit mcparser mirparser native nativecodegen objcarcopts objcopy object objectyaml option orcjit orcshared orctargetprocess \ -passes profiledata remarks runtimedyld scalaropts selectiondag support symbolize tablegen target textapi \ +passes profiledata remarks runtimedyld scalaropts selectiondag spirv spirvcodegen spirvdesc spirvinfo support symbolize tablegen target targetparser textapi \ transformutils vectorize webassembly webassemblyasmparser webassemblycodegen webassemblydesc webassemblydisassembler \ webassemblyinfo webassemblyutils windowsdriver windowsmanifest x86 x86asmparser x86codegen x86desc x86disassembler x86info \ x86targetmca xray" -static_libs="-lLLVMWindowsManifest -lLLVMWindowsDriver -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor \ +static_libs="-lLLVMWindowsManifest -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor \ +-lLLVMSPIRVCodeGen -lLLVMSPIRVDesc -lLLVMSPIRVInfo \ -lLLVMX86TargetMCA -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMWebAssemblyDisassembler \ -lLLVMWebAssemblyAsmParser -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc -lLLVMWebAssemblyUtils -lLLVMWebAssemblyInfo -lLLVMARMDisassembler \ -lLLVMARMAsmParser -lLLVMARMCodeGen -lLLVMARMDesc -lLLVMARMUtils -lLLVMARMInfo -lLLVMAArch64Disassembler \ -lLLVMAArch64AsmParser -lLLVMAArch64CodeGen -lLLVMAArch64Desc -lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMOrcJIT \ --lLLVMMCJIT -lLLVMJITLink -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMOrcTargetProcess -lLLVMOrcShared \ --lLLVMDWP -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMObjCopy -lLLVMMCA \ --lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMObjCARCOpts -lLLVMipo \ --lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMExtensions \ --lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMSelectionDAG \ --lLLVMCodeGen -lLLVMIRReader -lLLVMAsmParser -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget \ +-lLLVMWindowsDriver -lLLVMMCJIT -lLLVMJITLink -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMOrcTargetProcess -lLLVMOrcShared \ +-lLLVMDWP -lLLVMDebugInfoLogicalView -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMObjCopy -lLLVMMCA \ +-lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMipo \ +-lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMFrontendHLSL -lLLVMExtensions \ +-lLLVMDWARFLinkerParallel -lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMSelectionDAG \ +-lLLVMCodeGen -lLLVMObjCARCOpts -lLLVMIRPrinter -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget \ -lLLVMScalarOpts -lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter -lLLVMAnalysis \ --lLLVMProfileData -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoMSF -lLLVMDebugInfoDWARF -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMMC -lLLVMDebugInfoCodeView \ --lLLVMBitReader -lLLVMFuzzerCLI -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMTableGen -lLLVMSupport \ +-lLLVMProfileData -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoMSF -lLLVMDebugInfoDWARF -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMIRReader -lLLVMAsmParser -lLLVMMC -lLLVMDebugInfoCodeView \ +-lLLVMBitReader -lLLVMFuzzerCLI -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMTargetParser -lLLVMTableGen -lLLVMSupport \ -lLLVMDemangle" shared_libs="-lLLVM-$version" libs=$static_libs From 823b9200d6e670e1623b3ae6d84f698da137207c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Jul 2023 13:26:56 +0200 Subject: [PATCH 220/301] Re-enable lit-test codegen/dcompute_cl_images.d for LLVM 16 --- tests/codegen/dcompute_cl_images.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/codegen/dcompute_cl_images.d b/tests/codegen/dcompute_cl_images.d index b3f5f5ca07f..bb653a5c45c 100644 --- a/tests/codegen/dcompute_cl_images.d +++ b/tests/codegen/dcompute_cl_images.d @@ -1,7 +1,7 @@ // REQUIRES: target_SPIRV -// FIXME: hits an assertion with recent SPIRV-LLVM-Translator, see https://github.com/ldc-developers/ldc/pull/4010#issuecomment-1191820165 -// XFAIL: atleast_llvm1500 +// FIXME: hits an assertion with SPIRV-LLVM-Translator for LLVM 15, see https://github.com/ldc-developers/ldc/pull/4010#issuecomment-1191820165 +// XFAIL: atleast_llvm1500 && atmost_llvm1509 // RUN: %ldc -c -mdcompute-targets=ocl-220 -m64 -I%S/inputs -mdcompute-file-prefix=%t -output-ll -output-o %s && FileCheck %s < %t_ocl220_64.ll @compute(CompileFor.deviceOnly) module dcompute_cl_images; From 340d7cf2de6b249e9d99ac11b7760c0d306ff52f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Jul 2023 16:02:40 +0200 Subject: [PATCH 221/301] [fix testPluginLegacy lit-test regression with LLVM 14] --- tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp index cbdde8bbb30..0ad32d12d77 100644 --- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp @@ -59,7 +59,7 @@ bool FuncEntryCallPass::runOnFunction(Function &F) { } -#if LLVM_VERSION < 1400 // legacy pass manager +#if LLVM_VERSION < 1500 // legacy pass manager static void addFuncEntryCallPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -70,9 +70,10 @@ static RegisterStandardPasses RegisterFuncEntryCallPass0(PassManagerBuilder::EP_EnabledOnOptLevel0, addFuncEntryCallPass); -#else // LLVM 14+ +#endif + -// Implementation of plugin for the new passmanager +#if LLVM_VERSION >= 1400 // new pass manager #include "llvm/IR/PassManager.h" #include "llvm/Passes/PassBuilder.h" From 7f739844e945cf0f0494e300309c6b5d702683a5 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Mon, 10 Jul 2023 20:12:26 +0200 Subject: [PATCH 222/301] Fix regression introduced by dlang/dmd!14985 --- dmd/visitor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dmd/visitor.h b/dmd/visitor.h index 4cf798ad5ca..3d8c3e60220 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -268,6 +268,7 @@ class UshrAssignExp; class CatAssignExp; class CatElemAssignExp; class CatDcharAssignExp; +class LoweredAssignExp; class AddExp; class MinExp; class CatExp; @@ -659,6 +660,7 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); } virtual void visit(VoidInitExp *e) { visit((Expression *)e); } virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); } + virtual void visit(LoweredAssignExp *e) { visit((AssignExp *)e); } }; class StoppableVisitor : public Visitor From 2dd4b89c13c0a2d6f7d39b68deaff66705fc84cb Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Fri, 14 Jul 2023 14:34:04 +0300 Subject: [PATCH 223/301] Fix Issues 23951 and 23279 - traits(hasMember) does not follow alias this + ICE when using traits(hasMember) on an erroneous member (dlang/dmd!15406) * Fix Issue 23951 - traits(getMember) does not follow alias this * Fix Issue 23279 - ICE when using traits(hasMember) with an erroneous member --- dmd/traits.d | 21 +++++++++++++++------ tests/dmd/compilable/test23951.d | 10 ++++++++++ tests/dmd/fail_compilation/test23279.d | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 tests/dmd/compilable/test23951.d create mode 100644 tests/dmd/fail_compilation/test23279.d diff --git a/dmd/traits.d b/dmd/traits.d index 21ec3ab0e18..fbb77b0ffdf 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -947,15 +947,24 @@ Expression semanticTraits(TraitsExp e, Scope* sc) */ Dsymbol sym = getDsymbol(o); + + if (sym && e.ident == Id.hasMember) + { + if (auto sm = sym.search(e.loc, id)) + return True(); + + // https://issues.dlang.org/show_bug.cgi?id=23951 + if (auto decl = sym.isDeclaration()) + { + ex = typeDotIdExp(e.loc, decl.type, id); + goto doSemantic; + } + } + if (auto t = isType(o)) ex = typeDotIdExp(e.loc, t, id); else if (sym) { - if (e.ident == Id.hasMember) - { - if (auto sm = sym.search(e.loc, id)) - return True(); - } ex = new DsymbolExp(e.loc, sym); ex = new DotIdExp(e.loc, ex, id); } @@ -966,7 +975,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.error("invalid first argument"); return ErrorExp.get(); } - + doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; diff --git a/tests/dmd/compilable/test23951.d b/tests/dmd/compilable/test23951.d new file mode 100644 index 00000000000..e09a3d7b5d1 --- /dev/null +++ b/tests/dmd/compilable/test23951.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23951 + +struct S { int x; } +struct T { S a; alias a this; } +struct U { T t; } +static assert(__traits(hasMember, T, "x")); +static assert(__traits(hasMember, T.init, "x")); +static assert(__traits(hasMember, U.init.t, "x")); +static assert(__traits(hasMember, U.t, "a")); +static assert(__traits(hasMember, U.t, "x")); diff --git a/tests/dmd/fail_compilation/test23279.d b/tests/dmd/fail_compilation/test23279.d new file mode 100644 index 00000000000..43f2d44a071 --- /dev/null +++ b/tests/dmd/fail_compilation/test23279.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23279 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23279.d(13): Error: undefined identifier `Sth` +--- +*/ + +class Tester +{ + enum a = __traits(hasMember, Tester, "setIt"); + void setIt(Sth sth){} +} From 8f3edb527f5e268a215a22d6e880db918223c92a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Jul 2023 19:08:54 +0200 Subject: [PATCH 224/301] Bump LDC version / frontend version / Phobos submodule --- CMakeLists.txt | 6 +++--- runtime/phobos | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e07b0a0e70b..9c3dddeb6d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,10 +111,10 @@ include(GetLinuxDistribution) # # Version information -set(LDC_VERSION "1.33.0") # May be overridden by git hash tag +set(LDC_VERSION "1.34.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) -set(DMDFE_MINOR_VERSION 103) -set(DMDFE_PATCH_VERSION 1) +set(DMDFE_MINOR_VERSION 104) +set(DMDFE_PATCH_VERSION 2) set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION}) diff --git a/runtime/phobos b/runtime/phobos index f4961356a33..f9112228cac 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit f4961356a33849f24557a77bdc386eff852bb9f5 +Subproject commit f9112228cac41b64a42b4720cc821ba6189ee88b From 1adc890c0f8ad819633f5c42dfd1e7117c2fea4b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Jul 2023 19:24:32 +0200 Subject: [PATCH 225/301] Fix little upstream C++ header regression --- dmd/expression.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/expression.h b/dmd/expression.h index 0efcb490097..35ec908b5a5 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -988,6 +988,7 @@ class SliceExp final : public UnaExp private: uint8_t bitFields; +public: SliceExp *syntaxCopy() override; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; From 8151a5f65cbb10df1c736341f2cb0dfabb8a9578 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Jul 2023 19:25:11 +0200 Subject: [PATCH 226/301] Merge fixes to get compiler to compile --- dmd/ldcbindings.d | 2 +- dmd/statement.d | 2 -- dmd/target.d | 5 +++++ driver/timetrace_sema.d | 2 +- gen/pgo_ASTbased.cpp | 2 +- gen/toir.cpp | 4 ++-- tests/dmd/fail_compilation/diag15974.d | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dmd/ldcbindings.d b/dmd/ldcbindings.d index 018f9874304..e68aee435dd 100644 --- a/dmd/ldcbindings.d +++ b/dmd/ldcbindings.d @@ -80,6 +80,6 @@ Expression createExpressionForIntOp(const ref Loc loc, TOK op, Expression e1, Ex } } -Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op, __traits(classInstanceSize, Expression)); } +Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op); } DsymbolExp createDsymbolExp(const ref Loc loc, Dsymbol s) { return new DsymbolExp(loc, s, /*hasOverloads=*/false); } AddrExp createAddrExp(const ref Loc loc, Expression e) { return new AddrExp(loc, e); } diff --git a/dmd/statement.d b/dmd/statement.d index 42b57fa9e3f..50f1a9a745f 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -383,8 +383,6 @@ version (IN_LLVM) inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } - version (IN_LLVM) - inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } diff --git a/dmd/target.d b/dmd/target.d index 8db1f753ef7..bcbe11fd0cd 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -91,6 +91,9 @@ ubyte defaultTargetOSMajor() return 0; } +version (IN_LLVM) {} else +{ + /** * Add default `version` identifier for dmd, and set the * target platform in `params`. @@ -275,6 +278,8 @@ void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) } } +} // !IN_LLVM + //////////////////////////////////////////////////////////////////////////////// /** * Describes a back-end target. At present it is incomplete, but in the future diff --git a/driver/timetrace_sema.d b/driver/timetrace_sema.d index 11be682c34b..ab7e10bbd7a 100644 --- a/driver/timetrace_sema.d +++ b/driver/timetrace_sema.d @@ -131,7 +131,7 @@ extern(C++) final class SemanticTimeTraceVisitor(SemaVisitor) : Visitor override void visit(StaticForeachDeclaration sfd) { semavisitor.visit(sfd); } - override void visit(CompileDeclaration cd) { semavisitor.visit(cd); } + override void visit(MixinDeclaration md) { semavisitor.visit(md); } override void visit(CPPNamespaceDeclaration ns) { semavisitor.visit(ns); } diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index 7819d9594f1..299d8be0ba3 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -190,7 +190,7 @@ struct MapRegionCounters : public StoppableVisitor { void visit(ScopeStatement *) override {} void visit(ReturnStatement *) override {} void visit(StaticAssertStatement *) override {} - void visit(CompileStatement *) override {} + void visit(MixinStatement *) override {} void visit(ScopeGuardStatement *) override {} void visit(ConditionalStatement *) override {} void visit(StaticForeachStatement *) override {} diff --git a/gen/toir.cpp b/gen/toir.cpp index 265a12a35bf..69f8b31faa6 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -1196,8 +1196,8 @@ class ToElemVisitor : public Visitor { p->arrays.pop_back(); const bool hasLength = etype->ty != TY::Tpointer; - const bool needCheckUpper = hasLength && !e->upperIsInBounds; - const bool needCheckLower = !e->lowerIsLessThanUpper; + const bool needCheckUpper = hasLength && !e->upperIsInBounds(); + const bool needCheckLower = !e->lowerIsLessThanUpper(); if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) { llvm::BasicBlock *okbb = p->insertBB("bounds.ok"); llvm::BasicBlock *failbb = p->insertBBAfter(okbb, "bounds.fail"); diff --git a/tests/dmd/fail_compilation/diag15974.d b/tests/dmd/fail_compilation/diag15974.d index 03f63f4c21a..1967bb1e4f2 100644 --- a/tests/dmd/fail_compilation/diag15974.d +++ b/tests/dmd/fail_compilation/diag15974.d @@ -22,7 +22,7 @@ void test15974() struct S { - // CompileDeclaration + // MixinDeclaration mixin(format("%s", f)); } } From a6dc3fa73a5fe5c16f55b8788df6da4e978450e6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 11:38:20 +0200 Subject: [PATCH 227/301] Adapt to new _d_arraysetlengthT template lowering --- gen/arrays.cpp | 28 ------------------- gen/arrays.h | 2 -- gen/runtime.cpp | 6 ---- gen/toir.cpp | 19 ++++++------- .../src/core/internal/array/capacity.d | 2 +- tests/semantic/dcompute.d | 2 +- 6 files changed, 10 insertions(+), 49 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 14b1b26614d..f4e43eceea9 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -748,34 +748,6 @@ DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, return getSlice(arrayType, newptr); } -//////////////////////////////////////////////////////////////////////////////// -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - LLValue *newdim) { - IF_LOG Logger::println("DtoResizeDynArray : %s", arrayType->toChars()); - LOG_SCOPE; - - assert(array); - assert(newdim); - assert(arrayType); - assert(arrayType->toBasetype()->ty == TY::Tarray); - - // decide on what runtime function to call based on whether the type is zero - // initialized - bool zeroInit = arrayType->toBasetype()->nextOf()->isZeroInit(); - - // call runtime - LLFunction *fn = - getRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT" - : "_d_arraysetlengthiT"); - - LLValue *newArray = gIR->CreateCallOrInvoke( - fn, DtoTypeInfoOf(loc, arrayType), newdim, - DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), - ".gc_mem"); - - return getSlice(arrayType, newArray); -} - //////////////////////////////////////////////////////////////////////////////// static LLValue *DtoSlicePtr(DValue *dval) { diff --git a/gen/arrays.h b/gen/arrays.h index 8ea6e5ccfbc..3e893bbdc79 100644 --- a/gen/arrays.h +++ b/gen/arrays.h @@ -64,8 +64,6 @@ DSliceValue *DtoNewDynArray(const Loc &loc, Type *arrayType, DValue *dim, bool defaultInit = true); DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, DValue **dims, size_t ndims); -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - llvm::Value *newdim); DSliceValue *DtoCatArrays(const Loc &loc, Type *type, Expression *e1, Expression *e2); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 37cd6dc7419..8f491df3679 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -606,12 +606,6 @@ static void buildRuntimeModule() { createFwdDecl(LINK::c, voidArrayTy, {"_d_newarraymTX", "_d_newarraymiTX"}, {typeInfoTy, sizeTy->arrayOf()}, {STCconst, 0}); - // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p) - // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) - createFwdDecl(LINK::c, voidArrayTy, - {"_d_arraysetlengthT", "_d_arraysetlengthiT"}, - {typeInfoTy, sizeTy, voidArrayPtrTy}, {STCconst, 0, 0}); - // void[] _d_arrayappendcd(ref byte[] x, dchar c) // void[] _d_arrayappendwd(ref byte[] x, dchar c) createFwdDecl(LINK::c, voidArrayTy, {"_d_arrayappendcd", "_d_arrayappendwd"}, diff --git a/gen/toir.cpp b/gen/toir.cpp index 69f8b31faa6..fd55bc8601d 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -456,6 +456,14 @@ class ToElemVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////////// + void visit(LoweredAssignExp *e) override { + IF_LOG Logger::print("LoweredAssignExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toElem(e->lowering); + } + void visit(AssignExp *e) override { IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", e->toChars(), e->type->toChars(), @@ -463,17 +471,6 @@ class ToElemVisitor : public Visitor { e->e2->type ? e->e2->type->toChars() : nullptr); LOG_SCOPE; - if (auto ale = e->e1->isArrayLengthExp()) { - Logger::println("performing array.length assignment"); - DLValue arrval(ale->e1->type, DtoLVal(ale->e1)); - DValue *newlen = toElem(e->e2); - DSliceValue *slice = - DtoResizeDynArray(e->loc, arrval.type, &arrval, DtoRVal(newlen)); - DtoStore(DtoRVal(slice), DtoLVal(&arrval)); - result = newlen; - return; - } - // Initialization of ref variable? // Can't just override ConstructExp::toElem because not all EXP::construct // operations are actually instances of ConstructExp... Long live the DMD diff --git a/runtime/druntime/src/core/internal/array/capacity.d b/runtime/druntime/src/core/internal/array/capacity.d index 254e9501f63..10ce2c65c95 100644 --- a/runtime/druntime/src/core/internal/array/capacity.d +++ b/runtime/druntime/src/core/internal/array/capacity.d @@ -36,7 +36,7 @@ template _d_arraysetlengthTImpl(Tarr : T[], T) */ size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted pure nothrow { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); diff --git a/tests/semantic/dcompute.d b/tests/semantic/dcompute.d index 44247e71f67..1bde310b170 100644 --- a/tests/semantic/dcompute.d +++ b/tests/semantic/dcompute.d @@ -29,7 +29,7 @@ void func() //CHECK: dcompute.d([[@LINE+1]]): Error: {{.*}} interfaces and classes not allowed in `@compute` code C cc; int[] quux; - //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code + //CHECK: dcompute.d([[@LINE+1]]): Error: setting `length` in `@compute` code not allowed quux.length = 1; //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code quux ~= 42; From c20e6272b4daab78034c62ad916ab9c540cd9ae3 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 11:39:02 +0200 Subject: [PATCH 228/301] druntime: Fix `in` deprecations in LDC-specific modules --- runtime/druntime/src/ldc/eh_msvc.d | 2 +- runtime/druntime/src/rt/sections_ldc.d | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/druntime/src/ldc/eh_msvc.d b/runtime/druntime/src/ldc/eh_msvc.d index 9ea66485e7c..a19079c9233 100644 --- a/runtime/druntime/src/ldc/eh_msvc.d +++ b/runtime/druntime/src/ldc/eh_msvc.d @@ -99,7 +99,7 @@ struct CxxExceptionInfo extern(C) int _d_isbaseof(ClassInfo oc, ClassInfo c); // error and exit -extern(C) void fatalerror(in char* format, ...) +extern(C) void fatalerror(const(char)* format, ...) { import core.stdc.stdarg; import core.stdc.stdio; diff --git a/runtime/druntime/src/rt/sections_ldc.d b/runtime/druntime/src/rt/sections_ldc.d index 5ee022bcf05..abeffadde3b 100644 --- a/runtime/druntime/src/rt/sections_ldc.d +++ b/runtime/druntime/src/rt/sections_ldc.d @@ -100,7 +100,7 @@ private * Scan segments in Linux dl_phdr_info struct and store * the TLS and writeable data segments in *pdso. */ - void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc + void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc { foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) { @@ -153,7 +153,7 @@ private return dl_iterate_phdr(&callback, &dg) != 0; } - bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc + bool findSegmentForAddr(const scope ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc { if (addr < cast(void*)info.dlpi_addr) // quick reject return false; From 8e60686bdd264f614a5ad2911729ac7f2eaa4104 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 13:07:34 +0200 Subject: [PATCH 229/301] Fix DMD-style inline asm label mangling --- gen/asmstmt.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 48d8342bf9c..7ca35acc0ea 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -501,9 +501,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { { FuncDeclaration *fd = gIR->func()->decl; - OutBuffer mangleBuf; - mangleToBuffer(fd, &mangleBuf); - const char *fdmangle = mangleBuf.peekChars(); + const char *fdmangle = mangleExact(fd); // we use a simple static counter to make sure the new end labels are // unique From 67885a8ef7f0dcb1f7e58b5c2d9737fceb0b6042 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 13:26:32 +0200 Subject: [PATCH 230/301] Adapt to new _d_arraycatnTX template lowering --- gen/arrays.cpp | 99 ------------------------------------------------- gen/runtime.cpp | 10 ----- gen/toir.cpp | 8 +++- 3 files changed, 7 insertions(+), 110 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index f4e43eceea9..16abb3c9aca 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -750,105 +750,6 @@ DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, //////////////////////////////////////////////////////////////////////////////// -static LLValue *DtoSlicePtr(DValue *dval) { - Loc loc; - Type *vt = dval->type->toBasetype(); - if (vt->ty == TY::Tarray) { - return makeLValue(loc, dval); - } - - bool isStaticArray = vt->ty == TY::Tsarray; - LLValue *val = isStaticArray ? DtoLVal(dval) : makeLValue(loc, dval); - LLStructType *i8arrty = DtoArrayType(LLType::getInt8Ty(gIR->context())); - LLValue *array = DtoRawAlloca(i8arrty, 0, ".array"); - LLValue *len = isStaticArray ? DtoArrayLen(dval) : DtoConstSize_t(1); - DtoStore(len, DtoGEP(i8arrty, array, 0u, 0)); - DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEP(i8arrty, array, 0, 1)); - return array; -} - -static llvm::StructType *DtoSlicePtrType(DValue *dval) { - if(dval->type->toBasetype()->ty == TY::Tarray) - return isaStruct(DtoType(dval->type->toBasetype())); - else - return DtoArrayType(LLType::getInt8Ty(gIR->context())); -} - -static LLValue *DtoSlicePtr(Expression *e) { - return DtoSlicePtr(toElem(e)); -} - -DSliceValue *DtoCatArrays(const Loc &loc, Type *arrayType, Expression *exp1, - Expression *exp2) { - IF_LOG Logger::println("DtoCatArrays"); - LOG_SCOPE; - - llvm::SmallVector args; - LLFunction *fn = nullptr; - - if (auto ce = exp1->isCatExp()) { // handle multiple concat - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatnTX"); - - // Create array of slices - typedef llvm::SmallVector ArgVector; - ArgVector arrs; - DValue * dval = toElem(exp2); - arrs.push_back(DtoSlicePtr(dval)); - do { - arrs.push_back(DtoSlicePtr(ce->e2)); - ce = static_cast(ce->e1); - } while (ce->op == EXP::concatenate); - arrs.push_back(DtoSlicePtr(ce)); - - // Create static array from slices - LLPointerType *ptrarraytype = isaPointer(arrs[0]); - assert(ptrarraytype && "Expected pointer type"); - LLStructType *arraytype = DtoSlicePtrType(dval); - assert(arraytype && "Expected struct type"); - LLArrayType *type = LLArrayType::get(arraytype, arrs.size()); - LLValue *array = DtoRawAlloca(type, 0, ".slicearray"); - unsigned int i = 0; - for (ArgVector::reverse_iterator I = arrs.rbegin(), E = arrs.rend(); I != E; - ++I) { - LLValue *v = DtoLoad(arraytype, DtoBitCast(*I, ptrarraytype)); - DtoStore(v, DtoGEP(type, array, 0, i++, ".slice")); - } - - LLStructType *type2 = DtoArrayType(arraytype); - LLValue *array2 = DtoRawAlloca(type2, 0, ".array"); - DtoStore(DtoConstSize_t(arrs.size()), DtoGEP(type2, array2, 0u, 0, ".len")); - DtoStore(DtoBitCast(array, ptrarraytype), DtoGEP(type2, array2, 0, 1, ".ptr")); - LLType *bytearrarr = DtoArrayType(DtoArrayType(LLType::getInt8Ty(gIR->context()))); - LLType *pbytearrarr = getPtrToType(bytearrarr); - LLValue *val = DtoLoad(bytearrarr, DtoBitCast(array2, pbytearrarr)); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - // byte[][] arrs - args.push_back(val); - } else { - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatT"); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - - auto loadArray = [fn](Expression* e, int paramTypeIdx) { - DValue * dval = toElem(e); - LLValue *val = DtoLoad(DtoSlicePtrType(dval), DtoSlicePtr(dval)); - return DtoSlicePaint(val, fn->getFunctionType()->getParamType(paramTypeIdx)); - }; - // byte[] x - args.push_back(loadArray(exp1,1)); - // byte[] y - args.push_back(loadArray(exp2,2)); - } - - auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray"); - return getSlice(arrayType, newArray); -} - -//////////////////////////////////////////////////////////////////////////////// - DSliceValue *DtoAppendDChar(const Loc &loc, DValue *arr, Expression *exp, const char *func) { LLValue *valueToAppend = DtoRVal(exp); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 8f491df3679..e145db51e7d 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -73,8 +73,6 @@ static void checkForImplicitGCCall(const Loc &loc, const char *name) { "_d_arrayappendcTX", "_d_arrayappendcd", "_d_arrayappendwd", - "_d_arraycatT", - "_d_arraycatnTX", "_d_arraysetlengthT", "_d_arraysetlengthiT", "_d_assocarrayliteralTX", @@ -611,14 +609,6 @@ static void buildRuntimeModule() { createFwdDecl(LINK::c, voidArrayTy, {"_d_arrayappendcd", "_d_arrayappendwd"}, {voidArrayTy, dcharTy}, {STCref, 0}); - // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatT"}, - {typeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, 0, 0}); - - // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatnTX"}, - {typeInfoTy, voidArrayTy->arrayOf()}, {STCconst, 0}); - // Object _d_newclass(const ClassInfo ci) // Object _d_allocclass(const ClassInfo ci) createFwdDecl(LINK::c, objectTy, {"_d_newclass", "_d_allocclass"}, diff --git a/gen/toir.cpp b/gen/toir.cpp index fd55bc8601d..403ff996d8b 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2158,6 +2158,7 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; + // TODO: still required? if (global.params.betterC) { error( e->loc, @@ -2169,7 +2170,12 @@ class ToElemVisitor : public Visitor { return; } - result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); + if (e->lowering) { + result = toElem(e->lowering); + return; + } + + llvm_unreachable("CatExp should have been lowered"); } ////////////////////////////////////////////////////////////////////////////// From 292ca6bd60d1281b95aa3e40b49174bb42969282 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 15:03:26 +0200 Subject: [PATCH 231/301] druntime: Fix little core.thread regression for non-x86[_64] targets --- runtime/druntime/src/core/thread/types.d | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runtime/druntime/src/core/thread/types.d b/runtime/druntime/src/core/thread/types.d index 998f610c255..991299b808d 100644 --- a/runtime/druntime/src/core/thread/types.d +++ b/runtime/druntime/src/core/thread/types.d @@ -39,6 +39,14 @@ version (GNU) else enum isStackGrowingDown = false; } +else version (LDC) +{ + // The only LLVM targets as of LLVM 16 with stack growing *upwards* are + // apparently NVPTX and AMDGPU, both without druntime support. + // Note that there's an analogous `version = StackGrowsDown` in + // core.thread.fiber. + enum isStackGrowingDown = true; +} else { version (X86) enum isStackGrowingDown = true; From 9b2107927cf219619c9194b4b29210bc5164a20f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 17:54:32 +0200 Subject: [PATCH 232/301] Support new C variadic kind VarArg.KRvariadic Fixes a few dmd-testsuite regressions, such as runnable/test22342.c. --- dmd/mtype.h | 3 ++- gen/functions.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dmd/mtype.h b/dmd/mtype.h index e6eee337fc5..d48e7388231 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -125,8 +125,9 @@ enum VarArgValues { VARARGnone = 0, /// fixed number of arguments VARARGvariadic = 1, /// T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) - VARARGtypesafe = 2 /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGtypesafe = 2, /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGKRvariadic = 3 /// K+R C style variadics (no function prototype) }; typedef unsigned char VarArg; diff --git a/gen/functions.cpp b/gen/functions.cpp index b9d85c8341d..701cd602ea4 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -173,7 +173,8 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // Non-typesafe variadics (both C and D styles) are also variadics on the LLVM // level. - const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic); + const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic || + f->parameterList.varargs == VARARGKRvariadic); if (isLLVMVariadic && f->linkage == LINK::d) { // Add extra `_arguments` parameter for D-style variadic functions. newIrFty.arg_arguments = From 53e52ccbe4f83e381dc119b2f17e8f69f42689d0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 18:24:12 +0200 Subject: [PATCH 233/301] Fix initialization of new global.compileEnv Fixes some dmd-testsuite regressions, such as fail_compilation/diaginref.d. --- driver/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/driver/main.cpp b/driver/main.cpp index 2b35dee08b9..7683cf24ae4 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1112,6 +1112,10 @@ int cppmain() { fatal(); } + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (opts::fTimeTrace) { initializeTimeTrace(opts::fTimeTraceGranularity, 0, opts::allArguments[0]); } From 30bc9c65c8caf70e74b05a542beeaeb743faea62 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 18:26:23 +0200 Subject: [PATCH 234/301] Disable new dmd-testsuite tests wrt. unsupported -profile=gc --- tests/dmd/compilable/test23874.d | 1 + tests/dmd/runnable/profilegc_stdout.d | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/dmd/compilable/test23874.d b/tests/dmd/compilable/test23874.d index 81ee9d59ec2..09121844ee1 100644 --- a/tests/dmd/compilable/test23874.d +++ b/tests/dmd/compilable/test23874.d @@ -1,5 +1,6 @@ // https://issues.dlang.org/show_bug.cgi?id=23874 // REQUIRED_ARGS: -profile=gc +// DISABLED: LDC // -profile=gc not supported string myToString() { diff --git a/tests/dmd/runnable/profilegc_stdout.d b/tests/dmd/runnable/profilegc_stdout.d index 31f23ce1177..4cf94ed725f 100644 --- a/tests/dmd/runnable/profilegc_stdout.d +++ b/tests/dmd/runnable/profilegc_stdout.d @@ -1,5 +1,6 @@ /* REQUIRED_ARGS: -profile=gc +DISABLED: LDC // -profile=gc not supported RUN_OUTPUT: --- bytes allocated, allocations, type, function, file:line From 49bb2ac590ed575c9d4eb5cbc36b0a15a85796cc Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 18:36:11 +0200 Subject: [PATCH 235/301] Skip ModuleInfo generation for C files (incl. *.i) Fixes dmd-testsuite's compilable/testcomplex.i. --- gen/modules.cpp | 8 +++++--- tests/dmd/runnable/cstuff3.i | 2 ++ tests/dmd/runnable/test23014.i | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/gen/modules.cpp b/gen/modules.cpp index 3372e5c3190..ce32997812a 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -433,9 +433,11 @@ void codegenModule(IRState *irs, Module *m) { // Skip emission of all the additional module metadata if: // a) the -betterC switch is on, - // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), or if - // c) there's no ModuleInfo declaration. - if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo) { + // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), + // c) there's no ModuleInfo declaration, or if + // d) the module is a C file. + if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo && + m->filetype != FileType::c) { // generate ModuleInfo registerModuleInfo(m); } diff --git a/tests/dmd/runnable/cstuff3.i b/tests/dmd/runnable/cstuff3.i index 04a8a95b54a..fd1beb1ae6c 100644 --- a/tests/dmd/runnable/cstuff3.i +++ b/tests/dmd/runnable/cstuff3.i @@ -1,3 +1,5 @@ +// REQUIRED_ARGS: -defaultlib= + # 1 "runnable/extra-files/cstuff3.c" # 1 "" # 1 "" diff --git a/tests/dmd/runnable/test23014.i b/tests/dmd/runnable/test23014.i index 6730085507a..a890f0b42f3 100644 --- a/tests/dmd/runnable/test23014.i +++ b/tests/dmd/runnable/test23014.i @@ -1,4 +1,5 @@ /* EXTRA_SOURCES: imports/imp23014.i + * REQUIRED_ARGS: -defaultlib= */ static _Thread_local int tmp; From 06580b1fc7111022f699b95b9f0d00e7fa5308cd Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 18:39:17 +0200 Subject: [PATCH 236/301] Slightly adapt dmd-testsuite's fail_compilation/fail10968.d for LDC --- tests/dmd/fail_compilation/fail10968.d | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/dmd/fail_compilation/fail10968.d b/tests/dmd/fail_compilation/fail10968.d index a436197d047..3b6c3a0610e 100644 --- a/tests/dmd/fail_compilation/fail10968.d +++ b/tests/dmd/fail_compilation/fail10968.d @@ -8,14 +8,14 @@ fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arraysetassign!(SA[], SA)._d_arraysetassign` -$p:druntime/import/core/internal/array/arrayassign.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` +$p:/core/internal/array/arrayassign.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l` -$p:druntime/import/core/internal/array/arrayassign.d$-mixin-$n$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` +$p:/core/internal/array/arrayassign.d$-mixin-$n$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here @@ -23,14 +23,14 @@ fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` -$p:druntime/import/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` +$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor` -$p:druntime/import/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:druntime/import/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` +$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` --- */ From a3e12d24a8328686a33fb34c88c3c9cd07aa38a9 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 20:12:50 +0200 Subject: [PATCH 237/301] FreeBSD: Restore special cases in dmd-testsuite's compilable/stdcheaders.c --- tests/dmd/compilable/stdcheaders.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index d89bf72fff6..63bbcb88caa 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -12,8 +12,10 @@ #include #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\fenv.h(68): Error: variable `stdcheaders._Fenv1` extern symbols cannot have initializers +#ifndef __FreeBSD__ // /usr/include/fenv.h(341): Error: use `.` for member lookup, not `->` #include #endif +#endif #include #include @@ -66,11 +68,13 @@ #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` #if !(defined(__linux__) && defined(__aarch64__)) // /tmp/clang/lib/clang/15.0.3/include/tgmath.h(34): Error: named parameter required before `...` +#ifndef __FreeBSD__ // /usr/local/llvm15/lib/clang/15.0.7/include/tgmath.h(34): Error: named parameter required before `...` #include #endif #endif #endif #endif +#endif #ifndef __DMC__ #ifndef __linux__ From cfb229b2e0bf0c83f2cfcf950360ea96ae40d4e5 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Jul 2023 20:23:39 +0200 Subject: [PATCH 238/301] druntime: Restrict more `pragma(inline, false)` kludges to DMD only --- runtime/druntime/src/core/demangle.d | 12 ++++++------ runtime/druntime/src/core/internal/array/appending.d | 4 ++-- .../druntime/src/core/internal/array/construction.d | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index b33ee3937e6..2eece8b8863 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -127,7 +127,7 @@ pure @safe: void putComma(size_t n) { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); if (n) put(", "); } @@ -2868,7 +2868,7 @@ private class OverflowException : Exception /// Ditto private noreturn error(string msg = "Invalid symbol") @trusted pure { - pragma(inline, false); // tame dmd inliner + version (DigitalMars) pragma(inline, false); // tame dmd inliner //throw new ParseException( msg ); debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); @@ -2879,7 +2879,7 @@ private noreturn error(string msg = "Invalid symbol") @trusted pure /// Ditto private noreturn overflow(string msg = "Buffer overflow") @trusted pure { - pragma(inline, false); // tame dmd inliner + version (DigitalMars) pragma(inline, false); // tame dmd inliner //throw new OverflowException( msg ); debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); @@ -2934,7 +2934,7 @@ private struct Buffer // move val to the end of the dst buffer char[] shift(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner + version (DigitalMars) pragma(inline, false); // tame dmd inliner if (val.length) { @@ -2956,7 +2956,7 @@ private struct Buffer // remove val from dst buffer void remove(scope const(char)[] val) scope { - pragma(inline, false); // tame dmd inliner + version (DigitalMars) pragma(inline, false); // tame dmd inliner if ( val.length ) { @@ -2972,7 +2972,7 @@ private struct Buffer char[] append(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner + version (DigitalMars) pragma(inline, false); // tame dmd inliner if (val.length) { diff --git a/runtime/druntime/src/core/internal/array/appending.d b/runtime/druntime/src/core/internal/array/appending.d index b609167eefe..bb24813ae9e 100644 --- a/runtime/druntime/src/core/internal/array/appending.d +++ b/runtime/druntime/src/core/internal/array/appending.d @@ -35,7 +35,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow { // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718 - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); @@ -70,7 +70,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) /// Implementation of `_d_arrayappendT` ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.stdc.string : memcpy; import core.internal.traits : hasElaborateCopyConstructor, Unqual; diff --git a/runtime/druntime/src/core/internal/array/construction.d b/runtime/druntime/src/core/internal/array/construction.d index ae71f513129..25083597761 100644 --- a/runtime/druntime/src/core/internal/array/construction.d +++ b/runtime/druntime/src/core/internal/array/construction.d @@ -36,7 +36,7 @@ import core.internal.traits : Unqual; */ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.internal.traits : hasElaborateCopyConstructor; import core.lifetime : copyEmplace; import core.stdc.string : memcpy; @@ -200,7 +200,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma */ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.lifetime : copyEmplace; size_t i; From fde0bf8295375917690b2fe42f3134a69a2bcf7e Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Wed, 19 Jul 2023 23:54:20 +0200 Subject: [PATCH 239/301] Fix symbol visibility issues on Linux for plugins --- CMakeLists.txt | 6 +++++- cmake/Modules/BuildDExecutable.cmake | 5 +++-- tests/plugins/lit.local.cfg | 11 +++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e07b0a0e70b..eac804dac71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,8 +254,9 @@ if(NOT WIN32 AND NOT CYGWIN) # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by # different translation units being compiled with different visibility settings." # See LLVM's cmake/modules/HandleLLVMOptions.cmake. + # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) - if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) + if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} AND NOT LDC_ENABLE_PLUGINS) append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) endif() endif() @@ -683,10 +684,13 @@ if(LDC_ENABLE_PLUGINS) if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic") + else() + message(WARNING "Linker does not accept --export-dynamic, user plugins may give missing symbol errors upon load") endif() endif() endif() message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}") +message(STATUS "-- Linking LDC with flags: ${ALTERNATIVE_MALLOC_O};${LDC_LINKERFLAG_LIST}") build_d_executable( "${LDC_EXE}" diff --git a/cmake/Modules/BuildDExecutable.cmake b/cmake/Modules/BuildDExecutable.cmake index 32101dafd5c..4440fe678f9 100644 --- a/cmake/Modules/BuildDExecutable.cmake +++ b/cmake/Modules/BuildDExecutable.cmake @@ -40,7 +40,8 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin # Compile all D modules to a single object. set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}${CMAKE_CXX_OUTPUT_EXTENSION}) # Default to -linkonce-templates with LDMD host compiler, to speed-up optimization. - if("${D_COMPILER_ID}" STREQUAL "LDMD") + # Note: for plugin support we need the symbols to be global, don't use -linkonce-templates. + if("${D_COMPILER_ID}" STREQUAL "LDMD" AND NOT LDC_ENABLE_PLUGINS) set(dflags -linkonce-templates ${dflags}) endif() add_custom_command( @@ -105,7 +106,7 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin add_custom_command( OUTPUT ${output_exe} - COMMAND ${D_COMPILER} ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} + COMMAND ${D_COMPILER} ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} -fvisibility=public WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} DEPENDS ${target_name}_d_objects ${object_files} ${link_deps} ) diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 4d4c70576e8..568b04db77f 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -24,17 +24,16 @@ if (config.plugins_supported): config.substitutions.append( ('%plugin_compile_flags', " ".join(plugin_compile_flags) ) ) # Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler - # Be conservative: only set it when host LDC and just-built LDC have identical versions + # For our tets, the required ABI compatibility is OK since LDC 1.18 (interface ptr). + # If the compiler is built not by LDC but another compiler, then assume the ABI to be incompatible. command = [config.ldc2_bin, '--version'] p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)" text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" - m = re.compile('LDC - the LLVM D compiler \((.*)\)').match(text1) - ldc_version = m.group(1) - m3 = re.compile(' built with.* \((.*)\)').match(text3) - host_version = m3.group(1) - if (ldc_version == host_version): + host_version = re.compile(' built with LDC.* \((1\.[0-9]+).*\)').match(text3).group(1) + print(host_version) + if (float(host_version) > 1.1799): config.available_features.add('ABI_compatible_with_host_D') From ed429d4524d886073bb956417a4470ef917d9fff Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 00:17:54 +0200 Subject: [PATCH 240/301] Silence unused variable warning when assert is disabled. [NFC] (#4446) --- gen/llvmhelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index a7e2c2f8c31..46488f5d903 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1188,7 +1188,7 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) { val = llvm::ConstantArray::get(at, elements); } - (void)numTotalVals; + (void)numTotalVals; (void) product; // Silence unused variable warning when assert is disabled. assert(product == numTotalVals); return val; } From 4256475c2391cebff0f22cbf25c81b4508636e0e Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 00:25:02 +0200 Subject: [PATCH 241/301] Reorder CMakeLists such that LDC_ENABLE_PLUGINS is available when adding -fvisibility-inlines-hidden --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eac804dac71..29deed4428f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,18 +249,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) endif() endif() -if(NOT WIN32 AND NOT CYGWIN) - # Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global - # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by - # different translation units being compiled with different visibility settings." - # See LLVM's cmake/modules/HandleLLVMOptions.cmake. - # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. - check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) - if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} AND NOT LDC_ENABLE_PLUGINS) - append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) - endif() -endif() - if(MSVC) # Remove flags here, for exceptions and RTTI. # CL.EXE complains to override flags like "/GR /GR-". @@ -692,6 +680,18 @@ endif() message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}") message(STATUS "-- Linking LDC with flags: ${ALTERNATIVE_MALLOC_O};${LDC_LINKERFLAG_LIST}") +if(NOT WIN32 AND NOT CYGWIN) + # Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global + # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by + # different translation units being compiled with different visibility settings." + # See LLVM's cmake/modules/HandleLLVMOptions.cmake. + # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. + check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) + if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} AND NOT LDC_ENABLE_PLUGINS) + append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) + endif() +endif() + build_d_executable( "${LDC_EXE}" "${LDC_EXE_FULL}" From 86e677a1aed95d79b013f39e23ceb0cd6524852c Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 00:39:11 +0200 Subject: [PATCH 242/301] fixup remnant from testing --- cmake/Modules/BuildDExecutable.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/BuildDExecutable.cmake b/cmake/Modules/BuildDExecutable.cmake index 4440fe678f9..7880f209462 100644 --- a/cmake/Modules/BuildDExecutable.cmake +++ b/cmake/Modules/BuildDExecutable.cmake @@ -106,7 +106,7 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin add_custom_command( OUTPUT ${output_exe} - COMMAND ${D_COMPILER} ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} -fvisibility=public + COMMAND ${D_COMPILER} ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} DEPENDS ${target_name}_d_objects ${object_files} ${link_deps} ) From e566bdb4ad73b0123cd726978e3b526cd97c05c0 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 01:11:10 +0200 Subject: [PATCH 243/301] fixup host version check --- tests/plugins/lit.local.cfg | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 568b04db77f..2e0d18046d5 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -31,9 +31,8 @@ if (config.plugins_supported): text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)" text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" - host_version = re.compile(' built with LDC.* \((1\.[0-9]+).*\)').match(text3).group(1) - print(host_version) - if (float(host_version) > 1.1799): + host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3).group(1) + if (int(host_version) >= 18): config.available_features.add('ABI_compatible_with_host_D') From 582ebec9cf85f4e4ffb8f27124053855acaa5d9b Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 10:37:27 +0200 Subject: [PATCH 244/301] fix python when compiler is not LDC --- tests/plugins/lit.local.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 2e0d18046d5..6b869a60cda 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -31,8 +31,8 @@ if (config.plugins_supported): text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)" text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" - host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3).group(1) - if (int(host_version) >= 18): + host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3) + if (host_version and int(host_version.group(1)) >= 18): config.available_features.add('ABI_compatible_with_host_D') From f7dbb671fb2473407dc101eea7a19424744c643b Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 12:10:23 +0200 Subject: [PATCH 245/301] disable plugin test for macOS x86(-64) --- tests/plugins/basic_sema_plugin.d | 3 +++ tests/plugins/visitor_example.d | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d index 1cf010a8498..02c362053d3 100644 --- a/tests/plugins/basic_sema_plugin.d +++ b/tests/plugins/basic_sema_plugin.d @@ -1,5 +1,8 @@ // REQUIRES: Plugins +// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86 (but not for all CI testers...) +// UNSUPPORTED: Darwin && host_X86 + // RUN: split-file %s %t --leading-lines // RUN: %ldc %t/plugin.d %plugin_compile_flags -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index e96ed0252f1..8c783d39f31 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -1,6 +1,9 @@ // REQUIRES: Plugins // REQUIRES: ABI_compatible_with_host_D +// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86. +// UNSUPPORTED: Darwin && host_X86 + // RUN: split-file %s %t --leading-lines // RUN: %ldc -g %t/plugin.d %plugin_compile_flags -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d From 82b365ec442bd4e24f316df1cc2d99be0719a9bd Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 18:53:56 +0200 Subject: [PATCH 246/301] Increase plugin ABI LDC version. (1.27.1 did not work on my Mac, but 1.30 does work) --- tests/plugins/lit.local.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 6b869a60cda..577ba4cd18a 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -24,7 +24,7 @@ if (config.plugins_supported): config.substitutions.append( ('%plugin_compile_flags', " ".join(plugin_compile_flags) ) ) # Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler - # For our tets, the required ABI compatibility is OK since LDC 1.18 (interface ptr). + # For our tets, the required ABI compatibility seems OK since at least LDC 1.30. # If the compiler is built not by LDC but another compiler, then assume the ABI to be incompatible. command = [config.ldc2_bin, '--version'] p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) @@ -32,7 +32,7 @@ if (config.plugins_supported): text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3) - if (host_version and int(host_version.group(1)) >= 18): + if (host_version and int(host_version.group(1)) >= 30): # 30 = LDC 1.30 config.available_features.add('ABI_compatible_with_host_D') From c3b56d7cc1f4249991e0477b7e338451754e7221 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 20 Jul 2023 19:31:29 +0200 Subject: [PATCH 247/301] Add changelog entry, and fix order of plugins vs dcompute. --- CHANGELOG.md | 1 + gen/semantic.d | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6e4e252e8..701fb7528ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) +- The `--plugin` commandline option now also accepts semantic analysis plugins. Semantic analysis plugins are recognized by exporting the symbol: `extern(C) void runSemanticAnalysis(Module m)`. The plugin's `runSemanticAnalysis` function is called for each module, after all other semantic analysis steps (also after DCompute SemA), just before object codegen. (#4430) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). diff --git a/gen/semantic.d b/gen/semantic.d index 9ea4f454972..c818d7b7e09 100644 --- a/gen/semantic.d +++ b/gen/semantic.d @@ -19,12 +19,16 @@ extern(C++) void runAllSemanticAnalysisPlugins(Module m); extern(C++) void extraLDCSpecificSemanticAnalysis(ref Modules modules) { + // First finish DCompute SemA for all modules, before calling plugins. foreach(m; modules[]) { if (hasComputeAttr(m)) { dcomputeSemanticAnalysis(m); } + } + foreach(m; modules[]) + { runAllSemanticAnalysisPlugins(m); } } From e03b08459f0e0d297f51bea71d617409f04a7300 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 21 Jul 2023 22:17:01 +0200 Subject: [PATCH 248/301] Add -fcf-protection. (#4437) --- CHANGELOG.md | 1 + driver/cl_options_instrumentation.cpp | 21 ++++++++++++++++++ driver/cl_options_instrumentation.h | 3 +++ gen/modules.cpp | 21 ++++++++++++++++++ gen/target.cpp | 6 ++++++ tests/codegen/fcf_protection.d | 31 +++++++++++++++++++++++++++ 6 files changed, 83 insertions(+) create mode 100644 tests/codegen/fcf_protection.d diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6e4e252e8..fe4c90a9755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). - Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420) +- New command-line option `--fcf-protection`, which enables Intel's Control-Flow Enforcement Technology (CET). (#4437) #### Platform support diff --git a/driver/cl_options_instrumentation.cpp b/driver/cl_options_instrumentation.cpp index 00fe6031d4c..ea00e7d130c 100644 --- a/driver/cl_options_instrumentation.cpp +++ b/driver/cl_options_instrumentation.cpp @@ -84,6 +84,19 @@ llvm::StringRef getXRayInstructionThresholdString() { return thresholdString; } +cl::opt fCFProtection( + "fcf-protection", + cl::desc("Instrument control-flow architecture protection"), cl::ZeroOrMore, + cl::ValueOptional, + cl::values(clEnumValN(CFProtectionType::None, "none", ""), + clEnumValN(CFProtectionType::Branch, "branch", ""), + clEnumValN(CFProtectionType::Return, "return", ""), + clEnumValN(CFProtectionType::Full, "full", ""), + clEnumValN(CFProtectionType::Full, "", + "") // default to "full" if no argument specified + ), + cl::init(CFProtectionType::None)); + void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) { if (ASTPGOInstrGenFile.getNumOccurrences() > 0) { pgoMode = PGO_ASTBasedInstr; @@ -110,6 +123,14 @@ void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) { if (dmdFunctionTrace) global.params.trace = true; + + // fcf-protection is only valid for X86 + if (fCFProtection != CFProtectionType::None && + !(triple.getArch() == llvm::Triple::x86 || + triple.getArch() == llvm::Triple::x86_64)) { + error(Loc(), "option '--fcf-protection' cannot be specified on this target " + "architecture"); + } } } // namespace opts diff --git a/driver/cl_options_instrumentation.h b/driver/cl_options_instrumentation.h index 50a249c09aa..9b06789b341 100644 --- a/driver/cl_options_instrumentation.h +++ b/driver/cl_options_instrumentation.h @@ -29,6 +29,9 @@ extern cl::opt instrumentFunctions; extern cl::opt fXRayInstrument; llvm::StringRef getXRayInstructionThresholdString(); +enum class CFProtectionType { None = 0, Branch = 1, Return = 2, Full = 3 }; +extern cl::opt fCFProtection; + /// This initializes the instrumentation options, and checks the validity of the /// commandline flags. targetTriple should be initialized before calling this. /// It should be called only once. diff --git a/gen/modules.cpp b/gen/modules.cpp index 3372e5c3190..54412fd1dee 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -393,8 +393,27 @@ void registerModuleInfo(Module *m) { emitModuleRefToSection(mangle, moduleInfoSym); } } + +void addModuleFlags(llvm::Module &m) { +#if LDC_LLVM_VER >= 1500 + const auto ModuleMinFlag = llvm::Module::Min; +#else + const auto ModuleMinFlag = llvm::Module::Warning; // Fallback value +#endif + + if (opts::fCFProtection == opts::CFProtectionType::Return || + opts::fCFProtection == opts::CFProtectionType::Full) { + m.addModuleFlag(ModuleMinFlag, "cf-protection-return", 1); + } + + if (opts::fCFProtection == opts::CFProtectionType::Branch || + opts::fCFProtection == opts::CFProtectionType::Full) { + m.addModuleFlag(ModuleMinFlag, "cf-protection-branch", 1); + } } +} // anonymous namespace + void codegenModule(IRState *irs, Module *m) { TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc); @@ -444,6 +463,8 @@ void codegenModule(IRState *irs, Module *m) { addCoverageAnalysisInitializer(m); } + addModuleFlags(irs->module); + gIR = nullptr; irs->dmodule = nullptr; } diff --git a/gen/target.cpp b/gen/target.cpp index 154adb3676b..6285c9f1ea6 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -15,6 +15,7 @@ #include "dmd/mtype.h" #include "dmd/target.h" #include "driver/cl_options.h" +#include "driver/cl_options_instrumentation.h" #include "driver/linker.h" #include "gen/abi/abi.h" #include "gen/irstate.h" @@ -309,6 +310,11 @@ Expression *Target::getTargetInfo(const char *name_, const Loc &loc) { Loc(), static_cast(global.params.cplusplus), Type::tint32); } + if (name == "CET") { + auto cet = opts::fCFProtection.getValue(); + return IntegerExp::create(loc, static_cast(cet), Type::tint32); + } + #if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX if (name == "dcomputeTargets") { Expressions* exps = createExpressions(); diff --git a/tests/codegen/fcf_protection.d b/tests/codegen/fcf_protection.d new file mode 100644 index 00000000000..e5c28cea2ab --- /dev/null +++ b/tests/codegen/fcf_protection.d @@ -0,0 +1,31 @@ +// Test -fcf-protection + +// REQUIRES: target_X86 + +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t.ll %s -d-version=NOTHING && FileCheck %s --check-prefix=NOTHING < %t.ll + +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_branch.ll %s --fcf-protection=branch -d-version=BRANCH && FileCheck %s --check-prefix=BRANCH < %t_branch.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_return.ll %s --fcf-protection=return -d-version=RETURN && FileCheck %s --check-prefix=RETURN < %t_return.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_full.ll %s --fcf-protection=full -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_full.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_noarg.ll %s --fcf-protection -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_noarg.ll + +// NOTHING-NOT: cf-prot +// BRANCH-DAG: "cf-protection-branch", i32 1 +// RETURN-DAG: "cf-protection-return", i32 1 +// FULL-DAG: "cf-protection-branch", i32 1 +// FULL-DAG: "cf-protection-return", i32 1 + +void foo() {} + +version(NOTHING) { + static assert(__traits(getTargetInfo, "CET") == 0); +} +version(BRANCH) { + static assert(__traits(getTargetInfo, "CET") == 1); +} +version(RETURN) { + static assert(__traits(getTargetInfo, "CET") == 2); +} +version(FULL) { + static assert(__traits(getTargetInfo, "CET") == 3); +} From 234d85952ae7d0fe5a4d355af634a04ab1c07ea9 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 21 Jul 2023 22:17:58 +0200 Subject: [PATCH 249/301] Fix wrong Windows codepage being used in dmd.common.string.toWStringz() (#4450) --- dmd/common/string.d | 8 ++++++++ dmd/root/filename.d | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dmd/common/string.d b/dmd/common/string.d index 1111cec2cf1..2f7230cb92e 100644 --- a/dmd/common/string.d +++ b/dmd/common/string.d @@ -122,8 +122,16 @@ but is guaranteed to follow it. version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow { import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar; + +version (IN_LLVM) +{ + import dmd.root.filename : CodePage; +} +else +{ // assume filenames encoded in system default Windows ANSI code page enum CodePage = CP_ACP; +} if (narrow is null) return null; diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 14cffd265e2..d7842e2463f 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -44,7 +44,7 @@ version (Windows) version (IN_LLVM) { - private enum CodePage = CP_UTF8; + enum CodePage = CP_UTF8; } else { From b0d8748e62b4ce6e85e2089addb4873529e1f60f Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sat, 22 Jul 2023 00:51:08 +0200 Subject: [PATCH 250/301] Add ldc-build-plugin tool, and make use of it in plugins lit tests. --- tests/CMakeLists.txt | 1 + tests/lit.site.cfg.in | 2 + tests/plugins/basic_sema_plugin.d | 2 +- tests/plugins/lit.local.cfg | 10 -- tests/plugins/visitor_example.d | 2 +- tools/CMakeLists.txt | 22 +++ tools/ldc-build-plugin.d.in | 239 ++++++++++++++++++++++++++++++ 7 files changed, 266 insertions(+), 12 deletions(-) create mode 100644 tools/ldc-build-plugin.d.in diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b8879ecbc43..e6ce8521428 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,7 @@ set( LDC2_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_EXE} ) set( LDCPROFDATA_BIN ${PROJECT_BINARY_DIR}/bin/ldc-profdata ) set( LDCPRUNECACHE_BIN ${PROJECT_BINARY_DIR}/bin/${LDCPRUNECACHE_EXE} ) +set( LDCBUILDPLUGIN_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE} ) set( TIMETRACE2TXT_BIN ${PROJECT_BINARY_DIR}/bin/${TIMETRACE2TXT_EXE} ) set( LLVM_TOOLS_DIR ${LLVM_ROOT_DIR}/bin ) set( LDC2_BIN_DIR ${PROJECT_BINARY_DIR}/bin ) diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index db6350e4542..f18b6f4d3d9 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -17,6 +17,7 @@ OFF = False config.ldc2_bin = "@LDC2_BIN@" config.ldcprofdata_bin = "@LDCPROFDATA_BIN@" config.ldcprunecache_bin = "@LDCPRUNECACHE_BIN@" +config.ldcbuildplugin_bin = "@LDCBUILDPLUGIN_BIN@" config.timetrace2txt_bin = "@TIMETRACE2TXT_BIN@" config.ldc2_bin_dir = "@LDC2_BIN_DIR@" config.ldc2_lib_dir = "@LDC2_LIB_DIR@" @@ -157,6 +158,7 @@ config.substitutions.append( ('%ldc', config.ldc2_bin) ) config.substitutions.append( ('%gnu_make', config.gnu_make_bin) ) config.substitutions.append( ('%profdata', config.ldcprofdata_bin) ) config.substitutions.append( ('%prunecache', config.ldcprunecache_bin) ) +config.substitutions.append( ('%buildplugin', config.ldcbuildplugin_bin + " --ldcSrcDir=" + config.ldc2_source_dir ) ) config.substitutions.append( ('%timetrace2txt', config.timetrace2txt_bin) ) config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, 'llvm-spirv')) ) config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) ) diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d index 02c362053d3..6a348387715 100644 --- a/tests/plugins/basic_sema_plugin.d +++ b/tests/plugins/basic_sema_plugin.d @@ -4,7 +4,7 @@ // UNSUPPORTED: Darwin && host_X86 // RUN: split-file %s %t --leading-lines -// RUN: %ldc %t/plugin.d %plugin_compile_flags -of=%t/plugin%so +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d //--- plugin.d diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 577ba4cd18a..eb5b296e587 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -13,16 +13,6 @@ if (config.plugins_supported): config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config') config.environment['LLVM_VERSION'] = str(config.llvm_version) - plugin_compile_flags = [ '-I' + config.ldc2_source_dir, - '--d-version=IN_LLVM', - '-J' + config.ldc2_source_dir + '/dmd/res', - '--shared', - '--defaultlib=', - ] - if (platform.system() == 'Darwin'): - plugin_compile_flags.append('-L-Wl,-undefined,dynamic_lookup') - config.substitutions.append( ('%plugin_compile_flags', " ".join(plugin_compile_flags) ) ) - # Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler # For our tets, the required ABI compatibility seems OK since at least LDC 1.30. # If the compiler is built not by LDC but another compiler, then assume the ABI to be incompatible. diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index 8c783d39f31..ab80791f801 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -5,7 +5,7 @@ // UNSUPPORTED: Darwin && host_X86 // RUN: split-file %s %t --leading-lines -// RUN: %ldc -g %t/plugin.d %plugin_compile_flags -of=%t/plugin%so +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d //--- plugin.d diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 187c95035dd..eb510f3c940 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -60,3 +60,25 @@ build_d_executable( ${COMPILE_D_MODULES_SEPARATELY} ) install(PROGRAMS ${TIMETRACE2TXT_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + +############################################################################# +# Only build ldc-build-plugin tool for platforms where plugins are actually enabled. +if(LDC_ENABLE_PLUGINS) + configure_file(${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in ${PROJECT_BINARY_DIR}/ldc-build-plugin.d @ONLY) + set(LDC_BUILD_PLUGIN_EXE ldc-build-plugin) + set(LDC_BUILD_PLUGIN_EXE ${LDC_BUILD_PLUGIN_EXE} PARENT_SCOPE) # needed for correctly populating lit.site.cfg.in + set(LDC_BUILD_PLUGIN_EXE_NAME ${PROGRAM_PREFIX}${LDC_BUILD_PLUGIN_EXE}${PROGRAM_SUFFIX}) + set(LDC_BUILD_PLUGIN_EXE_FULL ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE_NAME}${CMAKE_EXECUTABLE_SUFFIX}) + build_d_executable( + "${LDC_BUILD_PLUGIN_EXE}" + "${LDC_BUILD_PLUGIN_EXE_FULL}" + "${PROJECT_BINARY_DIR}/ldc-build-plugin.d" + "${DFLAGS_BUILD_TYPE}" + "" + "${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in" + "" + ${COMPILE_D_MODULES_SEPARATELY} + ) + install(PROGRAMS ${LDC_BUILD_PLUGIN_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +endif() + diff --git a/tools/ldc-build-plugin.d.in b/tools/ldc-build-plugin.d.in new file mode 100644 index 00000000000..84fd25ff357 --- /dev/null +++ b/tools/ldc-build-plugin.d.in @@ -0,0 +1,239 @@ +module ldcBuildRuntime; + +import core.stdc.stdlib : exit; +import std.algorithm; +import std.array; +import std.file; +import std.path; +import std.stdio; + +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + +struct Config { + string ldcExecutable; + string buildDir; + string ldcSourceDir; + string[] dFlags; + string[] linkerFlags; + bool verbose; + string[] ldcArgs; + string userWorkDir; +} + +version (Windows) enum exeSuffix = ".exe"; +else enum exeSuffix = ""; + +string defaultLdcExecutable; +Config config; + +int main(string[] args) { + enum exeName = "ldc2" ~ exeSuffix; + defaultLdcExecutable = buildPath(thisExePath.dirName, exeName); + config.userWorkDir = getcwd(); + + parseCommandLine(args); + + findLdcExecutable(); + + prepareBuildDir(); + + prepareLdcSource(); + + build(); + + if (config.verbose) + writefln(".: Plugin library built successfully."); + return 0; +} + +void findLdcExecutable() { + if (config.ldcExecutable !is null) { + if (!config.ldcExecutable.exists) { + writefln(".: Error: LDC executable not found: %s", config.ldcExecutable); + exit(1); + } + config.ldcExecutable = config.ldcExecutable.absolutePath; + return; + } + + if (defaultLdcExecutable.exists) { + config.ldcExecutable = defaultLdcExecutable; + return; + } + + writefln(".: Please specify LDC executable via '--ldc='. Aborting.", exeSuffix); + exit(1); +} + +void prepareBuildDir() { + if (config.buildDir is null) + config.buildDir = "ldc-build-plugin.tmp"; + + if (!config.buildDir.exists) { + if (config.verbose) + writefln(".: Creating build directory: %s", config.buildDir); + mkdirRecurse(config.buildDir); + } + + config.buildDir = config.buildDir.absolutePath; +} + +void prepareLdcSource() { + if (config.ldcSourceDir !is null) { + if (!config.ldcSourceDir.exists) { + writefln(".: Error: LDC source directory not found: %s", config.ldcSourceDir); + exit(1); + } + config.ldcSourceDir = config.ldcSourceDir.absolutePath; + return; + } + + const ldcSrc = "ldc-src"; + config.ldcSourceDir = buildPath(config.buildDir, ldcSrc); + if (buildPath(config.ldcSourceDir, "dmd").exists) + return; + + // Download & extract LDC source archive if /ldc-src/dmd doesn't exist yet. + + const wd = WorkingDirScope(config.buildDir); + + auto ldcVersion = "@LDC_VERSION@"; + void removeVersionSuffix(string beginning) { + const suffixIndex = ldcVersion.countUntil(beginning); + if (suffixIndex > 0) + ldcVersion = ldcVersion[0 .. suffixIndex]; + } + removeVersionSuffix("git-"); + removeVersionSuffix("-dirty"); + + import std.format : format; + const localArchiveFile = "ldc-src.zip"; + if (!localArchiveFile.exists) { + const url = "https://github.com/ldc-developers/ldc/releases/download/v%1$s/ldc-%1$s-src.zip".format(ldcVersion); + writefln(".: Downloading LDC source archive: %s", url); + import std.net.curl : download; + download(url, localArchiveFile); + if (getSize(localArchiveFile) < 1_048_576) { + writefln(".: Error: downloaded file is corrupt; has LDC v%s been released?", ldcVersion); + writefln(" You can work around this by manually downloading a src package and moving it to: %s", + buildPath(config.buildDir, localArchiveFile)); + localArchiveFile.remove; + exit(1); + } + } + + extractZipArchive(localArchiveFile, "."); + rename("ldc-%1$s-src".format(ldcVersion), ldcSrc); +} + +void build() { + string[] args = [ + config.ldcExecutable, + "-I" ~ config.ldcSourceDir, + "--d-version=IN_LLVM", + "-J" ~ buildPath(config.ldcSourceDir, "dmd", "res"), + "--shared", + "--defaultlib=", + "--od=" ~ config.buildDir + ]; + + version (Darwin) { + args ~= "-L-Wl,-undefined,dynamic_lookup"; + } + + args ~= config.ldcArgs; + + exec(args); +} + +/*** helpers ***/ + +struct WorkingDirScope { + string originalPath; + this(string path) { originalPath = getcwd(); chdir(path); } + ~this() { chdir(originalPath); } +} + +void exec(string[] command) { + import std.process; + + static string quoteIfNeeded(string arg) { + const r = arg.findAmong(" ;"); + return !r.length ? arg : "'" ~ arg ~ "'"; + } + string flattened = command.map!quoteIfNeeded.join(" "); + if (config.verbose) { + writefln(".: Invoking: %s", flattened); + stdout.flush(); + } + + auto pid = spawnProcess(command, null, std.process.Config.none, config.userWorkDir); + const exitStatus = wait(pid); + + if (exitStatus != 0) { + if (config.verbose) + writeln(".: Error: command failed with status ", exitStatus); + exit(1); + } +} + +void extractZipArchive(string archivePath, string destination) { + import std.zip; + + auto archive = new ZipArchive(std.file.read(archivePath)); + foreach (name, am; archive.directory) { + const destPath = buildNormalizedPath(destination, name); + + const isDir = name.endsWith("/"); + const destDir = isDir ? destPath : destPath.dirName; + mkdirRecurse(destDir); + + if (!isDir) + std.file.write(destPath, archive.expand(am)); + } +} + +void parseCommandLine(string[] args) { + import std.getopt; + + try { + arraySep = ";"; + auto helpInformation = getopt( + args, + std.getopt.config.passThrough, + "ldc", "Path to LDC executable (default: '" ~ defaultLdcExecutable ~ "')", &config.ldcExecutable, + "buildDir", "Path to build directory (default: './ldc-build-plugin.tmp')", &config.buildDir, + "ldcSrcDir", "Path to LDC source directory (if not specified: downloads & extracts source archive into '/ldc-src')", &config.ldcSourceDir, + "dFlags", "Extra LDC flags for the D module (separated by ';')", &config.dFlags, + "verbose|v", "Verbose output (e.g. showing the compile commandline)", &config.verbose, + "linkerFlags", "Extra C linker flags for shared libraries and testrunner executables (separated by ';')", &config.linkerFlags + ); + + // getopt() has removed all consumed args from `args` + // Remaining arguments are interpreted as LDC arguments (e.g. plugin source files and -of=). + config.ldcArgs = args[1 .. $]; + + if (helpInformation.helpWanted) { + defaultGetoptPrinter( + "OVERVIEW: Builds a Semantic Analysis plugin for LDC.\n\n" ~ + "USAGE: ldc-build-plugin [options] sourcefiles... -of=\n\n" ~ + "OPTIONS:\n" ~ + " Unrecognized options are passed through to LDC.", + helpInformation.options + ); + exit(1); + } + } + catch (Exception e) { + writefln("Error processing command line arguments: %s", e.msg); + writeln("Use '--help' for help."); + exit(1); + } +} From a643e1d65a357ddf0e20e83085d0ac6481dbea2c Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sat, 22 Jul 2023 00:54:51 +0200 Subject: [PATCH 251/301] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 701fb7528ad..75855cbb955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - The `--plugin` commandline option now also accepts semantic analysis plugins. Semantic analysis plugins are recognized by exporting the symbol: `extern(C) void runSemanticAnalysis(Module m)`. The plugin's `runSemanticAnalysis` function is called for each module, after all other semantic analysis steps (also after DCompute SemA), just before object codegen. (#4430) +- New tool `ldc-build-plugin` that helps compiling user plugins. It downloads the correct LDC source version (if its not already available), and calls LDC with the correct commandline flags to build a plugin. (#4430) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). From 2c91ab15226aa159493157b582b5462368eef1d0 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sat, 22 Jul 2023 11:02:13 +0200 Subject: [PATCH 252/301] try to fix CI by running tests multiple times --- tests/plugins/basic_sema_plugin.d | 2 ++ tests/plugins/visitor_example.d | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d index 6a348387715..dd756693621 100644 --- a/tests/plugins/basic_sema_plugin.d +++ b/tests/plugins/basic_sema_plugin.d @@ -3,6 +3,8 @@ // For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86 (but not for all CI testers...) // UNSUPPORTED: Darwin && host_X86 +// ALLOW_RETRIES: 3 + // RUN: split-file %s %t --leading-lines // RUN: %buildplugin %t/plugin.d -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index ab80791f801..cc5e5163525 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -4,6 +4,8 @@ // For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86. // UNSUPPORTED: Darwin && host_X86 +// ALLOW_RETRIES: 3 + // RUN: split-file %s %t --leading-lines // RUN: %buildplugin %t/plugin.d -of=%t/plugin%so // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d From 9212dc43006a07516eccc59a9bd2efb494695410 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 22 Jul 2023 12:45:24 +0200 Subject: [PATCH 253/301] Disable DMD-specific lexer kludge wrt. C `long double` literals Fixing dmd-testsuite's compilable/testcomplex.i for targets with 64-bit real. --- dmd/lexer.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dmd/lexer.d b/dmd/lexer.d index 6e6add1dfbc..b5f6617b99a 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -2601,8 +2601,11 @@ class Lexer goto case 'L'; case 'L': ++p; +version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ } else +{ if (Ccompile && long_doublesize == 8) goto default; +} result = TOK.float80Literal; break; } From a45b0d8601f89546522f7210430c8973d437e18d Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sat, 22 Jul 2023 14:53:27 +0200 Subject: [PATCH 254/301] Add ldc-build-plugin to cross build packaging --- .github/actions/3-build-cross/action.yml | 2 +- .github/actions/5-install/action.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/3-build-cross/action.yml b/.github/actions/3-build-cross/action.yml index f583c3213ae..fb1473281da 100644 --- a/.github/actions/3-build-cross/action.yml +++ b/.github/actions/3-build-cross/action.yml @@ -145,4 +145,4 @@ runs: ${{ inputs.cmake_flags }} ${{ inputs.with_pgo == 'true' && '-DDFLAGS_LDC=-fprofile-use=../pgo-ldc/merged.profdata' || '' }} ${{ env.CROSS_CMAKE_FLAGS }} - build_targets: ldc2 ldmd2 ldc-build-runtime ldc-profdata ldc-prune-cache timetrace2txt + build_targets: ldc2 ldmd2 ldc-build-runtime ldc-build-plugin ldc-profdata ldc-prune-cache timetrace2txt diff --git a/.github/actions/5-install/action.yml b/.github/actions/5-install/action.yml index 4a442af58ac..f9388388db1 100644 --- a/.github/actions/5-install/action.yml +++ b/.github/actions/5-install/action.yml @@ -22,6 +22,7 @@ runs: else mkdir -p install/bin cp build-cross/bin/{ldc2,ldmd2,ldc-build-runtime,ldc-profdata,ldc-prune-cache,timetrace2txt} install/bin/ + cp build-cross/bin/ldc-build-plugin install/bin/ || true cp -R build-cross-libs/lib install/ cp build-cross/lib/{libldc_rt.*,libLTO-ldc.dylib,LLVMgold-ldc.so} install/lib/ || true mkdir install/etc From e3b605ed19ce308d9bddbd49b4df8b0dce8fde63 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sun, 23 Jul 2023 12:45:02 +0200 Subject: [PATCH 255/301] Reduce the buildflag impact, try to only make changes when needed (platform dependent) --- CMakeLists.txt | 5 +++-- cmake/Modules/BuildDExecutable.cmake | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29deed4428f..3617406ff81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -685,9 +685,10 @@ if(NOT WIN32 AND NOT CYGWIN) # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by # different translation units being compiled with different visibility settings." # See LLVM's cmake/modules/HandleLLVMOptions.cmake. - # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) - if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} AND NOT LDC_ENABLE_PLUGINS) + if (UNIX AND LDC_ENABLE_PLUGINS) + # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. On macOS it's OK to add. + elseif (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) endif() endif() diff --git a/cmake/Modules/BuildDExecutable.cmake b/cmake/Modules/BuildDExecutable.cmake index 7880f209462..6318433ea75 100644 --- a/cmake/Modules/BuildDExecutable.cmake +++ b/cmake/Modules/BuildDExecutable.cmake @@ -25,6 +25,7 @@ endmacro() # - DFLAGS_BASE # - LDC_LINK_MANUALLY # - D_LINKER_ARGS +# - LDC_ENABLE_PLUGINS function(build_d_executable target_name output_exe d_src_files compiler_args linker_args extra_compile_deps link_deps compile_separately) set(dflags "${D_COMPILER_FLAGS} ${DFLAGS_BASE} ${compiler_args}") if(UNIX) @@ -40,8 +41,9 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin # Compile all D modules to a single object. set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}${CMAKE_CXX_OUTPUT_EXTENSION}) # Default to -linkonce-templates with LDMD host compiler, to speed-up optimization. - # Note: for plugin support we need the symbols to be global, don't use -linkonce-templates. - if("${D_COMPILER_ID}" STREQUAL "LDMD" AND NOT LDC_ENABLE_PLUGINS) + if("${target_name}" STREQUAL "ldc2" AND LDC_ENABLE_PLUGINS) + # For plugin support we need ldc2's symbols to be global, don't use -linkonce-templates. + elseif("${D_COMPILER_ID}" STREQUAL "LDMD") set(dflags -linkonce-templates ${dflags}) endif() add_custom_command( From 04fa0d5e5652429054241b3f75f81b6bc0228da8 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 18:29:14 +0200 Subject: [PATCH 256/301] [really re-add -fvisibility-inlines-hidden on Apple targets] --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3617406ff81..83a4c0739ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -686,7 +686,7 @@ if(NOT WIN32 AND NOT CYGWIN) # different translation units being compiled with different visibility settings." # See LLVM's cmake/modules/HandleLLVMOptions.cmake. check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) - if (UNIX AND LDC_ENABLE_PLUGINS) + if (LDC_ENABLE_PLUGINS AND NOT APPLE) # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. On macOS it's OK to add. elseif (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) From 64655c22322bc28c18559deee1b6b4f09b6e3d14 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 19:08:51 +0200 Subject: [PATCH 257/301] [fix plugin tests flakiness] --- tests/plugins/basic_sema_plugin.d | 4 +--- tests/plugins/visitor_example.d | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d index dd756693621..873c2584df3 100644 --- a/tests/plugins/basic_sema_plugin.d +++ b/tests/plugins/basic_sema_plugin.d @@ -3,10 +3,8 @@ // For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86 (but not for all CI testers...) // UNSUPPORTED: Darwin && host_X86 -// ALLOW_RETRIES: 3 - // RUN: split-file %s %t --leading-lines -// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d //--- plugin.d diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index cc5e5163525..1b106444e58 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -4,10 +4,8 @@ // For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86. // UNSUPPORTED: Darwin && host_X86 -// ALLOW_RETRIES: 3 - // RUN: split-file %s %t --leading-lines -// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d //--- plugin.d From 94edb02a3aff4e8dc64814c20ef325f367672f78 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 20:53:50 +0200 Subject: [PATCH 258/301] Finalize v1.33.0 changelog --- .cirrus.yml | 2 +- CHANGELOG.md | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 9d9731a04ac..f9d528f0758 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -67,7 +67,7 @@ common_steps_template: &COMMON_STEPS_TEMPLATE excludes+='|^druntime-test-exceptions-debug$' elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then # FIXME: sporadic segfaults/bus errors with enabled optimizations - excludes+='|^core.thread.fiber-shared$' + excludes+='|^core.thread.fiber(-shared)?$' fi ctest -j$PARALLELISM --output-on-failure -E "$excludes" diff --git a/CHANGELOG.md b/CHANGELOG.md index 21345b384e7..b727772eeee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,17 @@ # LDC master +#### Big news + +#### Platform support + +#### Bug fixes + +# LDC 1.33.0 (2023-07-23) + #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) - The `--plugin` commandline option now also accepts semantic analysis plugins. Semantic analysis plugins are recognized by exporting the symbol: `extern(C) void runSemanticAnalysis(Module m)`. The plugin's `runSemanticAnalysis` function is called for each module, after all other semantic analysis steps (also after DCompute SemA), just before object codegen. (#4430) -- New tool `ldc-build-plugin` that helps compiling user plugins. It downloads the correct LDC source version (if its not already available), and calls LDC with the correct commandline flags to build a plugin. (#4430) +- New tool `ldc-build-plugin` that helps compiling user plugins. It downloads the correct LDC source version (if it's not already available), and calls LDC with the correct commandline flags to build a plugin. (#4430) - New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) - C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). @@ -11,6 +19,7 @@ - New command-line option `--fcf-protection`, which enables Intel's Control-Flow Enforcement Technology (CET). (#4437) #### Platform support +- Supports LLVM 9.0 - 15.0. #### Bug fixes - Handle potential lambda mangle collisions across separately compiled object files (and the linker then silently picking an arbitrary implementation). Lambdas (and their nested global variables) are now internal to each referencing object file (`static` linkage in C). (#4415) From 0cd7fc01a6ad4e67f688d5a3b67b8f7f0c270d12 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 22:53:27 +0200 Subject: [PATCH 259/301] Packaging: Bump mimalloc and bundled dlang tools & dub --- packaging/dlang-tools_version | 2 +- packaging/dub_version | 2 +- packaging/mimalloc_version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/dlang-tools_version b/packaging/dlang-tools_version index 4ac687073e6..9a0ba479e62 100644 --- a/packaging/dlang-tools_version +++ b/packaging/dlang-tools_version @@ -1 +1 @@ -v2.103.1 \ No newline at end of file +v2.104.2 \ No newline at end of file diff --git a/packaging/dub_version b/packaging/dub_version index 4beef39c0b7..3fdd57c402b 100644 --- a/packaging/dub_version +++ b/packaging/dub_version @@ -1 +1 @@ -v1.32.1 \ No newline at end of file +v1.33.1 \ No newline at end of file diff --git a/packaging/mimalloc_version b/packaging/mimalloc_version index 01990b444d6..e1fbb880433 100644 --- a/packaging/mimalloc_version +++ b/packaging/mimalloc_version @@ -1 +1 @@ -v1.7.9 \ No newline at end of file +v1.8.2 \ No newline at end of file From 40df4059f5649cb3c1758e1e697f6c96767b7d8d Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 22:55:00 +0200 Subject: [PATCH 260/301] [add changelog entry] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b727772eeee..9356f42fed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # LDC master #### Big news +- Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) #### Platform support From 1a1c2a2a77ccb7b54e9dd77b39b94f171c145625 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 23 Jul 2023 23:02:27 +0200 Subject: [PATCH 261/301] Weaken dmd-testsuite's compilable/cattributes.i wrt. unsupported __declspec(naked) / __attribute__((naked)) These attributes are apparently treated as if the function body started with `asm { naked; }`, where LDC only accepts DMD-style inline asm statements for that function body. `@naked` wouldn't be an option either, as that includes skipping our prologue, so accessing parameters is only safe in asm via register/stack slot directly. --- tests/dmd/compilable/cattributes.i | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/dmd/compilable/cattributes.i b/tests/dmd/compilable/cattributes.i index 0f255b1ee01..60b2f4c8b94 100644 --- a/tests/dmd/compilable/cattributes.i +++ b/tests/dmd/compilable/cattributes.i @@ -8,7 +8,8 @@ __declspec(dllexport) int ghi() { return 3; } __declspec(dllexport) int jkl; -__declspec(naked) __declspec(dllexport) +/* LDC FIXME: __declspec(naked) restricts bodies to DMD-style inline asm +__declspec(naked)*/ __declspec(dllexport) int test(int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f + abc + def() + ghi() + jkl; @@ -24,7 +25,8 @@ __attribute__((dllexport)) int ghix() { return 3; } __attribute__((dllexport)) int jklx; -__attribute__((naked)) __attribute__((dllexport)) +/* LDC FIXME: ditto for __attribute__((naked)) +__attribute__((naked))*/ __attribute__((dllexport)) int testx(int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f + abcx + defx() + ghix() + jklx; From f806ce19b8dec61c947cb85568e40a4656c4fa9a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 24 Jul 2023 00:59:37 +0200 Subject: [PATCH 262/301] Circle CI: Switch to 'large' runners on Linux, with 4 CPU cores and 8 GB RAM (#4452) --- .circleci/config.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index afdb8092017..9db6d69899d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,8 +104,9 @@ jobs: <<: *commonSteps docker: - image: ubuntu:20.04 + resource_class: large environment: - - PARALLELISM: 2 + - PARALLELISM: 4 - CI_OS: linux - EXTRA_APT_PACKAGES: llvm-dev libclang-common-10-dev - HOST_LDC_VERSION: 1.24.0 @@ -114,8 +115,9 @@ jobs: <<: *commonSteps docker: - image: ubuntu:20.04 + resource_class: large environment: - - PARALLELISM: 2 + - PARALLELISM: 4 - CI_OS: linux - EXTRA_APT_PACKAGES: gdmd llvm-dev libclang-common-10-dev - HOST_LDC_VERSION: 1.24.0 From 9ce57807c0aed28e2d884bc7110d1ac8a3458037 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 01:08:10 +0200 Subject: [PATCH 263/301] Drop support for LLVM 9 and LLVM 10 --- .github/workflows/supported_llvm_versions.yml | 25 +- CHANGELOG.md | 1 + CMakeLists.txt | 16 +- cmake/Modules/FindLLVM.cmake | 6 +- driver/archiver.cpp | 2 - driver/cache.cpp | 12 - driver/cl_options-llvm.cpp | 57 +- driver/cl_options-llvm.h | 2 - driver/cl_options_sanitizers.cpp | 8 +- driver/codegenerator.cpp | 13 +- driver/ldmd.cpp | 4 - driver/linker-gcc.cpp | 10 +- driver/linker-msvc.cpp | 2 - driver/toobj.cpp | 6 - gen/abi/generic.h | 8 +- gen/ctfloat.cpp | 7 +- gen/dibuilder.cpp | 10 - gen/dynamiccompile.cpp | 4 - gen/funcgenstate.cpp | 4 - gen/functions.cpp | 37 - gen/llvm.h | 11 - gen/llvmhelpers.cpp | 8 +- gen/optimizer.cpp | 16 - gen/optimizer.h | 4 - gen/passes/GarbageCollect2Stack.cpp | 4 - gen/toconstelem.cpp | 4 +- gen/toir.cpp | 4 +- gen/trycatchfinally.cpp | 7 +- gen/uda.cpp | 19 - ir/irtype.cpp | 8 +- runtime/druntime/src/ldc/intrinsics.di | 12 +- runtime/jit-rt/cpp-so/bind.cpp | 9 - runtime/jit-rt/cpp-so/disassembler.cpp | 43 +- runtime/jit-rt/cpp-so/jit_context.cpp | 4 - tests/PGO/lit.local.cfg | 5 +- tests/instrument/xray_link.d | 2 +- tests/lit.site.cfg.in | 8 +- .../addFuncEntryCall/addFuncEntryCallPass.cpp | 10 +- tests/sanitizers/lsan_memleak.d | 2 +- tools/ldc-profdata/llvm-profdata-10.0.cpp | 1178 ----------------- tools/ldc-profdata/llvm-profdata-9.0.cpp | 1087 --------------- utils/FileCheck-10.cpp | 664 ---------- utils/FileCheck-9.cpp | 651 --------- 43 files changed, 46 insertions(+), 3948 deletions(-) delete mode 100644 tools/ldc-profdata/llvm-profdata-10.0.cpp delete mode 100644 tools/ldc-profdata/llvm-profdata-9.0.cpp delete mode 100644 utils/FileCheck-10.cpp delete mode 100644 utils/FileCheck-9.cpp diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 9b9ff4021f0..6be3c8b38b9 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -29,22 +29,17 @@ jobs: os: ubuntu-20.04 host_dc: ldc-beta llvm_version: 12.0.1 - cmake_flags: -DLIB_SUFFIX=64 - - job_name: Ubuntu 20.04, LLVM 11, latest LDC beta - os: ubuntu-20.04 - host_dc: ldc-beta - llvm_version: 11.1.0 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON - - job_name: Ubuntu 20.04, LLVM 9, latest DMD beta + cmake_flags: -DBUILD_SHARED_LIBS=ON -DLIB_SUFFIX=64 + - job_name: Ubuntu 20.04, LLVM 11, latest DMD beta os: ubuntu-20.04 host_dc: dmd-beta - llvm_version: 9.0.1 + llvm_version: 11.1.0 cmake_flags: -DBUILD_SHARED_LIBS=OFF -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON - - job_name: macOS 11, LLVM 10, latest DMD beta - os: macos-11 - host_dc: dmd-beta - llvm_version: 10.0.1 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 + #- job_name: macOS 11, LLVM 10, latest DMD beta + # os: macos-11 + # host_dc: dmd-beta + # llvm_version: 10.0.1 + # cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 - job_name: macOS 11, LLVM 13, latest LDC beta os: macos-11 host_dc: ldc-beta @@ -132,10 +127,6 @@ jobs: if: success() || failure() run: | set -eux - # LLVM 9: libclang_rt.fuzzer-x86_64.a not compiled with -fPIC - if [[ '${{ matrix.llvm_version }}' = 9.* ]]; then - echo "config.available_features.remove('Fuzzer')" >> tests/sanitizers/lit.local.cfg - fi # LLVM 14+ on Linux: don't use vanilla llvm-symbolizer (no support for zlib-compressed debug sections => failing ASan tests) if [[ '${{ runner.os }}' == 'Linux' && '${{ matrix.llvm_version }}' =~ ^1[4-9]\. ]]; then mv llvm/bin/llvm-symbolizer llvm/bin/llvm-symbolizer.bak diff --git a/CHANGELOG.md b/CHANGELOG.md index 9356f42fed2..de9002914d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) #### Platform support +- Supports LLVM 11.0 - 15.0. #### Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c34a3e7710..5973ad29a67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ endfunction() # Locate LLVM. # -find_package(LLVM 9.0 REQUIRED +find_package(LLVM 11.0 REQUIRED all-targets analysis asmparser asmprinter bitreader bitwriter codegen core debuginfodwarf debuginfomsf debuginfopdb demangle instcombine ipo instrumentation irreader libdriver linker lto mc @@ -46,9 +46,9 @@ foreach(LLVM_SUPPORTED_TARGET ${LLVM_TARGETS_TO_BUILD}) add_definitions("-DLDC_LLVM_SUPPORTED_TARGET_${LLVM_SUPPORTED_TARGET}=1") endforeach() -# Set MLIR support variables if it is found. Only try for LLVM >= 10.0. +# Set MLIR support variables if it is found. # FIXME: LLVM 14+ (`mlir::OwningModuleRef` replacement) -if(NOT (LDC_LLVM_VER LESS 1000) AND NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) +if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) include(FindMLIR) if(MLIR_FOUND) message(STATUS "-- Building LDC with MLIR support (${MLIR_ROOT_DIR})") @@ -593,12 +593,10 @@ if(MSVC) endif() endif() if(LDC_WITH_LLD) - if(NOT (LDC_LLVM_VER LESS 1000)) - if(MSVC) - list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) - else() - set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) - endif() + if(MSVC) + list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) + else() + set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) endif() if (LDC_LLVM_VER LESS 1200 OR NOT LDC_LLVM_VER LESS 1400) set(LLD_MACHO lldMachO) diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake index 693227b5b08..e55fb6301ab 100644 --- a/cmake/Modules/FindLLVM.cmake +++ b/cmake/Modules/FindLLVM.cmake @@ -37,8 +37,6 @@ set(llvm_config_names llvm-config-15.0 llvm-config150 llvm-config-15 llvm-config-13.0 llvm-config130 llvm-config-13 llvm-config-12.0 llvm-config120 llvm-config-12 llvm-config-11.0 llvm-config110 llvm-config-11 - llvm-config-10.0 llvm-config100 llvm-config-10 - llvm-config-9.0 llvm-config90 llvm-config-9 llvm-config) find_program(LLVM_CONFIG NAMES ${llvm_config_names} @@ -51,11 +49,11 @@ if(APPLE) NAMES ${llvm_config_names} PATHS /opt/local/libexec/llvm-15/bin /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin /opt/local/libexec/llvm-12/bin - /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm-10/bin /opt/local/libexec/llvm-9.0/bin + /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm/bin /usr/local/opt/llvm@15/bin /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin /usr/local/opt/llvm@12/bin - /usr/local/opt/llvm@11/bin /usr/local/opt/llvm@10/bin /usr/local/opt/llvm@9/bin + /usr/local/opt/llvm@11/bin /usr/local/opt/llvm/bin NO_DEFAULT_PATH) endif() diff --git a/driver/archiver.cpp b/driver/archiver.cpp index bf5ce937d14..6ff14f22a8d 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -20,9 +20,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Errc.h" -#if LDC_LLVM_VER >= 1100 #include "llvm/Support/Host.h" -#endif #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" diff --git a/driver/cache.cpp b/driver/cache.cpp index bb5af90b1a1..9e9278847da 100644 --- a/driver/cache.cpp +++ b/driver/cache.cpp @@ -73,20 +73,12 @@ static std::error_code createSymLink(const char *to, const char *from) { #include namespace llvm { namespace sys { -#if LDC_LLVM_VER >= 1100 namespace windows { // Fwd declaration to an internal LLVM function. std::error_code widenPath(const llvm::Twine &Path8, llvm::SmallVectorImpl &Path16, size_t MaxPathLen = MAX_PATH); } -#else -namespace path { -// Fwd declaration to an internal LLVM function. -std::error_code widenPath(const llvm::Twine &Path8, - llvm::SmallVectorImpl &Path16); -} -#endif // LDC_LLVM_VER < 1100 } // namespace sys } // namespace llvm @@ -100,11 +92,7 @@ std::error_code createLink(FType f, const char *to, const char *from) { // //===----------------------------------------------------------------------===// -#if LDC_LLVM_VER >= 1100 using llvm::sys::windows::widenPath; -#else - using llvm::sys::path::widenPath; -#endif llvm::SmallVector wide_from; llvm::SmallVector wide_to; diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp index dd2e18f5268..657029f81fd 100644 --- a/driver/cl_options-llvm.cpp +++ b/driver/cl_options-llvm.cpp @@ -15,51 +15,28 @@ // Pull in command-line options and helper functions from special LLVM header // shared by multiple LLVM tools. -#if LDC_LLVM_VER >= 1100 #include "llvm/CodeGen/CommandFlags.h" static llvm::codegen::RegisterCodeGenFlags CGF; using namespace llvm; -#else -#include "llvm/CodeGen/CommandFlags.inc" -#endif static cl::opt DisableRedZone("disable-red-zone", cl::ZeroOrMore, cl::desc("Do not emit code that uses the red zone.")); -#if LDC_LLVM_VER < 1100 -// legacy option -static cl::opt - disableFPElim("disable-fp-elim", cl::ZeroOrMore, cl::ReallyHidden, - cl::desc("Disable frame pointer elimination optimization")); -#endif - // Now expose the helper functions (with static linkage) via external wrappers // in the opts namespace, including some additional helper functions. namespace opts { std::string getArchStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getMArch(); -#else - return ::MArch; -#endif } Optional getRelocModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitRelocModel(); -#else - return ::getRelocModel(); -#endif } Optional getCodeModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitCodeModel(); -#else - return ::getCodeModel(); -#endif } #if LDC_LLVM_VER >= 1300 @@ -69,26 +46,16 @@ using FPK = llvm::FramePointer::FP; #endif llvm::Optional framePointerUsage() { -#if LDC_LLVM_VER >= 1100 // Defaults to `FP::None`; no way to check if set explicitly by user except // indirectly via setFunctionAttributes()... return codegen::getFramePointerUsage(); -#else - if (::FramePointerUsage.getNumOccurrences() > 0) - return ::FramePointerUsage.getValue(); - if (disableFPElim.getNumOccurrences() > 0) - return disableFPElim ? llvm::FramePointer::All : llvm::FramePointer::None; - return llvm::None; -#endif } bool disableRedZone() { return ::DisableRedZone; } bool printTargetFeaturesHelp() { -#if LDC_LLVM_VER >= 1100 const auto MCPU = codegen::getMCPU(); const auto MAttrs = codegen::getMAttrs(); -#endif if (MCPU == "help") return true; return std::any_of(MAttrs.begin(), MAttrs.end(), @@ -98,39 +65,23 @@ bool printTargetFeaturesHelp() { TargetOptions InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple) { #if LDC_LLVM_VER >= 1200 return codegen::InitTargetOptionsFromCodeGenFlags(triple); -#elif LDC_LLVM_VER >= 1100 - return codegen::InitTargetOptionsFromCodeGenFlags(); #else - return ::InitTargetOptionsFromCodeGenFlags(); + return codegen::InitTargetOptionsFromCodeGenFlags(); #endif } std::string getCPUStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getCPUStr(); -#else - return ::getCPUStr(); -#endif } std::string getFeaturesStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getFeaturesStr(); -#else - return ::getFeaturesStr(); -#endif } -#if LDC_LLVM_VER >= 1000 void setFunctionAttributes(StringRef cpu, StringRef features, Function &function) { -#if LDC_LLVM_VER >= 1100 return codegen::setFunctionAttributes(cpu, features, function); -#else - return ::setFunctionAttributes(cpu, features, function); -#endif } -#endif } // namespace opts #if LDC_WITH_LLD @@ -142,11 +93,9 @@ TargetOptions initTargetOptionsFromCodeGenFlags() { return ::opts::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } -#if LDC_LLVM_VER >= 1000 Optional getRelocModelFromCMModel() { return ::opts::getRelocModel(); } -#endif Optional getCodeModelFromCMModel() { return ::opts::getCodeModel(); @@ -154,10 +103,6 @@ Optional getCodeModelFromCMModel() { std::string getCPUStr() { return ::opts::getCPUStr(); } -#if LDC_LLVM_VER >= 1100 std::vector getMAttrs() { return codegen::getMAttrs(); } -#else -std::vector getMAttrs() { return ::MAttrs; } -#endif } // namespace lld #endif // LDC_WITH_LLD diff --git a/driver/cl_options-llvm.h b/driver/cl_options-llvm.h index ba6379f144f..a0ed68613d4 100644 --- a/driver/cl_options-llvm.h +++ b/driver/cl_options-llvm.h @@ -38,8 +38,6 @@ InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple); std::string getCPUStr(); std::string getFeaturesStr(); -#if LDC_LLVM_VER >= 1000 void setFunctionAttributes(llvm::StringRef cpu, llvm::StringRef features, llvm::Function &function); -#endif } diff --git a/driver/cl_options_sanitizers.cpp b/driver/cl_options_sanitizers.cpp index 416568b209c..4994e8b162a 100644 --- a/driver/cl_options_sanitizers.cpp +++ b/driver/cl_options_sanitizers.cpp @@ -206,12 +206,8 @@ void initializeSanitizerOptionsFromCmdline() if (isAnySanitizerEnabled() && !fSanitizeBlacklist.empty()) { std::string loadError; - sanitizerBlacklist = - llvm::SpecialCaseList::create(fSanitizeBlacklist, -#if LDC_LLVM_VER >= 1000 - *llvm::vfs::getRealFileSystem(), -#endif - loadError); + sanitizerBlacklist = llvm::SpecialCaseList::create( + fSanitizeBlacklist, *llvm::vfs::getRealFileSystem(), loadError); if (!sanitizerBlacklist) error(Loc(), "-fsanitize-blacklist error: %s", loadError.c_str()); } diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index e15cfe22822..af3b19ce075 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -28,11 +28,7 @@ #if LDC_LLVM_VER >= 1400 #include "llvm/IR/DiagnosticInfo.h" #endif -#if LDC_LLVM_VER >= 1100 #include "llvm/IR/LLVMRemarkStreamer.h" -#else -#include "llvm/IR/RemarkStreamer.h" -#endif #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" @@ -66,13 +62,8 @@ createAndSetDiagnosticsOutputFile(IRState &irs, llvm::LLVMContext &ctx, // If there is instrumentation data available, also output function hotness const bool withHotness = opts::isUsingPGOProfile(); - auto remarksFileOrError = -#if LDC_LLVM_VER >= 1100 - llvm::setupLLVMOptimizationRemarks( -#else - llvm::setupOptimizationRemarks( -#endif - ctx, diagnosticsFilename, "", "", withHotness); + auto remarksFileOrError = llvm::setupLLVMOptimizationRemarks( + ctx, diagnosticsFilename, "", "", withHotness); if (llvm::Error e = remarksFileOrError.takeError()) { irs.dmodule->error("Could not create file %s: %s", diagnosticsFilename.c_str(), diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 888b2efc5bd..c25938a1c51 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -512,11 +512,7 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, } else if (strcmp(p + 1, "gf") == 0) { ldcArgs.push_back("-g"); } else if (strcmp(p + 1, "gs") == 0) { -#if LDC_LLVM_VER >= 1100 ldcArgs.push_back("-frame-pointer=all"); -#else - ldcArgs.push_back("-disable-fp-elim"); -#endif } else if (strcmp(p + 1, "gx") == 0) { goto Lnot_in_ldc; } else if (strcmp(p + 1, "gt") == 0) { diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index f755258c95b..938dc25cb1e 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -759,10 +759,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -778,10 +776,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -789,14 +785,12 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, ); } else if (global.params.targetTriple->isOSBinFormatCOFF()) { success = lld::mingw::link(fullArgs -#if LDC_LLVM_VER >= 1000 && LDC_LLVM_VER < 1400 +#if LDC_LLVM_VER < 1400 , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -813,10 +807,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp index 43c162e6444..15830d5182a 100644 --- a/driver/linker-msvc.cpp +++ b/driver/linker-msvc.cpp @@ -276,10 +276,8 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, , canExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , canExitEarly, false diff --git a/driver/toobj.cpp b/driver/toobj.cpp index b25a8773cce..48c8d6407ad 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -45,13 +45,7 @@ #include #include -#if LDC_LLVM_VER < 1000 -using CodeGenFileType = llvm::TargetMachine::CodeGenFileType; -constexpr CodeGenFileType CGFT_AssemblyFile = llvm::TargetMachine::CGFT_AssemblyFile; -constexpr CodeGenFileType CGFT_ObjectFile = llvm::TargetMachine::CGFT_ObjectFile; -#else using CodeGenFileType = llvm::CodeGenFileType; -#endif static llvm::cl::opt NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore, diff --git a/gen/abi/generic.h b/gen/abi/generic.h index e1e6f452f44..19b7aee0362 100644 --- a/gen/abi/generic.h +++ b/gen/abi/generic.h @@ -36,12 +36,8 @@ struct LLTypeMemoryLayout { const size_t sizeInBits = getTypeBitSize(type); assert(sizeInBits % 8 == 0); return llvm::VectorType::get(LLIntegerType::get(gIR->context(), 8), - sizeInBits / 8 -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + sizeInBits / 8, + /*Scalable=*/false); } if (LLStructType *structType = isaStruct(type)) { diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp index 0ba8a5e838c..ef033c018df 100644 --- a/gen/ctfloat.cpp +++ b/gen/ctfloat.cpp @@ -26,11 +26,8 @@ union CTFloatUnion { APFloat parseLiteral(const llvm::fltSemantics &semantics, const char *literal, bool *isOutOfRange = nullptr) { APFloat ap(semantics, APFloat::uninitialized); - auto r = -#if LDC_LLVM_VER >= 1000 - llvm::cantFail -#endif - (ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); + auto r = llvm::cantFail( + ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); if (isOutOfRange) { *isOutOfRange = (r & (APFloat::opOverflow | APFloat::opUnderflow)) != 0; } diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 1e817ee3f4a..f32b3631235 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -385,12 +385,8 @@ DIType DIBuilder::CreateVectorType(TypeVector *type) { LLType *T = DtoType(type); const auto dim = type->basetype->isTypeSArray()->dim->toInteger(); -#if LDC_LLVM_VER >= 1100 const auto Dim = llvm::ConstantAsMetadata::get(DtoConstSize_t(dim)); auto subscript = DBuilder.getOrCreateSubrange(Dim, nullptr, nullptr, nullptr); -#else - auto subscript = DBuilder.getOrCreateSubrange(0, dim); -#endif return DBuilder.createVectorType( getTypeAllocSize(T) * 8, // size (bits) @@ -669,13 +665,9 @@ DIType DIBuilder::CreateSArrayType(TypeSArray *type) { for (; te->ty == TY::Tsarray; te = te->nextOf()) { TypeSArray *tsa = static_cast(te); const auto count = tsa->dim->toInteger(); -#if LDC_LLVM_VER >= 1100 const auto Count = llvm::ConstantAsMetadata::get(DtoConstSize_t(count)); const auto subscript = DBuilder.getOrCreateSubrange(Count, nullptr, nullptr, nullptr); -#else - const auto subscript = DBuilder.getOrCreateSubrange(0, count); -#endif subscripts.push_back(subscript); } @@ -1292,9 +1284,7 @@ void DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar, vd->loc.linnum, // line num CreateTypeDescription(vd->type), // type vd->visibility.kind == Visibility::private_, // is local to unit -#if LDC_LLVM_VER >= 1000 !(vd->storage_class & STCextern), // bool isDefined -#endif nullptr, // DIExpression *Expr Decl // declaration ); diff --git a/gen/dynamiccompile.cpp b/gen/dynamiccompile.cpp index f0a0906c3f9..b982013c4f3 100644 --- a/gen/dynamiccompile.cpp +++ b/gen/dynamiccompile.cpp @@ -766,12 +766,8 @@ void createThunkFunc(llvm::Module &module, const llvm::Function *src, for (auto &arg : dst->args()) { args.push_back(&arg); } -#if LDC_LLVM_VER >= 1100 auto ret = builder.CreateCall( llvm::FunctionCallee(dst->getFunctionType(), thunkPtr), args); -#else - auto ret = builder.CreateCall(thunkPtr, args); -#endif ret->setCallingConv(src->getCallingConv()); ret->setAttributes(src->getAttributes()); if (dst->getReturnType()->isVoidTy()) { diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index a59ec360e80..52cf698e40b 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -124,11 +124,7 @@ LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, // calls inside a funclet must be annotated with its value llvm::SmallVector BundleList; -#if LDC_LLVM_VER >= 1100 llvm::FunctionCallee calleeArg(calleeType, callee); -#else - auto calleeArg = callee; -#endif if (doesNotThrow || scopes.empty()) { auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name); diff --git a/gen/functions.cpp b/gen/functions.cpp index 701cd602ea4..adbdf4c5b7c 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -217,9 +217,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // opaque struct if (!opts::fNullPointerIsValid) attrs.addAttribute(LLAttribute::NonNull); -#if LDC_LLVM_VER >= 1100 attrs.addAttribute(LLAttribute::NoUndef); -#endif } else { attrs.addDereferenceableAttr(loweredDType->size()); } @@ -498,42 +496,11 @@ void applyTargetMachineAttributes(llvm::Function &func, const auto cpu = dcompute ? "" : target.getTargetCPU(); const auto features = dcompute ? "" : target.getTargetFeatureString(); -#if LDC_LLVM_VER >= 1000 opts::setFunctionAttributes(cpu, features, func); if (opts::fFastMath) // -ffast-math[=true] overrides -enable-unsafe-fp-math func.addFnAttr("unsafe-fp-math", "true"); if (!func.hasFnAttribute("frame-pointer")) // not explicitly set by user func.addFnAttr("frame-pointer", isOptimizationEnabled() ? "none" : "all"); -#else - if (!cpu.empty()) - func.addFnAttr("target-cpu", cpu); - if (!features.empty()) - func.addFnAttr("target-features", features); - - // Floating point settings - const auto &TO = target.Options; - func.addFnAttr("unsafe-fp-math", TO.UnsafeFPMath ? "true" : "false"); - // This option was removed from llvm::TargetOptions in LLVM 5.0. - // Clang sets this to true when `-cl-mad-enable` is passed (OpenCL only). - // TODO: implement interface for this option. - const bool lessPreciseFPMADOption = false; - func.addFnAttr("less-precise-fpmad", - lessPreciseFPMADOption ? "true" : "false"); - func.addFnAttr("no-infs-fp-math", TO.NoInfsFPMath ? "true" : "false"); - func.addFnAttr("no-nans-fp-math", TO.NoNaNsFPMath ? "true" : "false"); - - switch (whichFramePointersToEmit()) { - case llvm::FramePointer::None: - func.addFnAttr("frame-pointer", "none"); - break; - case llvm::FramePointer::NonLeaf: - func.addFnAttr("frame-pointer", "non-leaf"); - break; - case llvm::FramePointer::All: - func.addFnAttr("frame-pointer", "all"); - break; - } -#endif // LDC_LLVM_VER < 1000 } void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { @@ -1255,11 +1222,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { } applyXRayAttributes(*fd, *func); if (opts::fNullPointerIsValid) { -#if LDC_LLVM_VER >= 1100 func->addFnAttr(LLAttribute::NullPointerIsValid); -#else - func->addFnAttr("null-pointer-is-valid", "true"); -#endif } if (opts::fSplitStack && !hasNoSplitStackUDA(fd)) { func->addFnAttr("split-stack"); diff --git a/gen/llvm.h b/gen/llvm.h index daebd1f2e7c..86dd6cdd47c 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -31,30 +31,19 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/DebugInfo.h" -#if LDC_LLVM_VER >= 1000 // LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back // and point to std::make_unique. #include namespace llvm { using std::make_unique; } -#endif using llvm::APFloat; using llvm::APInt; using llvm::IRBuilder; -#if LDC_LLVM_VER >= 1000 -#if LDC_LLVM_VER >= 1100 #define LLAlign llvm::Align -#else -#define LLAlign llvm::MaybeAlign -#endif #define LLMaybeAlign llvm::MaybeAlign -#else -#define LLAlign -#define LLMaybeAlign -#endif #define GET_INTRINSIC_DECL(_X) \ (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X)) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 46488f5d903..ae9bed70cdc 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1202,10 +1202,8 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) { static_cast(tv->basetype)->dim->toInteger(); #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif return llvm::ConstantVector::getSplat(elementCount, val); } @@ -1281,11 +1279,7 @@ static char *DtoOverloadedIntrinsicName(TemplateInstance *ti, if (dtype->isPPC_FP128Ty()) { // special case replacement = "ppcf128"; } else if (dtype->isVectorTy()) { -#if LDC_LLVM_VER >= 1100 auto vectorType = llvm::cast(dtype); -#else - auto vectorType = llvm::cast(dtype); -#endif llvm::raw_string_ostream stream(replacement); stream << 'v' << vectorType->getNumElements() << prefix << gDataLayout->getTypeSizeInBits(vectorType->getElementType()); diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 29cc8c2f43d..2997e52ae38 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -49,9 +49,7 @@ #include "llvm/Transforms/Scalar/LICM.h" #include "llvm/Transforms/Scalar/Reassociate.h" #endif -#if LDC_LLVM_VER >= 1000 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" -#endif extern llvm::TargetMachine *gTargetMachine; using namespace llvm; @@ -141,15 +139,6 @@ bool willCrossModuleInline() { return enableCrossModuleInlining == llvm::cl::BOU_TRUE && willInline(); } -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit() { - if (auto option = opts::framePointerUsage()) - return *option; - return isOptimizationEnabled() ? llvm::FramePointer::None - : llvm::FramePointer::All; -} -#endif - bool isOptimizationEnabled() { return optimizeLevel != 0; } llvm::CodeGenOpt::Level codeGenOptLevel() { @@ -238,13 +227,8 @@ static void legacyAddThreadSanitizerPass(const PassManagerBuilder &Builder, static void legacyAddSanitizerCoveragePass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { -#if LDC_LLVM_VER >= 1000 PM.add(createModuleSanitizerCoverageLegacyPassPass( opts::getSanitizerCoverageOptions())); -#else - PM.add( - createSanitizerCoverageModulePass(opts::getSanitizerCoverageOptions())); -#endif } // Adds PGO instrumentation generation and use passes. diff --git a/gen/optimizer.h b/gen/optimizer.h index cbe3adb8fa2..0b3da829fb6 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -35,10 +35,6 @@ bool willInline(); bool willCrossModuleInline(); -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit(); -#endif - unsigned optLevel(); bool isOptimizationEnabled(); diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 7684906a7de..a6946dbfc73 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -71,11 +71,7 @@ void EmitMemSet(IRBuilder<> &B, Value *Dst, Value *Val, Value *Len, const G2StackAnalysis &A) { Dst = B.CreateBitCast(Dst, PointerType::getUnqual(B.getInt8Ty())); -#if LDC_LLVM_VER >= 1000 MaybeAlign Align(1); -#else - unsigned Align = 1; -#endif auto CS = B.CreateMemSet(Dst, Val, Len, Align, false /*isVolatile*/); if (A.CGNode) { diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 918cd5d91fb..4a4e8422772 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -708,10 +708,8 @@ class ToConstElemVisitor : public Visitor { // constructed. #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif result = llvm::ConstantVector::getSplat( elementCount, toConstElem(e->e1->optimize(WANTvalue), p)); diff --git a/gen/toir.cpp b/gen/toir.cpp index 403ff996d8b..5433060b144 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2723,10 +2723,8 @@ class ToElemVisitor : public Visitor { if (auto llConstant = isaConstant(llElement)) { #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(N); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(N, false); #else - const auto elementCount = N; + const auto elementCount = llvm::ElementCount(N, false); #endif auto vectorConstant = llvm::ConstantVector::getSplat(elementCount, llConstant); diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp index f27e42ad656..4b6dfe05963 100644 --- a/gen/trycatchfinally.cpp +++ b/gen/trycatchfinally.cpp @@ -371,11 +371,8 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock, // And convert the BranchInst to the existing branch target to a // SelectInst so we can append the other cases to it. endBlock()->getTerminator()->eraseFromParent(); - llvm::Value *sel = new llvm::LoadInst( -#if LDC_LLVM_VER >= 1100 - branchSelectorType, -#endif - branchSelector, "", endBlock()); + llvm::Value *sel = + new llvm::LoadInst(branchSelectorType, branchSelector, "", endBlock()); llvm::SwitchInst::Create( sel, exitTargets[0].branchTarget, 1, // Expected number of branches, only for pre-allocating. diff --git a/gen/uda.cpp b/gen/uda.cpp index 371b565b38a..1cad8a21349 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -17,15 +17,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#if LDC_LLVM_VER < 1100 -namespace llvm { -// Auto-generate: -// Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { ... } -#define GET_ATTR_KIND_FROM_NAME -#include "llvm/IR/Attributes.inc" -} -#endif - namespace { /// Checks whether `moduleDecl` is in the ldc package and it's identifier is @@ -238,11 +229,7 @@ void applyAttrLLVMAttr(StructLiteralExp *sle, llvm::AttrBuilder &attrs) { llvm::StringRef key = getStringElem(sle, 0); llvm::StringRef value = getStringElem(sle, 1); if (value.empty()) { -#if LDC_LLVM_VER >= 1100 const auto kind = llvm::Attribute::getAttrKindFromName(key); -#else - const auto kind = llvm::getAttrKindFromName(key); -#endif if (kind != llvm::Attribute::None) { attrs.addAttribute(kind); } else { @@ -423,9 +410,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("ccc", llvm::CallingConv::C) .Case("fastcc", llvm::CallingConv::Fast) .Case("coldcc", llvm::CallingConv::Cold) -#if LDC_LLVM_VER >= 1000 .Case("cfguard_checkcc", llvm::CallingConv::CFGuard_Check) -#endif .Case("x86_stdcallcc", llvm::CallingConv::X86_StdCall) .Case("x86_fastcallcc", llvm::CallingConv::X86_FastCall) .Case("x86_regcallcc", llvm::CallingConv::X86_RegCall) @@ -435,10 +420,8 @@ bool parseCallingConvention(llvm::StringRef name, .Case("arm_aapcscc", llvm::CallingConv::ARM_AAPCS) .Case("arm_aapcs_vfpcc", llvm::CallingConv::ARM_AAPCS_VFP) .Case("aarch64_vector_pcs", llvm::CallingConv::AArch64_VectorCall) -#if LDC_LLVM_VER >= 1000 .Case("aarch64_sve_vector_pcs", llvm::CallingConv::AArch64_SVE_VectorCall) -#endif .Case("msp430_intrcc", llvm::CallingConv::MSP430_INTR) .Case("avr_intrcc", llvm::CallingConv::AVR_INTR) .Case("avr_signalcc", llvm::CallingConv::AVR_SIGNAL) @@ -473,9 +456,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("amdgpu_ps", llvm::CallingConv::AMDGPU_PS) .Case("amdgpu_cs", llvm::CallingConv::AMDGPU_CS) .Case("amdgpu_kernel", llvm::CallingConv::AMDGPU_KERNEL) -#if LDC_LLVM_VER >= 1000 .Case("tailcc", llvm::CallingConv::Tail) -#endif .Case("default", llvm::CallingConv::MaxID - 1) .Default(llvm::CallingConv::MaxID); diff --git a/ir/irtype.cpp b/ir/irtype.cpp index 0553fbd7b3f..0cf987d9284 100644 --- a/ir/irtype.cpp +++ b/ir/irtype.cpp @@ -199,12 +199,8 @@ IrTypeVector *IrTypeVector::get(Type *dt) { // Could have already built the type as part of a struct forward reference, // just as for pointers and arrays. if (!ctype) { - LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger() -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger(), + /*Scalable=*/false); ctype = new IrTypeVector(dt, lt); } diff --git a/runtime/druntime/src/ldc/intrinsics.di b/runtime/druntime/src/ldc/intrinsics.di index c1950e4fcca..f18bf5035dc 100644 --- a/runtime/druntime/src/ldc/intrinsics.di +++ b/runtime/druntime/src/ldc/intrinsics.di @@ -19,9 +19,7 @@ else static assert(false, "This module is only valid for LDC"); } - version (LDC_LLVM_900) enum LLVM_version = 900; -else version (LDC_LLVM_1000) enum LLVM_version = 1000; -else version (LDC_LLVM_1100) enum LLVM_version = 1100; + version (LDC_LLVM_1100) enum LLVM_version = 1100; else version (LDC_LLVM_1101) enum LLVM_version = 1101; else version (LDC_LLVM_1200) enum LLVM_version = 1200; else version (LDC_LLVM_1300) enum LLVM_version = 1300; @@ -56,9 +54,7 @@ pragma(LDC_intrinsic, "llvm.returnaddress") /// The 'llvm.frameaddress' intrinsic attempts to return the target-specific /// frame pointer value for the specified stack frame. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.frameaddress."~p0i8 : - "llvm.frameaddress") +pragma(LDC_intrinsic, "llvm.frameaddress."~p0i8) void* llvm_frameaddress(uint level); /// The 'llvm.stacksave' intrinsic is used to remember the current state of the @@ -85,9 +81,7 @@ pragma(LDC_intrinsic, "llvm.stackrestore") /// keep in cache. The cache type specifies whether the prefetch is performed on /// the data (1) or instruction (0) cache. The rw, locality and cache type /// arguments must be constant integers. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.prefetch."~p0i8 : - "llvm.prefetch") +pragma(LDC_intrinsic, "llvm.prefetch."~p0i8) void llvm_prefetch(const(void)* ptr, uint rw, uint locality, uint cachetype) pure @safe; /// The 'llvm.pcmarker' intrinsic is a method to export a Program Counter (PC) diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index 05d2b260c0e..7f0347d9ab8 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -18,17 +18,8 @@ #include "valueparser.h" -#if LDC_LLVM_VER >= 1000 -#if LDC_LLVM_VER >= 1100 #define LLAlign llvm::Align -#else -#define LLAlign llvm::MaybeAlign -#endif #define LLMaybeAlign llvm::MaybeAlign -#else -#define LLAlign -#define LLMaybeAlign -#endif namespace { enum { SmallParamsCount = 5 }; diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp index 44bffba7c14..32a817c570d 100644 --- a/runtime/jit-rt/cpp-so/disassembler.cpp +++ b/runtime/jit-rt/cpp-so/disassembler.cpp @@ -35,11 +35,9 @@ #endif #include "llvm/Target/TargetMachine.h" -#if LDC_LLVM_VER >= 1000 namespace llvm { using std::make_unique; } -#endif namespace { template std::unique_ptr unique(T *ptr) { @@ -132,28 +130,15 @@ void printFunction(const llvm::MCDisassembler &disasm, std::string comment; llvm::raw_string_ostream commentStream(comment); auto status = disasm.getInstruction(inst, size, data.slice(pos), pos, -#if LDC_LLVM_VER < 1000 - llvm::nulls(), -#endif commentStream); switch (status) { case llvm::MCDisassembler::Fail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "failed to disassemble"); + streamer.emitRawText("failed to disassemble"); return; case llvm::MCDisassembler::SoftFail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "potentially undefined instruction encoding:"); + streamer.emitRawText("potentially undefined instruction encoding:"); LLVM_FALLTHROUGH; case llvm::MCDisassembler::Success: @@ -166,22 +151,14 @@ void printFunction(const llvm::MCDisassembler &disasm, } } else if (Stage::Emit == stage) { if (auto label = symTable.getTargetLabel(pos)) { -#if LDC_LLVM_VER >= 1100 streamer.emitLabel(label); -#else - streamer.EmitLabel(label); -#endif } commentStream.flush(); if (!comment.empty()) { streamer.AddComment(comment); comment.clear(); } -#if LDC_LLVM_VER >= 1100 streamer.emitInstruction(inst, sti); -#else - streamer.EmitInstruction(inst, sti); -#endif } break; } @@ -320,11 +297,7 @@ void disassemble(const llvm::TargetMachine &tm, for (const auto &symbol : object.symbols()) { const auto secIt = llvm::cantFail(symbol.getSection()); if (object.section_end() != secIt) { -#if LDC_LLVM_VER >= 1100 auto offset = llvm::cantFail(symbol.getValue()); -#else - auto offset = symbol.getValue(); -#endif sectionsToProcess[secIt->getIndex()].push_back(offset); } } @@ -346,21 +319,13 @@ void disassemble(const llvm::TargetMachine &tm, llvm::cantFail(symbol.getType())) { symTable.reset(); symTable.addLabel(0, 0, name); // Function start -#if LDC_LLVM_VER >= 1100 auto offset = llvm::cantFail(symbol.getValue()); -#else - auto offset = symbol.getValue(); -#endif processRelocations(symTable, offset, object, sec); // TODO: something more optimal for (const auto &globalSec : object.sections()) { -#if LDC_LLVM_VER >= 1000 auto rs = globalSec.getRelocatedSection(); if (rs && *rs == secIt) { -#else - if (globalSec.getRelocatedSection() == secIt) { -#endif processRelocations(symTable, offset, object, globalSec); } } @@ -377,11 +342,7 @@ void disassemble(const llvm::TargetMachine &tm, reinterpret_cast(data.data() + offset), size); printFunction(*disasm, *mcia, buff, symTable, *sti, *asmStreamer); -#if LDC_LLVM_VER >= 1100 asmStreamer->emitRawText(""); -#else - asmStreamer->EmitRawText(""); -#endif } } } diff --git a/runtime/jit-rt/cpp-so/jit_context.cpp b/runtime/jit-rt/cpp-so/jit_context.cpp index 5e9c0cf4626..535647b17e2 100644 --- a/runtime/jit-rt/cpp-so/jit_context.cpp +++ b/runtime/jit-rt/cpp-so/jit_context.cpp @@ -185,12 +185,8 @@ std::shared_ptr DynamicCompilerContext::createResolver() { return llvm::orc::createLegacyLookupResolver( execSession, -#if LDC_LLVM_VER >= 1100 [this](llvm::StringRef name_) -> llvm::JITSymbol { const std::string name = name_.str(); -#else - [this](const std::string &name) -> llvm::JITSymbol { -#endif if (auto Sym = compileLayer.findSymbol(name, false)) { return Sym; } else if (auto Err = Sym.takeError()) { diff --git a/tests/PGO/lit.local.cfg b/tests/PGO/lit.local.cfg index 3cf5779cfbc..d5d1d48297c 100644 --- a/tests/PGO/lit.local.cfg +++ b/tests/PGO/lit.local.cfg @@ -1,7 +1,4 @@ # Add "PGO_RT" feature, assuming the `profile` compiler-rt library is available config.available_features.add('PGO_RT') -if (config.llvm_version >= 700): - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) -else: - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '') ) +config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) diff --git a/tests/instrument/xray_link.d b/tests/instrument/xray_link.d index 83c498c2747..d05d174ea1e 100644 --- a/tests/instrument/xray_link.d +++ b/tests/instrument/xray_link.d @@ -2,7 +2,7 @@ // fails on macOS with LLVM 11 due to a linker error, see // https://github.com/llvm/llvm-test-suite/commit/2c3c4a6286d453f763c0245c6536ddd368f0db99 -// XFAIL: Darwin && atleast_llvm1100 +// XFAIL: Darwin // RUN: %ldc -fxray-instrument -fxray-instruction-threshold=1 -of=%t%exe %s -vv 2>&1 | FileCheck %s diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index f18b6f4d3d9..484b7246de2 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -217,12 +217,8 @@ if (platform.system() != 'Windows') and lit.util.which('gdb', config.environment config.substitutions.append( ('%_gdb_dflags', gdb_dflags) ) # Add substitutions for functionality across different LLVM versions -if config.llvm_version >= 800: - config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) - config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) -else: - config.substitutions.append( ('%disable_fp_elim', '-disable-fp-elim') ) - config.substitutions.append( ('%enable_fp_elim', '-disable-fp-elim=false') ) +config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) +config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) if 'LD_LIBRARY_PATH' in os.environ: libs = [] diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp index 065b3b086a1..18b6ab66c61 100644 --- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp @@ -37,11 +37,7 @@ bool FuncEntryCallPass::doInitialization(Module &M) { // Add fwd declaration of the `void __test_funcentrycall(void)` function. auto functionType = FunctionType::get(Type::getVoidTy(M.getContext()), false); funcToCallUponEntry = - M.getOrInsertFunction("__test_funcentrycall", functionType) -#if LLVM_VERSION >= 900 - .getCallee() -#endif - ; + M.getOrInsertFunction("__test_funcentrycall", functionType).getCallee(); return true; } @@ -50,11 +46,7 @@ bool FuncEntryCallPass::runOnFunction(Function &F) { // (this includes e.g. `ldc.register_dso`!) llvm::BasicBlock &block = F.getEntryBlock(); IRBuilder<> builder(&block, block.begin()); -#if LLVM_VERSION >= 1100 builder.CreateCall(FunctionCallee(cast(funcToCallUponEntry))); -#else - builder.CreateCall(funcToCallUponEntry); -#endif return true; } diff --git a/tests/sanitizers/lsan_memleak.d b/tests/sanitizers/lsan_memleak.d index 6fc7c16ac35..de9a654a13a 100644 --- a/tests/sanitizers/lsan_memleak.d +++ b/tests/sanitizers/lsan_memleak.d @@ -1,6 +1,6 @@ // Test leak detection with LSan -// REQUIRES: LSan && atleast_llvm1000 +// REQUIRES: LSan // UNSUPPORTED: Windows, FreeBSD diff --git a/tools/ldc-profdata/llvm-profdata-10.0.cpp b/tools/ldc-profdata/llvm-profdata-10.0.cpp deleted file mode 100644 index 41e9abb82b1..00000000000 --- a/tools/ldc-profdata/llvm-profdata-10.0.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// llvm-profdata merges .profdata files. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/ProfileData/InstrProfReader.h" -#include "llvm/ProfileData/InstrProfWriter.h" -#include "llvm/ProfileData/ProfileCommon.h" -#include "llvm/ProfileData/SampleProfReader.h" -#include "llvm/ProfileData/SampleProfWriter.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include - -using namespace llvm; - -enum ProfileFormat { - PF_None = 0, - PF_Text, - PF_Compact_Binary, - PF_Ext_Binary, - PF_GCC, - PF_Binary -}; - -static void warn(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::warning(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; -} - -static void exitWithError(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::error(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; - ::exit(1); -} - -static void exitWithError(Error E, StringRef Whence = "") { - if (E.isA()) { - handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { - instrprof_error instrError = IPE.get(); - StringRef Hint = ""; - if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; - } - exitWithError(IPE.message(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); -} - -namespace { -enum ProfileKinds { instr, sample }; -enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; -} - -static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, - StringRef Whence = "") { - if (FailMode == failIfAnyAreInvalid) - exitWithErrorCode(EC, Whence); - else - warn(EC.message(), Whence); -} - -static void handleMergeWriterError(Error E, StringRef WhenceFile = "", - StringRef WhenceFunction = "", - bool ShowHint = true) { - if (!WhenceFile.empty()) - errs() << WhenceFile << ": "; - if (!WhenceFunction.empty()) - errs() << WhenceFunction << ": "; - - auto IPE = instrprof_error::success; - E = handleErrors(std::move(E), - [&IPE](std::unique_ptr E) -> Error { - IPE = E->get(); - return Error(std::move(E)); - }); - errs() << toString(std::move(E)) << "\n"; - - if (ShowHint) { - StringRef Hint = ""; - if (IPE != instrprof_error::success) { - switch (IPE) { - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::value_site_count_mismatch: - Hint = "Make sure that all profile data to be merged is generated " - "from the same binary."; - break; - default: - break; - } - } - - if (!Hint.empty()) - errs() << Hint << "\n"; - } -} - -namespace { -/// A remapper from original symbol names to new symbol names based on a file -/// containing a list of mappings from old name to new name. -class SymbolRemapper { - std::unique_ptr File; - DenseMap RemappingTable; - -public: - /// Build a SymbolRemapper from a file containing a list of old/new symbols. - static std::unique_ptr create(StringRef InputFile) { - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - auto Remapper = std::make_unique(); - Remapper->File = std::move(BufOrError.get()); - - for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); - !LineIt.is_at_eof(); ++LineIt) { - std::pair Parts = LineIt->split(' '); - if (Parts.first.empty() || Parts.second.empty() || - Parts.second.count(' ')) { - exitWithError("unexpected line in remapping file", - (InputFile + ":" + Twine(LineIt.line_number())).str(), - "expected 'old_symbol new_symbol'"); - } - Remapper->RemappingTable.insert(Parts); - } - return Remapper; - } - - /// Attempt to map the given old symbol into a new symbol. - /// - /// \return The new symbol, or \p Name if no such symbol was found. - StringRef operator()(StringRef Name) { - StringRef New = RemappingTable.lookup(Name); - return New.empty() ? Name : New; - } -}; -} - -struct WeightedFile { - std::string Filename; - uint64_t Weight; -}; -typedef SmallVector WeightedFileVector; - -/// Keep track of merged data and reported errors. -struct WriterContext { - std::mutex Lock; - InstrProfWriter Writer; - std::vector> Errors; - std::mutex &ErrLock; - SmallSet &WriterErrorCodes; - - WriterContext(bool IsSparse, std::mutex &ErrLock, - SmallSet &WriterErrorCodes) - : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock), - WriterErrorCodes(WriterErrorCodes) {} -}; - -/// Computer the overlap b/w profile BaseFilename and TestFileName, -/// and store the program level result to Overlap. -static void overlapInput(const std::string &BaseFilename, - const std::string &TestFilename, WriterContext *WC, - OverlapStats &Overlap, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - auto ReaderOrErr = InstrProfReader::create(TestFilename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Errors.emplace_back(make_error(IPE), TestFilename); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - for (auto &I : *Reader) { - OverlapStats FuncOverlap(OverlapStats::FunctionLevel); - FuncOverlap.setFuncInfo(I.Name, I.Hash); - - WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); - FuncOverlap.dump(OS); - } -} - -/// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, - WriterContext *WC) { - std::unique_lock CtxGuard{WC->Lock}; - - // Copy the filename, because llvm::ThreadPool copied the input "const - // WeightedFile &" by value, making a reference to the filename within it - // invalid outside of this packaged task. - std::string Filename = Input.Filename; - - auto ReaderOrErr = InstrProfReader::create(Input.Filename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Errors.emplace_back(make_error(IPE), Filename); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRProfile = Reader->isIRLevelProfile(); - bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Errors.emplace_back( - make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()), - Filename); - return; - } - - for (auto &I : *Reader) { - if (Remapper) - I.Name = (*Remapper)(I.Name); - const StringRef FuncName = I.Name; - bool Reported = false; - WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - // Only show hint the first time an error occurs. - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{WC->ErrLock}; - bool firstTime = WC->WriterErrorCodes.insert(IPE).second; - handleMergeWriterError(make_error(IPE), Input.Filename, - FuncName, firstTime); - }); - } - if (Reader->hasError()) - if (Error E = Reader->getError()) - WC->Errors.emplace_back(std::move(E), Filename); -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - for (auto &ErrorPair : Src->Errors) - Dst->Errors.push_back(std::move(ErrorPair)); - Src->Errors.clear(); - - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{Dst->ErrLock}; - bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; - if (firstTime) - warn(toString(make_error(IPE))); - }); -} - -static void mergeInstrProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads, FailureMode FailMode) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); - - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); - - // Initialize the writer contexts. - SmallVector, 4> Contexts; - for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(std::make_unique( - OutputSparse, ErrorLock, WriterErrorCodes)); - - if (NumThreads == 1) { - for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); - } else { - ThreadPool Pool(NumThreads); - - // Load the inputs in parallel (N/NumThreads serial steps). - unsigned Ctx = 0; - for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); - Ctx = (Ctx + 1) % NumThreads; - } - Pool.wait(); - - // Merge the writer contexts together (~ lg(NumThreads) serial steps). - unsigned Mid = Contexts.size() / 2; - unsigned End = Contexts.size(); - assert(Mid > 0 && "Expected more than one context"); - do { - for (unsigned I = 0; I < Mid; ++I) - Pool.async(mergeWriterContexts, Contexts[I].get(), - Contexts[I + Mid].get()); - Pool.wait(); - if (End & 1) { - Pool.async(mergeWriterContexts, Contexts[0].get(), - Contexts[End - 1].get()); - Pool.wait(); - } - End = Mid; - Mid /= 2; - } while (Mid > 0); - } - - // Handle deferred errors encountered during merging. If the number of errors - // is equal to the number of inputs the merge failed. - unsigned NumErrors = 0; - for (std::unique_ptr &WC : Contexts) { - for (auto &ErrorPair : WC->Errors) { - ++NumErrors; - warn(toString(std::move(ErrorPair.first)), ErrorPair.second); - } - } - if (NumErrors == Inputs.size() || - (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) - exitWithError("No profiles could be merged."); - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// Make a copy of the given function samples with all symbol names remapped -/// by the provided symbol remapper. -static sampleprof::FunctionSamples -remapSamples(const sampleprof::FunctionSamples &Samples, - SymbolRemapper &Remapper, sampleprof_error &Error) { - sampleprof::FunctionSamples Result; - Result.setName(Remapper(Samples.getName())); - Result.addTotalSamples(Samples.getTotalSamples()); - Result.addHeadSamples(Samples.getHeadSamples()); - for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - BodySample.second.getSamples()); - for (const auto &Target : BodySample.second.getCallTargets()) { - Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - Remapper(Target.first()), Target.second); - } - } - for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { - sampleprof::FunctionSamplesMap &Target = - Result.functionSamplesAt(CallsiteSamples.first); - for (const auto &Callsite : CallsiteSamples.second) { - sampleprof::FunctionSamples Remapped = - remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); - } - } - return Result; -} - -static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, - sampleprof::SPF_Text, - sampleprof::SPF_Compact_Binary, - sampleprof::SPF_Ext_Binary, - sampleprof::SPF_GCC, - sampleprof::SPF_Binary}; - -static std::unique_ptr -getInputFileBuf(const StringRef &InputFile) { - if (InputFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - return std::move(*BufOrError); -} - -static void populateProfileSymbolList(MemoryBuffer *Buffer, - sampleprof::ProfileSymbolList &PSL) { - if (!Buffer) - return; - - SmallVector SymbolVec; - StringRef Data = Buffer->getBuffer(); - Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - - for (StringRef symbol : SymbolVec) - PSL.add(symbol); -} - -static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, - ProfileFormat OutputFormat, - MemoryBuffer *Buffer, - sampleprof::ProfileSymbolList &WriterList, - bool CompressAllSections) { - populateProfileSymbolList(Buffer, WriterList); - if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) - warn("Profile Symbol list is not empty but the output format is not " - "ExtBinary format. The list will be lost in the output. "); - - Writer.setProfileSymbolList(&WriterList); - - if (CompressAllSections) { - if (OutputFormat != PF_Ext_Binary) { - warn("-compress-all-section is ignored. Specify -extbinary to enable it"); - } else { - auto ExtBinaryWriter = - static_cast(&Writer); - ExtBinaryWriter->setToCompressAllSections(); - } - } -} - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, - bool CompressAllSections, FailureMode FailMode) { - using namespace sampleprof; - StringMap ProfileMap; - SmallVector, 5> Readers; - LLVMContext Context; - sampleprof::ProfileSymbolList WriterList; - for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) { - warnOrExitGivenError(FailMode, EC, Input.Filename); - continue; - } - - // We need to keep the readers around until after all the files are - // read so that we do not lose the function names stored in each - // reader's memory. The function names are needed to write out the - // merged profile map. - Readers.push_back(std::move(ReaderOrErr.get())); - const auto Reader = Readers.back().get(); - if (std::error_code EC = Reader->read()) { - warnOrExitGivenError(FailMode, EC, Input.Filename); - Readers.pop_back(); - continue; - } - - StringMap &Profiles = Reader->getProfiles(); - for (StringMap::iterator I = Profiles.begin(), - E = Profiles.end(); - I != E; ++I) { - sampleprof_error Result = sampleprof_error::success; - FunctionSamples Remapped = - Remapper ? remapSamples(I->second, *Remapper, Result) - : FunctionSamples(); - FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getName(); - MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); - if (Result != sampleprof_error::success) { - std::error_code EC = make_error_code(Result); - handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); - } - } - - std::unique_ptr ReaderList = - Reader->getProfileSymbolList(); - if (ReaderList) - WriterList.merge(*ReaderList); - } - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); - // WriterList will have StringRef refering to string in Buffer. - // Make sure Buffer lives as long as WriterList. - auto Buffer = getInputFileBuf(ProfileSymbolListFile); - handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, - CompressAllSections); - Writer->write(ProfileMap); -} - -static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { - StringRef WeightStr, FileName; - std::tie(WeightStr, FileName) = WeightedFilename.split(','); - - uint64_t Weight; - if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); - - return {FileName, Weight}; -} - -static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { - StringRef Filename = WF.Filename; - uint64_t Weight = WF.Weight; - - // If it's STDIN just pass it on. - if (Filename == "-") { - WNI.push_back({Filename, Weight}); - return; - } - - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(Filename, Status); - if (!llvm::sys::fs::exists(Status)) - exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), - Filename); - // If it's a source file, collect it. - if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); - return; - } - - if (llvm::sys::fs::is_directory(Status)) { - std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; - F != E && !EC; F.increment(EC)) { - if (llvm::sys::fs::is_regular_file(F->path())) { - addWeightedInput(WNI, {F->path(), Weight}); - } - } - if (EC) - exitWithErrorCode(EC, Filename); - } -} - -static void parseInputFilenamesFile(MemoryBuffer *Buffer, - WeightedFileVector &WFV) { - if (!Buffer) - return; - - SmallVector Entries; - StringRef Data = Buffer->getBuffer(); - Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (const StringRef &FileWeightEntry : Entries) { - StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); - // Skip comments. - if (SanitizedEntry.startswith("#")) - continue; - // If there's no comma, it's an unweighted profile. - else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); - else - addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); - } -} - -static int merge_main(int argc, const char *argv[]) { - cl::list InputFilenames(cl::Positional, - cl::desc("")); - cl::list WeightedInputFilenames("weighted-input", - cl::desc(",")); - cl::opt InputFilenamesFile( - "input-files", cl::init(""), - cl::desc("Path to file containing newline-separated " - "[,] entries")); - cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), - cl::aliasopt(InputFilenamesFile)); - cl::opt DumpInputFileList( - "dump-input-file-list", cl::init(false), cl::Hidden, - cl::desc("Dump the list of input files and their weights, then exit")); - cl::opt RemappingFile("remapping-file", cl::value_desc("file"), - cl::desc("Symbol remapping file")); - cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), - cl::aliasopt(RemappingFile)); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt OutputFormat( - cl::desc("Format of output profile"), cl::init(PF_Binary), - cl::values( - clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), - clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - cl::opt FailureMode( - "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), - cl::values(clEnumValN(failIfAnyAreInvalid, "any", - "Fail if any profile is invalid."), - clEnumValN(failIfAllAreInvalid, "all", - "Fail only if all profiles are invalid."))); - cl::opt OutputSparse("sparse", cl::init(false), - cl::desc("Generate a sparse profile (only meaningful for -instr)")); - cl::opt NumThreads( - "num-threads", cl::init(0), - cl::desc("Number of merge threads to use (default: autodetect)")); - cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), - cl::aliasopt(NumThreads)); - cl::opt ProfileSymbolListFile( - "prof-sym-list", cl::init(""), - cl::desc("Path to file containing the list of function symbols " - "used to populate profile symbol list")); - cl::opt CompressAllSections( - "compress-all-sections", cl::init(false), cl::Hidden, - cl::desc("Compress all sections when writing the profile (only " - "meaningful for -extbinary)")); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); - for (StringRef WeightedFilename : WeightedInputFilenames) - addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); - - // Make sure that the file buffer stays alive for the duration of the - // weighted input vector's lifetime. - auto Buffer = getInputFileBuf(InputFilenamesFile); - parseInputFilenamesFile(Buffer.get(), WeightedInputs); - - if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + - sys::path::filename(argv[0]) + " -help"); - - if (DumpInputFileList) { - for (auto &WF : WeightedInputs) - outs() << WF.Weight << "," << WF.Filename << "\n"; - return 0; - } - - std::unique_ptr Remapper; - if (!RemappingFile.empty()) - Remapper = SymbolRemapper::create(RemappingFile); - - if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, OutputSparse, NumThreads, FailureMode); - else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, ProfileSymbolListFile, CompressAllSections, - FailureMode); - - return 0; -} - -/// Computer the overlap b/w profile BaseFilename and profile TestFilename. -static void overlapInstrProfile(const std::string &BaseFilename, - const std::string &TestFilename, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - WriterContext Context(false, ErrorLock, WriterErrorCodes); - WeightedFile WeightedInput{BaseFilename, 1}; - OverlapStats Overlap; - Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); - if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); - if (Overlap.Base.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; - exit(0); - } - if (Overlap.Test.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; - exit(0); - } - loadInput(WeightedInput, nullptr, &Context); - overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, - IsCS); - Overlap.dump(OS); -} - -static int overlap_main(int argc, const char *argv[]) { - cl::opt BaseFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt TestFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt Output("output", cl::value_desc("output"), cl::init("-"), - cl::desc("Output file")); - cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(-1), - cl::desc( - "Function level overlap information for every function in test " - "profile with max count value greater then the parameter value")); - cl::opt FuncNameFilter( - "function", - cl::desc("Function level overlap information for matching functions")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); - - std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); - if (EC) - exitWithErrorCode(EC, Output); - - overlapInstrProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, - IsCS); - - return 0; -} - -typedef struct ValueSitesStats { - ValueSitesStats() - : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), - TotalNumValues(0) {} - uint64_t TotalNumValueSites; - uint64_t TotalNumValueSitesWithValueProfile; - uint64_t TotalNumValues; - std::vector ValueSitesHistogram; -} ValueSitesStats; - -static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, - ValueSitesStats &Stats, raw_fd_ostream &OS, - InstrProfSymtab *Symtab) { - uint32_t NS = Func.getNumValueSites(VK); - Stats.TotalNumValueSites += NS; - for (size_t I = 0; I < NS; ++I) { - uint32_t NV = Func.getNumValueDataForSite(VK, I); - std::unique_ptr VD = Func.getValueForSite(VK, I); - Stats.TotalNumValues += NV; - if (NV) { - Stats.TotalNumValueSitesWithValueProfile++; - if (NV > Stats.ValueSitesHistogram.size()) - Stats.ValueSitesHistogram.resize(NV, 0); - Stats.ValueSitesHistogram[NV - 1]++; - } - - uint64_t SiteSum = 0; - for (uint32_t V = 0; V < NV; V++) - SiteSum += VD[V].Count; - if (SiteSum == 0) - SiteSum = 1; - - for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << format("%2u", I) << ", "; - if (Symtab == nullptr) - OS << format("%4" PRIu64, VD[V].Value); - else - OS << Symtab->getFuncName(VD[V].Value); - OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" - << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; - } - } -} - -static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, - ValueSitesStats &Stats) { - OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; - OS << " Total number of sites with values: " - << Stats.TotalNumValueSitesWithValueProfile << "\n"; - OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; - - OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; - for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { - if (Stats.ValueSitesHistogram[I] > 0) - OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; - } -} - -static int showInstrProfile(const std::string &Filename, bool ShowCounts, - uint32_t TopN, bool ShowIndirectCallTargets, - bool ShowMemOPSizes, bool ShowDetailedSummary, - std::vector DetailedSummaryCutoffs, - bool ShowAllFunctions, bool ShowCS, - uint64_t ValueCutoff, bool OnlyListBelow, - const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { - auto ReaderOrErr = InstrProfReader::create(Filename); - std::vector Cutoffs = std::move(DetailedSummaryCutoffs); - if (ShowDetailedSummary && Cutoffs.empty()) { - Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; - } - InstrProfSummaryBuilder Builder(std::move(Cutoffs)); - if (Error E = ReaderOrErr.takeError()) - exitWithError(std::move(E), Filename); - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRInstr = Reader->isIRLevelProfile(); - size_t ShownFunctions = 0; - size_t BelowCutoffFunctions = 0; - int NumVPKind = IPVK_Last - IPVK_First + 1; - std::vector VPStats(NumVPKind); - - auto MinCmp = [](const std::pair &v1, - const std::pair &v2) { - return v1.second > v2.second; - }; - - std::priority_queue, - std::vector>, - decltype(MinCmp)> - HottestFuncs(MinCmp); - - if (!TextFormat && OnlyListBelow) { - OS << "The list of functions with the maximum counter less than " - << ValueCutoff << ":\n"; - } - - // Add marker so that IR-level instrumentation round-trips properly. - if (TextFormat && IsIRInstr) - OS << ":ir\n"; - - for (const auto &Func : *Reader) { - if (Reader->isIRLevelProfile()) { - bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); - if (FuncIsCS != ShowCS) - continue; - } - bool Show = - ShowAllFunctions || (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); - - bool doTextFormatDump = (Show && TextFormat); - - if (doTextFormatDump) { - InstrProfSymtab &Symtab = Reader->getSymtab(); - InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, - OS); - continue; - } - - assert(Func.Counts.size() > 0 && "function missing entry counter"); - Builder.addRecord(Func); - - uint64_t FuncMax = 0; - uint64_t FuncSum = 0; - for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { - FuncMax = std::max(FuncMax, Func.Counts[I]); - FuncSum += Func.Counts[I]; - } - - if (FuncMax < ValueCutoff) { - ++BelowCutoffFunctions; - if (OnlyListBelow) { - OS << " " << Func.Name << ": (Max = " << FuncMax - << " Sum = " << FuncSum << ")\n"; - } - continue; - } else if (OnlyListBelow) - continue; - - if (TopN) { - if (HottestFuncs.size() == TopN) { - if (HottestFuncs.top().second < FuncMax) { - HottestFuncs.pop(); - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - } else - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - - if (Show) { - if (!ShownFunctions) - OS << "Counters:\n"; - - ++ShownFunctions; - - OS << " " << Func.Name << ":\n" - << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" - << " Counters: " << Func.Counts.size() << "\n"; - if (!IsIRInstr) - OS << " Function count: " << Func.Counts[0] << "\n"; - - if (ShowIndirectCallTargets) - OS << " Indirect Call Site Count: " - << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; - - uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); - if (ShowMemOPSizes && NumMemOPCalls > 0) - OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls - << "\n"; - - if (ShowCounts) { - OS << " Block counts: ["; - size_t Start = (IsIRInstr ? 0 : 1); - for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { - OS << (I == Start ? "" : ", ") << Func.Counts[I]; - } - OS << "]\n"; - } - - if (ShowIndirectCallTargets) { - OS << " Indirect Target Results:\n"; - traverseAllValueSites(Func, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget], OS, - &(Reader->getSymtab())); - } - - if (ShowMemOPSizes && NumMemOPCalls > 0) { - OS << " Memory Intrinsic Size Results:\n"; - traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, - nullptr); - } - } - } - if (Reader->hasError()) - exitWithError(Reader->getError(), Filename); - - if (TextFormat) - return 0; - std::unique_ptr PS(Builder.getSummary()); - OS << "Instrumentation level: " - << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; - if (ShowAllFunctions || !ShowFunction.empty()) - OS << "Functions shown: " << ShownFunctions << "\n"; - OS << "Total functions: " << PS->getNumFunctions() << "\n"; - if (ValueCutoff > 0) { - OS << "Number of functions with maximum count (< " << ValueCutoff - << "): " << BelowCutoffFunctions << "\n"; - OS << "Number of functions with maximum count (>= " << ValueCutoff - << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; - } - OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; - OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - - if (TopN) { - std::vector> SortedHottestFuncs; - while (!HottestFuncs.empty()) { - SortedHottestFuncs.emplace_back(HottestFuncs.top()); - HottestFuncs.pop(); - } - OS << "Top " << TopN - << " functions with the largest internal block counts: \n"; - for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) - OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; - } - - if (ShownFunctions && ShowIndirectCallTargets) { - OS << "Statistics for indirect call sites profile:\n"; - showValueSitesStats(OS, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget]); - } - - if (ShownFunctions && ShowMemOPSizes) { - OS << "Statistics for memory intrinsic calls sizes profile:\n"; - showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); - } - - if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } - } - return 0; -} - -static void showSectionInfo(sampleprof::SampleProfileReader *Reader, - raw_fd_ostream &OS) { - if (!Reader->dumpSectionInfo(OS)) { - WithColor::warning() << "-show-sec-info-only is only supported for " - << "sample profile in extbinary format and is " - << "ignored for other formats.\n"; - return; - } -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, raw_fd_ostream &OS) { - using namespace sampleprof; - LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Filename); - - auto Reader = std::move(ReaderOrErr.get()); - - if (ShowSectionInfoOnly) { - showSectionInfo(Reader.get(), OS); - return 0; - } - - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); - - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else - Reader->dumpFunctionProfile(ShowFunction, OS); - - if (ShowProfileSymbolList) { - std::unique_ptr ReaderList = - Reader->getProfileSymbolList(); - ReaderList->dump(OS); - } - - return 0; -} - -static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt TextFormat( - "text", cl::init(false), - cl::desc("Show instr profile data in text dump format")); - cl::opt ShowIndirectCallTargets( - "ic-targets", cl::init(false), - cl::desc("Show indirect call site target values for shown functions")); - cl::opt ShowMemOPSizes( - "memop-sizes", cl::init(false), - cl::desc("Show the profiled sizes of the memory intrinsic calls " - "for shown functions")); - cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), - cl::desc("Show detailed profile summary")); - cl::list DetailedSummaryCutoffs( - cl::CommaSeparated, "detailed-summary-cutoffs", - cl::desc( - "Cutoff percentages (times 10000) for generating detailed summary"), - cl::value_desc("800000,901000,999999")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowCS("showcs", cl::init(false), - cl::desc("Show context sensitive counts")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt TopNFunctions( - "topn", cl::init(0), - cl::desc("Show the list of functions with the largest internal counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(0), - cl::desc("Set the count value cutoff. Functions with the maximum count " - "less than this value will not be printed out. (Default is 0)")); - cl::opt OnlyListBelow( - "list-below-cutoff", cl::init(false), - cl::desc("Only output names of functions whose max count values are " - "below the cutoff value")); - cl::opt ShowProfileSymbolList( - "show-prof-sym-list", cl::init(false), - cl::desc("Show profile symbol list if it exists in the profile. ")); - cl::opt ShowSectionInfoOnly( - "show-sec-info-only", cl::init(false), - cl::desc("Show the information of each section in the sample profile. " - "The flag is only usable when the sample profile is in " - "extbinary format")); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - - if (OutputFilename.empty()) - OutputFilename = "-"; - - if (!Filename.compare(OutputFilename)) { - errs() << sys::path::filename(argv[0]) - << ": Input file name cannot be the same as the output file name!\n"; - return 1; - } - - std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - if (ShowAllFunctions && !ShowFunction.empty()) - WithColor::warning() << "-function argument ignored: showing all functions\n"; - - if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); - else - return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, OS); -} - -int main(int argc, const char *argv[]) { - InitLLVM X(argc, argv); - - StringRef ProgName(sys::path::filename(argv[0])); - if (argc > 1) { - int (*func)(int, const char *[]) = nullptr; - - if (strcmp(argv[1], "merge") == 0) - func = merge_main; - else if (strcmp(argv[1], "show") == 0) - func = show_main; - else if (strcmp(argv[1], "overlap") == 0) - func = overlap_main; - - if (func) { - std::string Invocation(ProgName.str() + " " + argv[1]); - argv[1] = Invocation.c_str(); - return func(argc - 1, argv + 1); - } - - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || - strcmp(argv[1], "--help") == 0) { - - errs() << "OVERVIEW: LLVM profile data tools\n\n" - << "USAGE: " << ProgName << " [args...]\n" - << "USAGE: " << ProgName << " -help\n\n" - << "See each individual command --help for more details.\n" - << "Available commands: merge, show, overlap\n"; - return 0; - } - } - - if (argc < 2) - errs() << ProgName << ": No command specified!\n"; - else - errs() << ProgName << ": Unknown command!\n"; - - errs() << "USAGE: " << ProgName << " [args...]\n"; - return 1; -} diff --git a/tools/ldc-profdata/llvm-profdata-9.0.cpp b/tools/ldc-profdata/llvm-profdata-9.0.cpp deleted file mode 100644 index 16d3ebe3fcb..00000000000 --- a/tools/ldc-profdata/llvm-profdata-9.0.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// llvm-profdata merges .profdata files. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/ProfileData/InstrProfReader.h" -#include "llvm/ProfileData/InstrProfWriter.h" -#include "llvm/ProfileData/ProfileCommon.h" -#include "llvm/ProfileData/SampleProfReader.h" -#include "llvm/ProfileData/SampleProfWriter.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include - -using namespace llvm; - -enum ProfileFormat { - PF_None = 0, - PF_Text, - PF_Compact_Binary, - PF_GCC, - PF_Binary -}; - -static void warn(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::warning(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; -} - -static void exitWithError(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::error(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; - ::exit(1); -} - -static void exitWithError(Error E, StringRef Whence = "") { - if (E.isA()) { - handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { - instrprof_error instrError = IPE.get(); - StringRef Hint = ""; - if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; - } - exitWithError(IPE.message(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); -} - -namespace { -enum ProfileKinds { instr, sample }; -} - -static void handleMergeWriterError(Error E, StringRef WhenceFile = "", - StringRef WhenceFunction = "", - bool ShowHint = true) { - if (!WhenceFile.empty()) - errs() << WhenceFile << ": "; - if (!WhenceFunction.empty()) - errs() << WhenceFunction << ": "; - - auto IPE = instrprof_error::success; - E = handleErrors(std::move(E), - [&IPE](std::unique_ptr E) -> Error { - IPE = E->get(); - return Error(std::move(E)); - }); - errs() << toString(std::move(E)) << "\n"; - - if (ShowHint) { - StringRef Hint = ""; - if (IPE != instrprof_error::success) { - switch (IPE) { - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::value_site_count_mismatch: - Hint = "Make sure that all profile data to be merged is generated " - "from the same binary."; - break; - default: - break; - } - } - - if (!Hint.empty()) - errs() << Hint << "\n"; - } -} - -namespace { -/// A remapper from original symbol names to new symbol names based on a file -/// containing a list of mappings from old name to new name. -class SymbolRemapper { - std::unique_ptr File; - DenseMap RemappingTable; - -public: - /// Build a SymbolRemapper from a file containing a list of old/new symbols. - static std::unique_ptr create(StringRef InputFile) { - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - auto Remapper = llvm::make_unique(); - Remapper->File = std::move(BufOrError.get()); - - for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); - !LineIt.is_at_eof(); ++LineIt) { - std::pair Parts = LineIt->split(' '); - if (Parts.first.empty() || Parts.second.empty() || - Parts.second.count(' ')) { - exitWithError("unexpected line in remapping file", - (InputFile + ":" + Twine(LineIt.line_number())).str(), - "expected 'old_symbol new_symbol'"); - } - Remapper->RemappingTable.insert(Parts); - } - return Remapper; - } - - /// Attempt to map the given old symbol into a new symbol. - /// - /// \return The new symbol, or \p Name if no such symbol was found. - StringRef operator()(StringRef Name) { - StringRef New = RemappingTable.lookup(Name); - return New.empty() ? Name : New; - } -}; -} - -struct WeightedFile { - std::string Filename; - uint64_t Weight; -}; -typedef SmallVector WeightedFileVector; - -/// Keep track of merged data and reported errors. -struct WriterContext { - std::mutex Lock; - InstrProfWriter Writer; - Error Err; - std::string ErrWhence; - std::mutex &ErrLock; - SmallSet &WriterErrorCodes; - - WriterContext(bool IsSparse, std::mutex &ErrLock, - SmallSet &WriterErrorCodes) - : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""), - ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} -}; - -/// Determine whether an error is fatal for profile merging. -static bool isFatalError(instrprof_error IPE) { - switch (IPE) { - default: - return true; - case instrprof_error::success: - case instrprof_error::eof: - case instrprof_error::unknown_function: - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::counter_overflow: - case instrprof_error::value_site_count_mismatch: - return false; - } -} - -/// Computer the overlap b/w profile BaseFilename and TestFileName, -/// and store the program level result to Overlap. -static void overlapInput(const std::string &BaseFilename, - const std::string &TestFilename, WriterContext *WC, - OverlapStats &Overlap, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - auto ReaderOrErr = InstrProfReader::create(TestFilename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error(IPE); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - for (auto &I : *Reader) { - OverlapStats FuncOverlap(OverlapStats::FunctionLevel); - FuncOverlap.setFuncInfo(I.Name, I.Hash); - - WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); - FuncOverlap.dump(OS); - } -} - -/// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, - WriterContext *WC) { - std::unique_lock CtxGuard{WC->Lock}; - - // If there's a pending hard error, don't do more work. - if (WC->Err) - return; - - // Copy the filename, because llvm::ThreadPool copied the input "const - // WeightedFile &" by value, making a reference to the filename within it - // invalid outside of this packaged task. - WC->ErrWhence = Input.Filename; - - auto ReaderOrErr = InstrProfReader::create(Input.Filename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error(IPE); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRProfile = Reader->isIRLevelProfile(); - bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Err = make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()); - return; - } - - for (auto &I : *Reader) { - if (Remapper) - I.Name = (*Remapper)(I.Name); - const StringRef FuncName = I.Name; - bool Reported = false; - WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - // Only show hint the first time an error occurs. - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{WC->ErrLock}; - bool firstTime = WC->WriterErrorCodes.insert(IPE).second; - handleMergeWriterError(make_error(IPE), Input.Filename, - FuncName, firstTime); - }); - } - if (Reader->hasError()) { - if (Error E = Reader->getError()) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (isFatalError(IPE)) - WC->Err = make_error(IPE); - } - } -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - // If we've already seen a hard error, continuing with the merge would - // clobber it. - if (Dst->Err || Src->Err) - return; - - bool Reported = false; - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - Dst->Err = std::move(E); - }); -} - -static void mergeInstrProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); - - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); - - // Initialize the writer contexts. - SmallVector, 4> Contexts; - for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(llvm::make_unique( - OutputSparse, ErrorLock, WriterErrorCodes)); - - if (NumThreads == 1) { - for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); - } else { - ThreadPool Pool(NumThreads); - - // Load the inputs in parallel (N/NumThreads serial steps). - unsigned Ctx = 0; - for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); - Ctx = (Ctx + 1) % NumThreads; - } - Pool.wait(); - - // Merge the writer contexts together (~ lg(NumThreads) serial steps). - unsigned Mid = Contexts.size() / 2; - unsigned End = Contexts.size(); - assert(Mid > 0 && "Expected more than one context"); - do { - for (unsigned I = 0; I < Mid; ++I) - Pool.async(mergeWriterContexts, Contexts[I].get(), - Contexts[I + Mid].get()); - Pool.wait(); - if (End & 1) { - Pool.async(mergeWriterContexts, Contexts[0].get(), - Contexts[End - 1].get()); - Pool.wait(); - } - End = Mid; - Mid /= 2; - } while (Mid > 0); - } - - // Handle deferred hard errors encountered during merging. - for (std::unique_ptr &WC : Contexts) { - if (!WC->Err) - continue; - if (!WC->Err.isA()) - exitWithError(std::move(WC->Err), WC->ErrWhence); - - instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); - if (isFatalError(IPE)) - exitWithError(make_error(IPE), WC->ErrWhence); - else - warn(toString(make_error(IPE)), - WC->ErrWhence); - } - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// Make a copy of the given function samples with all symbol names remapped -/// by the provided symbol remapper. -static sampleprof::FunctionSamples -remapSamples(const sampleprof::FunctionSamples &Samples, - SymbolRemapper &Remapper, sampleprof_error &Error) { - sampleprof::FunctionSamples Result; - Result.setName(Remapper(Samples.getName())); - Result.addTotalSamples(Samples.getTotalSamples()); - Result.addHeadSamples(Samples.getHeadSamples()); - for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - BodySample.second.getSamples()); - for (const auto &Target : BodySample.second.getCallTargets()) { - Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - Remapper(Target.first()), Target.second); - } - } - for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { - sampleprof::FunctionSamplesMap &Target = - Result.functionSamplesAt(CallsiteSamples.first); - for (const auto &Callsite : CallsiteSamples.second) { - sampleprof::FunctionSamples Remapped = - remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); - } - } - return Result; -} - -static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, - sampleprof::SPF_GCC, sampleprof::SPF_Binary}; - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat) { - using namespace sampleprof; - StringMap ProfileMap; - SmallVector, 5> Readers; - LLVMContext Context; - for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Input.Filename); - - // We need to keep the readers around until after all the files are - // read so that we do not lose the function names stored in each - // reader's memory. The function names are needed to write out the - // merged profile map. - Readers.push_back(std::move(ReaderOrErr.get())); - const auto Reader = Readers.back().get(); - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Input.Filename); - - StringMap &Profiles = Reader->getProfiles(); - for (StringMap::iterator I = Profiles.begin(), - E = Profiles.end(); - I != E; ++I) { - sampleprof_error Result = sampleprof_error::success; - FunctionSamples Remapped = - Remapper ? remapSamples(I->second, *Remapper, Result) - : FunctionSamples(); - FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getName(); - MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); - if (Result != sampleprof_error::success) { - std::error_code EC = make_error_code(Result); - handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); - } - } - } - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); - Writer->write(ProfileMap); -} - -static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { - StringRef WeightStr, FileName; - std::tie(WeightStr, FileName) = WeightedFilename.split(','); - - uint64_t Weight; - if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); - - return {FileName, Weight}; -} - -static std::unique_ptr -getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) { - if (InputFilenamesFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFilenamesFile); - - return std::move(*BufOrError); -} - -static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { - StringRef Filename = WF.Filename; - uint64_t Weight = WF.Weight; - - // If it's STDIN just pass it on. - if (Filename == "-") { - WNI.push_back({Filename, Weight}); - return; - } - - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(Filename, Status); - if (!llvm::sys::fs::exists(Status)) - exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), - Filename); - // If it's a source file, collect it. - if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); - return; - } - - if (llvm::sys::fs::is_directory(Status)) { - std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; - F != E && !EC; F.increment(EC)) { - if (llvm::sys::fs::is_regular_file(F->path())) { - addWeightedInput(WNI, {F->path(), Weight}); - } - } - if (EC) - exitWithErrorCode(EC, Filename); - } -} - -static void parseInputFilenamesFile(MemoryBuffer *Buffer, - WeightedFileVector &WFV) { - if (!Buffer) - return; - - SmallVector Entries; - StringRef Data = Buffer->getBuffer(); - Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (const StringRef &FileWeightEntry : Entries) { - StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); - // Skip comments. - if (SanitizedEntry.startswith("#")) - continue; - // If there's no comma, it's an unweighted profile. - else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); - else - addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); - } -} - -static int merge_main(int argc, const char *argv[]) { - cl::list InputFilenames(cl::Positional, - cl::desc("")); - cl::list WeightedInputFilenames("weighted-input", - cl::desc(",")); - cl::opt InputFilenamesFile( - "input-files", cl::init(""), - cl::desc("Path to file containing newline-separated " - "[,] entries")); - cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), - cl::aliasopt(InputFilenamesFile)); - cl::opt DumpInputFileList( - "dump-input-file-list", cl::init(false), cl::Hidden, - cl::desc("Dump the list of input files and their weights, then exit")); - cl::opt RemappingFile("remapping-file", cl::value_desc("file"), - cl::desc("Symbol remapping file")); - cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), - cl::aliasopt(RemappingFile)); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt OutputFormat( - cl::desc("Format of output profile"), cl::init(PF_Binary), - cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - cl::opt OutputSparse("sparse", cl::init(false), - cl::desc("Generate a sparse profile (only meaningful for -instr)")); - cl::opt NumThreads( - "num-threads", cl::init(0), - cl::desc("Number of merge threads to use (default: autodetect)")); - cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), - cl::aliasopt(NumThreads)); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); - for (StringRef WeightedFilename : WeightedInputFilenames) - addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); - - // Make sure that the file buffer stays alive for the duration of the - // weighted input vector's lifetime. - auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile); - parseInputFilenamesFile(Buffer.get(), WeightedInputs); - - if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + - sys::path::filename(argv[0]) + " -help"); - - if (DumpInputFileList) { - for (auto &WF : WeightedInputs) - outs() << WF.Weight << "," << WF.Filename << "\n"; - return 0; - } - - std::unique_ptr Remapper; - if (!RemappingFile.empty()) - Remapper = SymbolRemapper::create(RemappingFile); - - if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, OutputSparse, NumThreads); - else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat); - - return 0; -} - -/// Computer the overlap b/w profile BaseFilename and profile TestFilename. -static void overlapInstrProfile(const std::string &BaseFilename, - const std::string &TestFilename, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - WriterContext Context(false, ErrorLock, WriterErrorCodes); - WeightedFile WeightedInput{BaseFilename, 1}; - OverlapStats Overlap; - Error E = Overlap.accumuateCounts(BaseFilename, TestFilename, IsCS); - if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); - if (Overlap.Base.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; - exit(0); - } - if (Overlap.Test.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; - exit(0); - } - loadInput(WeightedInput, nullptr, &Context); - overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, - IsCS); - Overlap.dump(OS); -} - -static int overlap_main(int argc, const char *argv[]) { - cl::opt BaseFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt TestFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt Output("output", cl::value_desc("output"), cl::init("-"), - cl::desc("Output file")); - cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(-1), - cl::desc( - "Function level overlap information for every function in test " - "profile with max count value greater then the parameter value")); - cl::opt FuncNameFilter( - "function", - cl::desc("Function level overlap information for matching functions")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); - - std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::F_Text); - if (EC) - exitWithErrorCode(EC, Output); - - overlapInstrProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, - IsCS); - - return 0; -} - -typedef struct ValueSitesStats { - ValueSitesStats() - : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), - TotalNumValues(0) {} - uint64_t TotalNumValueSites; - uint64_t TotalNumValueSitesWithValueProfile; - uint64_t TotalNumValues; - std::vector ValueSitesHistogram; -} ValueSitesStats; - -static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, - ValueSitesStats &Stats, raw_fd_ostream &OS, - InstrProfSymtab *Symtab) { - uint32_t NS = Func.getNumValueSites(VK); - Stats.TotalNumValueSites += NS; - for (size_t I = 0; I < NS; ++I) { - uint32_t NV = Func.getNumValueDataForSite(VK, I); - std::unique_ptr VD = Func.getValueForSite(VK, I); - Stats.TotalNumValues += NV; - if (NV) { - Stats.TotalNumValueSitesWithValueProfile++; - if (NV > Stats.ValueSitesHistogram.size()) - Stats.ValueSitesHistogram.resize(NV, 0); - Stats.ValueSitesHistogram[NV - 1]++; - } - - uint64_t SiteSum = 0; - for (uint32_t V = 0; V < NV; V++) - SiteSum += VD[V].Count; - if (SiteSum == 0) - SiteSum = 1; - - for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << format("%2u", I) << ", "; - if (Symtab == nullptr) - OS << format("%4" PRIu64, VD[V].Value); - else - OS << Symtab->getFuncName(VD[V].Value); - OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" - << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; - } - } -} - -static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, - ValueSitesStats &Stats) { - OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; - OS << " Total number of sites with values: " - << Stats.TotalNumValueSitesWithValueProfile << "\n"; - OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; - - OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; - for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { - if (Stats.ValueSitesHistogram[I] > 0) - OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; - } -} - -static int showInstrProfile(const std::string &Filename, bool ShowCounts, - uint32_t TopN, bool ShowIndirectCallTargets, - bool ShowMemOPSizes, bool ShowDetailedSummary, - std::vector DetailedSummaryCutoffs, - bool ShowAllFunctions, bool ShowCS, - uint64_t ValueCutoff, bool OnlyListBelow, - const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { - auto ReaderOrErr = InstrProfReader::create(Filename); - std::vector Cutoffs = std::move(DetailedSummaryCutoffs); - if (ShowDetailedSummary && Cutoffs.empty()) { - Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; - } - InstrProfSummaryBuilder Builder(std::move(Cutoffs)); - if (Error E = ReaderOrErr.takeError()) - exitWithError(std::move(E), Filename); - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRInstr = Reader->isIRLevelProfile(); - size_t ShownFunctions = 0; - size_t BelowCutoffFunctions = 0; - int NumVPKind = IPVK_Last - IPVK_First + 1; - std::vector VPStats(NumVPKind); - - auto MinCmp = [](const std::pair &v1, - const std::pair &v2) { - return v1.second > v2.second; - }; - - std::priority_queue, - std::vector>, - decltype(MinCmp)> - HottestFuncs(MinCmp); - - if (!TextFormat && OnlyListBelow) { - OS << "The list of functions with the maximum counter less than " - << ValueCutoff << ":\n"; - } - - // Add marker so that IR-level instrumentation round-trips properly. - if (TextFormat && IsIRInstr) - OS << ":ir\n"; - - for (const auto &Func : *Reader) { - if (Reader->isIRLevelProfile()) { - bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); - if (FuncIsCS != ShowCS) - continue; - } - bool Show = - ShowAllFunctions || (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); - - bool doTextFormatDump = (Show && TextFormat); - - if (doTextFormatDump) { - InstrProfSymtab &Symtab = Reader->getSymtab(); - InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, - OS); - continue; - } - - assert(Func.Counts.size() > 0 && "function missing entry counter"); - Builder.addRecord(Func); - - uint64_t FuncMax = 0; - uint64_t FuncSum = 0; - for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { - FuncMax = std::max(FuncMax, Func.Counts[I]); - FuncSum += Func.Counts[I]; - } - - if (FuncMax < ValueCutoff) { - ++BelowCutoffFunctions; - if (OnlyListBelow) { - OS << " " << Func.Name << ": (Max = " << FuncMax - << " Sum = " << FuncSum << ")\n"; - } - continue; - } else if (OnlyListBelow) - continue; - - if (TopN) { - if (HottestFuncs.size() == TopN) { - if (HottestFuncs.top().second < FuncMax) { - HottestFuncs.pop(); - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - } else - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - - if (Show) { - if (!ShownFunctions) - OS << "Counters:\n"; - - ++ShownFunctions; - - OS << " " << Func.Name << ":\n" - << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" - << " Counters: " << Func.Counts.size() << "\n"; - if (!IsIRInstr) - OS << " Function count: " << Func.Counts[0] << "\n"; - - if (ShowIndirectCallTargets) - OS << " Indirect Call Site Count: " - << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; - - uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); - if (ShowMemOPSizes && NumMemOPCalls > 0) - OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls - << "\n"; - - if (ShowCounts) { - OS << " Block counts: ["; - size_t Start = (IsIRInstr ? 0 : 1); - for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { - OS << (I == Start ? "" : ", ") << Func.Counts[I]; - } - OS << "]\n"; - } - - if (ShowIndirectCallTargets) { - OS << " Indirect Target Results:\n"; - traverseAllValueSites(Func, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget], OS, - &(Reader->getSymtab())); - } - - if (ShowMemOPSizes && NumMemOPCalls > 0) { - OS << " Memory Intrinsic Size Results:\n"; - traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, - nullptr); - } - } - } - if (Reader->hasError()) - exitWithError(Reader->getError(), Filename); - - if (TextFormat) - return 0; - std::unique_ptr PS(Builder.getSummary()); - OS << "Instrumentation level: " - << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; - if (ShowAllFunctions || !ShowFunction.empty()) - OS << "Functions shown: " << ShownFunctions << "\n"; - OS << "Total functions: " << PS->getNumFunctions() << "\n"; - if (ValueCutoff > 0) { - OS << "Number of functions with maximum count (< " << ValueCutoff - << "): " << BelowCutoffFunctions << "\n"; - OS << "Number of functions with maximum count (>= " << ValueCutoff - << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; - } - OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; - OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - - if (TopN) { - std::vector> SortedHottestFuncs; - while (!HottestFuncs.empty()) { - SortedHottestFuncs.emplace_back(HottestFuncs.top()); - HottestFuncs.pop(); - } - OS << "Top " << TopN - << " functions with the largest internal block counts: \n"; - for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) - OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; - } - - if (ShownFunctions && ShowIndirectCallTargets) { - OS << "Statistics for indirect call sites profile:\n"; - showValueSitesStats(OS, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget]); - } - - if (ShownFunctions && ShowMemOPSizes) { - OS << "Statistics for memory intrinsic calls sizes profile:\n"; - showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); - } - - if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } - } - return 0; -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - raw_fd_ostream &OS) { - using namespace sampleprof; - LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Filename); - - auto Reader = std::move(ReaderOrErr.get()); - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); - - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else - Reader->dumpFunctionProfile(ShowFunction, OS); - - return 0; -} - -static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt TextFormat( - "text", cl::init(false), - cl::desc("Show instr profile data in text dump format")); - cl::opt ShowIndirectCallTargets( - "ic-targets", cl::init(false), - cl::desc("Show indirect call site target values for shown functions")); - cl::opt ShowMemOPSizes( - "memop-sizes", cl::init(false), - cl::desc("Show the profiled sizes of the memory intrinsic calls " - "for shown functions")); - cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), - cl::desc("Show detailed profile summary")); - cl::list DetailedSummaryCutoffs( - cl::CommaSeparated, "detailed-summary-cutoffs", - cl::desc( - "Cutoff percentages (times 10000) for generating detailed summary"), - cl::value_desc("800000,901000,999999")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowCS("showcs", cl::init(false), - cl::desc("Show context sensitive counts")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt TopNFunctions( - "topn", cl::init(0), - cl::desc("Show the list of functions with the largest internal counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(0), - cl::desc("Set the count value cutoff. Functions with the maximum count " - "less than this value will not be printed out. (Default is 0)")); - cl::opt OnlyListBelow( - "list-below-cutoff", cl::init(false), - cl::desc("Only output names of functions whose max count values are " - "below the cutoff value")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - - if (OutputFilename.empty()) - OutputFilename = "-"; - - if (!Filename.compare(OutputFilename)) { - errs() << sys::path::filename(argv[0]) - << ": Input file name cannot be the same as the output file name!\n"; - return 1; - } - - std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - if (ShowAllFunctions && !ShowFunction.empty()) - WithColor::warning() << "-function argument ignored: showing all functions\n"; - - if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); - else - return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, OS); -} - -int main(int argc, const char *argv[]) { - InitLLVM X(argc, argv); - - StringRef ProgName(sys::path::filename(argv[0])); - if (argc > 1) { - int (*func)(int, const char *[]) = nullptr; - - if (strcmp(argv[1], "merge") == 0) - func = merge_main; - else if (strcmp(argv[1], "show") == 0) - func = show_main; - else if (strcmp(argv[1], "overlap") == 0) - func = overlap_main; - - if (func) { - std::string Invocation(ProgName.str() + " " + argv[1]); - argv[1] = Invocation.c_str(); - return func(argc - 1, argv + 1); - } - - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || - strcmp(argv[1], "--help") == 0) { - - errs() << "OVERVIEW: LLVM profile data tools\n\n" - << "USAGE: " << ProgName << " [args...]\n" - << "USAGE: " << ProgName << " -help\n\n" - << "See each individual command --help for more details.\n" - << "Available commands: merge, show, overlap\n"; - return 0; - } - } - - if (argc < 2) - errs() << ProgName << ": No command specified!\n"; - else - errs() << ProgName << ": Unknown command!\n"; - - errs() << "USAGE: " << ProgName << " [args...]\n"; - return 1; -} diff --git a/utils/FileCheck-10.cpp b/utils/FileCheck-10.cpp deleted file mode 100644 index 6f5791354ec..00000000000 --- a/utils/FileCheck-10.cpp +++ /dev/null @@ -1,664 +0,0 @@ -//===- FileCheck.cpp - Check that File's Contents match what is expected --===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// FileCheck does a line-by line check of a file that validates whether it -// contains the expected content. This is useful for regression tests etc. -// -// This program exits with an exit status of 2 on error, exit status of 0 if -// the file matched the expected contents, and exit status of 1 if it did not -// contain the expected contents. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/FileCheck.h" -#include -using namespace llvm; - -static cl::extrahelp FileCheckOptsEnv( - "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n" - "from the command line.\n"); - -static cl::opt - CheckFilename(cl::Positional, cl::desc(""), cl::Optional); - -static cl::opt - InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), - cl::init("-"), cl::value_desc("filename")); - -static cl::list CheckPrefixes( - "check-prefix", - cl::desc("Prefix to use from check file (defaults to 'CHECK')")); -static cl::alias CheckPrefixesAlias( - "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, - cl::NotHidden, - cl::desc( - "Alias for -check-prefix permitting multiple comma separated values")); - -static cl::opt NoCanonicalizeWhiteSpace( - "strict-whitespace", - cl::desc("Do not treat all horizontal whitespace as equivalent")); - -static cl::opt IgnoreCase( - "ignore-case", - cl::desc("Use case-insensitive matching")); - -static cl::list ImplicitCheckNot( - "implicit-check-not", - cl::desc("Add an implicit negative check with this pattern to every\n" - "positive check. This can be used to ensure that no instances of\n" - "this pattern occur which are not matched by a positive pattern"), - cl::value_desc("pattern")); - -static cl::list - GlobalDefines("D", cl::AlwaysPrefix, - cl::desc("Define a variable to be used in capture patterns."), - cl::value_desc("VAR=VALUE")); - -static cl::opt AllowEmptyInput( - "allow-empty", cl::init(false), - cl::desc("Allow the input file to be empty. This is useful when making\n" - "checks that some error message does not occur, for example.")); - -static cl::opt MatchFullLines( - "match-full-lines", cl::init(false), - cl::desc("Require all positive matches to cover an entire input line.\n" - "Allows leading and trailing whitespace if --strict-whitespace\n" - "is not also passed.")); - -static cl::opt EnableVarScope( - "enable-var-scope", cl::init(false), - cl::desc("Enables scope for regex variables. Variables with names that\n" - "do not start with '$' will be reset at the beginning of\n" - "each CHECK-LABEL block.")); - -static cl::opt AllowDeprecatedDagOverlap( - "allow-deprecated-dag-overlap", cl::init(false), - cl::desc("Enable overlapping among matches in a group of consecutive\n" - "CHECK-DAG directives. This option is deprecated and is only\n" - "provided for convenience as old tests are migrated to the new\n" - "non-overlapping CHECK-DAG implementation.\n")); - -static cl::opt Verbose( - "v", cl::init(false), - cl::desc("Print directive pattern matches, or add them to the input dump\n" - "if enabled.\n")); - -static cl::opt VerboseVerbose( - "vv", cl::init(false), - cl::desc("Print information helpful in diagnosing internal FileCheck\n" - "issues, or add it to the input dump if enabled. Implies\n" - "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", - cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); - -// The order of DumpInputValue members affects their precedence, as documented -// for -dump-input below. -enum DumpInputValue { - DumpInputDefault, - DumpInputNever, - DumpInputFail, - DumpInputAlways, - DumpInputHelp -}; - -static cl::list DumpInputs( - "dump-input", - cl::desc("Dump input to stderr, adding annotations representing\n" - "currently enabled diagnostics. When there are multiple\n" - "occurrences of this option, the that appears earliest\n" - "in the list below has precedence.\n"), - cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), - clEnumValN(DumpInputAlways, "always", "Always dump input"), - clEnumValN(DumpInputFail, "fail", "Dump input on failure"), - clEnumValN(DumpInputNever, "never", "Never dump input"))); - -typedef cl::list::const_iterator prefix_iterator; - - - - - - - -static void DumpCommandLine(int argc, char **argv) { - errs() << "FileCheck command line: "; - for (int I = 0; I < argc; I++) - errs() << " " << argv[I]; - errs() << "\n"; -} - -struct MarkerStyle { - /// The starting char (before tildes) for marking the line. - char Lead; - /// What color to use for this annotation. - raw_ostream::Colors Color; - /// A note to follow the marker, or empty string if none. - std::string Note; - MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(Note) {} -}; - -static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { - switch (MatchTy) { - case FileCheckDiag::MatchFoundAndExpected: - return MarkerStyle('^', raw_ostream::GREEN); - case FileCheckDiag::MatchFoundButExcluded: - return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); - case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); - case FileCheckDiag::MatchFoundButDiscarded: - return MarkerStyle('!', raw_ostream::CYAN, - "discard: overlaps earlier match"); - case FileCheckDiag::MatchNoneAndExcluded: - return MarkerStyle('X', raw_ostream::GREEN); - case FileCheckDiag::MatchNoneButExpected: - return MarkerStyle('X', raw_ostream::RED, "error: no match found"); - case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); - } - llvm_unreachable_internal("unexpected match type"); -} - -static void DumpInputAnnotationHelp(raw_ostream &OS) { - OS << "The following description was requested by -dump-input=help to\n" - << "explain the input annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\n\n"; - - // Labels for input lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; - OS << " labels line number L of the input file\n"; - - // Labels for annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; - OS << " labels the only match result for a pattern of type T from " - << "line L of\n" - << " the check file\n"; - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\n"; - - // Markers on annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; - OS << " marks good match (reported if -v)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; - OS << " marks bad match, such as:\n" - << " - CHECK-NEXT on same line as previous match (error)\n" - << " - CHECK-NOT found (error)\n" - << " - CHECK-DAG overlapping match (discarded, reported if " - << "-vv)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; - OS << " marks search range when no match is found, such as:\n" - << " - CHECK-NEXT not found (error)\n" - << " - CHECK-NOT not found (success, reported if -vv)\n" - << " - CHECK-DAG not found after discarded matches (error)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; - OS << " marks fuzzy match when no match is found\n"; - - // Colors. - OS << " - colors "; - WithColor(OS, raw_ostream::GREEN, true) << "success"; - OS << ", "; - WithColor(OS, raw_ostream::RED, true) << "error"; - OS << ", "; - WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; - OS << "\n\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; -} - -/// An annotation for a single input line. -struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; - /// The label for this annotation. - std::string Label; - /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. - unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to to mark the - /// input line. If InputEndCol is UINT_MAX, treat it as the last column - /// before the newline. - unsigned InputStartCol, InputEndCol; - /// The marker to use. - MarkerStyle Marker; - /// Whether this annotation represents a good match for an expected pattern. - bool FoundAndExpectedMatch; -}; - -/// Get an abbreviation for the check type. -std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { - switch (Ty) { - case Check::CheckPlain: - if (Ty.getCount() > 1) - return "count"; - return "check"; - case Check::CheckNext: - return "next"; - case Check::CheckSame: - return "same"; - case Check::CheckNot: - return "not"; - case Check::CheckDAG: - return "dag"; - case Check::CheckLabel: - return "label"; - case Check::CheckEmpty: - return "empty"; - case Check::CheckEOF: - return "eof"; - case Check::CheckBadNot: - return "bad-not"; - case Check::CheckBadCount: - return "bad-count"; - case Check::CheckNone: - llvm_unreachable("invalid FileCheckType"); - } - llvm_unreachable("unknown FileCheckType"); -} - -static void BuildInputAnnotations(const std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // How many diagnostics has the current check seen so far? - unsigned CheckDiagCount = 0; - // What's the widest label? - LabelWidth = 0; - for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; - ++DiagItr) { - InputAnnotation A; - - // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; - llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; - else - A.CheckDiagIndex = 0; - Label.flush(); - LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); - - A.Marker = GetMarker(DiagItr->MatchTy); - A.FoundAndExpectedMatch = - DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; - - // Compute the mark location, and break annotation into multiple - // annotations if it spans multiple lines. - A.InputLine = DiagItr->InputStartLine; - A.InputStartCol = DiagItr->InputStartCol; - if (DiagItr->InputStartLine == DiagItr->InputEndLine) { - // Sometimes ranges are empty in order to indicate a specific point, but - // that would mean nothing would be marked, so adjust the range to - // include the following character. - A.InputEndCol = - std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); - Annotations.push_back(A); - } else { - assert(DiagItr->InputStartLine < DiagItr->InputEndLine && - "expected input range not to be inverted"); - A.InputEndCol = UINT_MAX; - Annotations.push_back(A); - for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; - L <= E; ++L) { - // If a range ends before the first column on a line, then it has no - // characters on that line, so there's nothing to render. - if (DiagItr->InputEndCol == 1 && L == E) - break; - InputAnnotation B; - B.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; - B.Label = A.Label; - B.InputLine = L; - B.Marker = A.Marker; - B.Marker.Lead = '~'; - B.Marker.Note = ""; - B.InputStartCol = 1; - if (L != E) - B.InputEndCol = UINT_MAX; - else - B.InputEndCol = DiagItr->InputEndCol; - B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; - Annotations.push_back(B); - } - } - } -} - -static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, - StringRef InputFileText, - std::vector &Annotations, - unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; - - // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); - - // Compute the width of the label column. - const unsigned char *InputFilePtr = InputFileText.bytes_begin(), - *InputFileEnd = InputFileText.bytes_end(); - unsigned LineCount = InputFileText.count('\n'); - if (InputFileEnd[-1] != '\n') - ++LineCount; - unsigned LineNoWidth = std::log10(LineCount) + 1; - // +3 below adds spaces (1) to the left of the (right-aligned) line numbers - // on input lines and (2) to the right of the (left-aligned) labels on - // annotation lines so that input lines and annotation lines are more - // visually distinct. For example, the spaces on the annotation lines ensure - // that input line numbers and check directive line numbers never align - // horizontally. Those line numbers might not even be for the same file. - // One space would be enough to achieve that, but more makes it even easier - // to see. - LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; - - // Print annotated input lines. - auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); - for (unsigned Line = 1; - InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; - ++Line) { - const unsigned char *InputFileLine = InputFilePtr; - - // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) - << format_decimal(Line, LabelWidth) << ": "; - - // For the case where -v and colors are enabled, find the annotations for - // good matches for expected patterns in order to highlight everything - // else in the line. There are no such annotations if -v is disabled. - std::vector FoundAndExpectedMatches; - if (Req.Verbose && WithColor(OS).colorsEnabled()) { - for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; - ++I) { - if (I->FoundAndExpectedMatch) - FoundAndExpectedMatches.push_back(*I); - } - } - - // Print numbered line with highlighting where there are no matches for - // expected patterns. - bool Newline = false; - { - WithColor COS(OS); - bool InMatch = false; - if (Req.Verbose) - COS.changeColor(raw_ostream::CYAN, true, true); - for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { - bool WasInMatch = InMatch; - InMatch = false; - for (auto M : FoundAndExpectedMatches) { - if (M.InputStartCol <= Col && Col < M.InputEndCol) { - InMatch = true; - break; - } - } - if (!WasInMatch && InMatch) - COS.resetColor(); - else if (WasInMatch && !InMatch) - COS.changeColor(raw_ostream::CYAN, true, true); - if (*InputFilePtr == '\n') - Newline = true; - else - COS << *InputFilePtr; - ++InputFilePtr; - } - } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; - - // Print any annotations. - while (AnnotationItr != AnnotationEnd && - AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); - // The two spaces below are where the ": " appears on input lines. - COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; - unsigned Col; - for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) - COS << ' '; - COS << AnnotationItr->Marker.Lead; - // If InputEndCol=UINT_MAX, stop at InputLineWidth. - for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; - ++Col) - COS << '~'; - const std::string &Note = AnnotationItr->Marker.Note; - if (!Note.empty()) { - // Put the note at the end of the input line. If we were to instead - // put the note right after the marker, subsequent annotations for the - // same input line might appear to mark this note instead of the input - // line. - for (; Col <= InputLineWidth; ++Col) - COS << ' '; - COS << ' ' << Note; - } - COS << '\n'; - ++AnnotationItr; - } - } - - OS << ">>>>>>\n"; -} - -int main(int argc, char **argv) { - // Enable use of ANSI color codes because FileCheck is using them to - // highlight text. - llvm::sys::Process::UseANSIEscapeCodes(true); - - InitLLVM X(argc, argv); - cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, - "FILECHECK_OPTS"); - DumpInputValue DumpInput = - DumpInputs.empty() - ? DumpInputDefault - : *std::max_element(DumpInputs.begin(), DumpInputs.end()); - if (DumpInput == DumpInputHelp) { - DumpInputAnnotationHelp(outs()); - return 0; - } - if (CheckFilename.empty()) { - errs() << " not specified\n"; - return 2; - } - - FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); - - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); - - bool GlobalDefineError = false; - for (auto G : GlobalDefines) { - size_t EqIdx = G.find('='); - if (EqIdx == std::string::npos) { - errs() << "Missing equal sign in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - if (EqIdx == 0) { - errs() << "Missing variable name in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - Req.GlobalDefines.push_back(G); - } - if (GlobalDefineError) - return 2; - - Req.AllowEmptyInput = AllowEmptyInput; - Req.EnableVarScope = EnableVarScope; - Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; - Req.Verbose = Verbose; - Req.VerboseVerbose = VerboseVerbose; - Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; - Req.MatchFullLines = MatchFullLines; - Req.IgnoreCase = IgnoreCase; - - if (VerboseVerbose) - Req.Verbose = true; - - FileCheck FC(Req); - if (!FC.ValidateCheckPrefixes()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; - return 2; - } - - Regex PrefixRE = FC.buildCheckPrefixRegex(); - std::string REError; - if (!PrefixRE.isValid(REError)) { - errs() << "Unable to combine check-prefix strings into a prefix regular " - "expression! This is likely a bug in FileCheck's verification of " - "the check-prefix strings. Regular expression parsing failed " - "with the following error: " - << REError << "\n"; - return 2; - } - - SourceMgr SM; - - // Read the expected strings from the check file. - ErrorOr> CheckFileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); - if (std::error_code EC = CheckFileOrErr.getError()) { - errs() << "Could not open check file '" << CheckFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &CheckFile = *CheckFileOrErr.get(); - - SmallString<4096> CheckFileBuffer; - StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); - - if (FC.readCheckFile(SM, CheckFileText, PrefixRE)) - return 2; - - // Open the file to check and add it to SourceMgr. - ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - if (std::error_code EC = InputFileOrErr.getError()) { - errs() << "Could not open input file '" << InputFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &InputFile = *InputFileOrErr.get(); - - if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { - errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; - DumpCommandLine(argc, argv); - return 2; - } - - SmallString<4096> InputFileBuffer; - StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - InputFileText, InputFile.getBufferIdentifier()), - SMLoc()); - - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - - std::vector Diags; - int ExitCode = FC.checkInput(SM, InputFileText, - DumpInput == DumpInputNever ? nullptr : &Diags) - ? EXIT_SUCCESS - : 1; - if (DumpInput == DumpInputAlways || - (ExitCode == 1 && DumpInput == DumpInputFail)) { - errs() << "\n" - << "Input file: " - << (InputFilename == "-" ? "" : InputFilename.getValue()) - << "\n" - << "Check file: " << CheckFilename << "\n" - << "\n" - << "-dump-input=help describes the format of the following dump.\n" - << "\n"; - std::vector Annotations; - unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); - } - - return ExitCode; -} diff --git a/utils/FileCheck-9.cpp b/utils/FileCheck-9.cpp deleted file mode 100644 index c882736d4bf..00000000000 --- a/utils/FileCheck-9.cpp +++ /dev/null @@ -1,651 +0,0 @@ -//===- FileCheck.cpp - Check that File's Contents match what is expected --===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// FileCheck does a line-by line check of a file that validates whether it -// contains the expected content. This is useful for regression tests etc. -// -// This program exits with an exit status of 2 on error, exit status of 0 if -// the file matched the expected contents, and exit status of 1 if it did not -// contain the expected contents. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/FileCheck.h" -#include -using namespace llvm; - -static cl::opt - CheckFilename(cl::Positional, cl::desc(""), cl::Optional); - -static cl::opt - InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), - cl::init("-"), cl::value_desc("filename")); - -static cl::list CheckPrefixes( - "check-prefix", - cl::desc("Prefix to use from check file (defaults to 'CHECK')")); -static cl::alias CheckPrefixesAlias( - "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, - cl::NotHidden, - cl::desc( - "Alias for -check-prefix permitting multiple comma separated values")); - -static cl::opt NoCanonicalizeWhiteSpace( - "strict-whitespace", - cl::desc("Do not treat all horizontal whitespace as equivalent")); - -static cl::list ImplicitCheckNot( - "implicit-check-not", - cl::desc("Add an implicit negative check with this pattern to every\n" - "positive check. This can be used to ensure that no instances of\n" - "this pattern occur which are not matched by a positive pattern"), - cl::value_desc("pattern")); - -static cl::list - GlobalDefines("D", cl::AlwaysPrefix, - cl::desc("Define a variable to be used in capture patterns."), - cl::value_desc("VAR=VALUE")); - -static cl::opt AllowEmptyInput( - "allow-empty", cl::init(false), - cl::desc("Allow the input file to be empty. This is useful when making\n" - "checks that some error message does not occur, for example.")); - -static cl::opt MatchFullLines( - "match-full-lines", cl::init(false), - cl::desc("Require all positive matches to cover an entire input line.\n" - "Allows leading and trailing whitespace if --strict-whitespace\n" - "is not also passed.")); - -static cl::opt EnableVarScope( - "enable-var-scope", cl::init(false), - cl::desc("Enables scope for regex variables. Variables with names that\n" - "do not start with '$' will be reset at the beginning of\n" - "each CHECK-LABEL block.")); - -static cl::opt AllowDeprecatedDagOverlap( - "allow-deprecated-dag-overlap", cl::init(false), - cl::desc("Enable overlapping among matches in a group of consecutive\n" - "CHECK-DAG directives. This option is deprecated and is only\n" - "provided for convenience as old tests are migrated to the new\n" - "non-overlapping CHECK-DAG implementation.\n")); - -static cl::opt Verbose( - "v", cl::init(false), - cl::desc("Print directive pattern matches, or add them to the input dump\n" - "if enabled.\n")); - -static cl::opt VerboseVerbose( - "vv", cl::init(false), - cl::desc("Print information helpful in diagnosing internal FileCheck\n" - "issues, or add it to the input dump if enabled. Implies\n" - "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", cl::init(std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); - -enum DumpInputValue { - DumpInputDefault, - DumpInputHelp, - DumpInputNever, - DumpInputFail, - DumpInputAlways -}; - -static cl::opt DumpInput( - "dump-input", cl::init(DumpInputDefault), - cl::desc("Dump input to stderr, adding annotations representing\n" - " currently enabled diagnostics\n"), - cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), - clEnumValN(DumpInputNever, "never", "Never dump input"), - clEnumValN(DumpInputFail, "fail", "Dump input on failure"), - clEnumValN(DumpInputAlways, "always", "Always dump input"))); - -typedef cl::list::const_iterator prefix_iterator; - - - - - - - -static void DumpCommandLine(int argc, char **argv) { - errs() << "FileCheck command line: "; - for (int I = 0; I < argc; I++) - errs() << " " << argv[I]; - errs() << "\n"; -} - -struct MarkerStyle { - /// The starting char (before tildes) for marking the line. - char Lead; - /// What color to use for this annotation. - raw_ostream::Colors Color; - /// A note to follow the marker, or empty string if none. - std::string Note; - MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(Note) {} -}; - -static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { - switch (MatchTy) { - case FileCheckDiag::MatchFoundAndExpected: - return MarkerStyle('^', raw_ostream::GREEN); - case FileCheckDiag::MatchFoundButExcluded: - return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); - case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); - case FileCheckDiag::MatchFoundButDiscarded: - return MarkerStyle('!', raw_ostream::CYAN, - "discard: overlaps earlier match"); - case FileCheckDiag::MatchNoneAndExcluded: - return MarkerStyle('X', raw_ostream::GREEN); - case FileCheckDiag::MatchNoneButExpected: - return MarkerStyle('X', raw_ostream::RED, "error: no match found"); - case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); - } - llvm_unreachable_internal("unexpected match type"); -} - -static void DumpInputAnnotationHelp(raw_ostream &OS) { - OS << "The following description was requested by -dump-input=help to\n" - << "explain the input annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\n\n"; - - // Labels for input lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; - OS << " labels line number L of the input file\n"; - - // Labels for annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; - OS << " labels the only match result for a pattern of type T from " - << "line L of\n" - << " the check file\n"; - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\n"; - - // Markers on annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; - OS << " marks good match (reported if -v)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; - OS << " marks bad match, such as:\n" - << " - CHECK-NEXT on same line as previous match (error)\n" - << " - CHECK-NOT found (error)\n" - << " - CHECK-DAG overlapping match (discarded, reported if " - << "-vv)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; - OS << " marks search range when no match is found, such as:\n" - << " - CHECK-NEXT not found (error)\n" - << " - CHECK-NOT not found (success, reported if -vv)\n" - << " - CHECK-DAG not found after discarded matches (error)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; - OS << " marks fuzzy match when no match is found\n"; - - // Colors. - OS << " - colors "; - WithColor(OS, raw_ostream::GREEN, true) << "success"; - OS << ", "; - WithColor(OS, raw_ostream::RED, true) << "error"; - OS << ", "; - WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; - OS << "\n\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; -} - -/// An annotation for a single input line. -struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; - /// The label for this annotation. - std::string Label; - /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. - unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to to mark the - /// input line. If InputEndCol is UINT_MAX, treat it as the last column - /// before the newline. - unsigned InputStartCol, InputEndCol; - /// The marker to use. - MarkerStyle Marker; - /// Whether this annotation represents a good match for an expected pattern. - bool FoundAndExpectedMatch; -}; - -/// Get an abbreviation for the check type. -std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { - switch (Ty) { - case Check::CheckPlain: - if (Ty.getCount() > 1) - return "count"; - return "check"; - case Check::CheckNext: - return "next"; - case Check::CheckSame: - return "same"; - case Check::CheckNot: - return "not"; - case Check::CheckDAG: - return "dag"; - case Check::CheckLabel: - return "label"; - case Check::CheckEmpty: - return "empty"; - case Check::CheckEOF: - return "eof"; - case Check::CheckBadNot: - return "bad-not"; - case Check::CheckBadCount: - return "bad-count"; - case Check::CheckNone: - llvm_unreachable("invalid FileCheckType"); - } - llvm_unreachable("unknown FileCheckType"); -} - -static void BuildInputAnnotations(const std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // How many diagnostics has the current check seen so far? - unsigned CheckDiagCount = 0; - // What's the widest label? - LabelWidth = 0; - for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; - ++DiagItr) { - InputAnnotation A; - - // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; - llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; - else - A.CheckDiagIndex = 0; - Label.flush(); - LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); - - MarkerStyle Marker = GetMarker(DiagItr->MatchTy); - A.Marker = Marker; - A.FoundAndExpectedMatch = - DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; - - // Compute the mark location, and break annotation into multiple - // annotations if it spans multiple lines. - A.InputLine = DiagItr->InputStartLine; - A.InputStartCol = DiagItr->InputStartCol; - if (DiagItr->InputStartLine == DiagItr->InputEndLine) { - // Sometimes ranges are empty in order to indicate a specific point, but - // that would mean nothing would be marked, so adjust the range to - // include the following character. - A.InputEndCol = - std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); - Annotations.push_back(A); - } else { - assert(DiagItr->InputStartLine < DiagItr->InputEndLine && - "expected input range not to be inverted"); - A.InputEndCol = UINT_MAX; - A.Marker.Note = ""; - Annotations.push_back(A); - for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; - L <= E; ++L) { - // If a range ends before the first column on a line, then it has no - // characters on that line, so there's nothing to render. - if (DiagItr->InputEndCol == 1 && L == E) { - Annotations.back().Marker.Note = Marker.Note; - break; - } - InputAnnotation B; - B.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; - B.Label = A.Label; - B.InputLine = L; - B.Marker = Marker; - B.Marker.Lead = '~'; - B.InputStartCol = 1; - if (L != E) { - B.InputEndCol = UINT_MAX; - B.Marker.Note = ""; - } else - B.InputEndCol = DiagItr->InputEndCol; - B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; - Annotations.push_back(B); - } - } - } -} - -static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, - StringRef InputFileText, - std::vector &Annotations, - unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; - - // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); - - // Compute the width of the label column. - const unsigned char *InputFilePtr = InputFileText.bytes_begin(), - *InputFileEnd = InputFileText.bytes_end(); - unsigned LineCount = InputFileText.count('\n'); - if (InputFileEnd[-1] != '\n') - ++LineCount; - unsigned LineNoWidth = std::log10(LineCount) + 1; - // +3 below adds spaces (1) to the left of the (right-aligned) line numbers - // on input lines and (2) to the right of the (left-aligned) labels on - // annotation lines so that input lines and annotation lines are more - // visually distinct. For example, the spaces on the annotation lines ensure - // that input line numbers and check directive line numbers never align - // horizontally. Those line numbers might not even be for the same file. - // One space would be enough to achieve that, but more makes it even easier - // to see. - LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; - - // Print annotated input lines. - auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); - for (unsigned Line = 1; - InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; - ++Line) { - const unsigned char *InputFileLine = InputFilePtr; - - // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) - << format_decimal(Line, LabelWidth) << ": "; - - // For the case where -v and colors are enabled, find the annotations for - // good matches for expected patterns in order to highlight everything - // else in the line. There are no such annotations if -v is disabled. - std::vector FoundAndExpectedMatches; - if (Req.Verbose && WithColor(OS).colorsEnabled()) { - for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; - ++I) { - if (I->FoundAndExpectedMatch) - FoundAndExpectedMatches.push_back(*I); - } - } - - // Print numbered line with highlighting where there are no matches for - // expected patterns. - bool Newline = false; - { - WithColor COS(OS); - bool InMatch = false; - if (Req.Verbose) - COS.changeColor(raw_ostream::CYAN, true, true); - for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { - bool WasInMatch = InMatch; - InMatch = false; - for (auto M : FoundAndExpectedMatches) { - if (M.InputStartCol <= Col && Col < M.InputEndCol) { - InMatch = true; - break; - } - } - if (!WasInMatch && InMatch) - COS.resetColor(); - else if (WasInMatch && !InMatch) - COS.changeColor(raw_ostream::CYAN, true, true); - if (*InputFilePtr == '\n') - Newline = true; - else - COS << *InputFilePtr; - ++InputFilePtr; - } - } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; - - // Print any annotations. - while (AnnotationItr != AnnotationEnd && - AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); - // The two spaces below are where the ": " appears on input lines. - COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; - unsigned Col; - for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) - COS << ' '; - COS << AnnotationItr->Marker.Lead; - // If InputEndCol=UINT_MAX, stop at InputLineWidth. - for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; - ++Col) - COS << '~'; - const std::string &Note = AnnotationItr->Marker.Note; - if (!Note.empty()) { - // Put the note at the end of the input line. If we were to instead - // put the note right after the marker, subsequent annotations for the - // same input line might appear to mark this note instead of the input - // line. - for (; Col <= InputLineWidth; ++Col) - COS << ' '; - COS << ' ' << Note; - } - COS << '\n'; - ++AnnotationItr; - } - } - - OS << ">>>>>>\n"; -} - -int main(int argc, char **argv) { - // Enable use of ANSI color codes because FileCheck is using them to - // highlight text. - llvm::sys::Process::UseANSIEscapeCodes(true); - - InitLLVM X(argc, argv); - cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, - "FILECHECK_OPTS"); - if (DumpInput == DumpInputHelp) { - DumpInputAnnotationHelp(outs()); - return 0; - } - if (CheckFilename.empty()) { - errs() << " not specified\n"; - return 2; - } - - FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); - - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); - - bool GlobalDefineError = false; - for (auto G : GlobalDefines) { - size_t EqIdx = G.find('='); - if (EqIdx == std::string::npos) { - errs() << "Missing equal sign in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - if (EqIdx == 0) { - errs() << "Missing variable name in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - Req.GlobalDefines.push_back(G); - } - if (GlobalDefineError) - return 2; - - Req.AllowEmptyInput = AllowEmptyInput; - Req.EnableVarScope = EnableVarScope; - Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; - Req.Verbose = Verbose; - Req.VerboseVerbose = VerboseVerbose; - Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; - Req.MatchFullLines = MatchFullLines; - - if (VerboseVerbose) - Req.Verbose = true; - - FileCheck FC(Req); - if (!FC.ValidateCheckPrefixes()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; - return 2; - } - - Regex PrefixRE = FC.buildCheckPrefixRegex(); - std::string REError; - if (!PrefixRE.isValid(REError)) { - errs() << "Unable to combine check-prefix strings into a prefix regular " - "expression! This is likely a bug in FileCheck's verification of " - "the check-prefix strings. Regular expression parsing failed " - "with the following error: " - << REError << "\n"; - return 2; - } - - SourceMgr SM; - - // Read the expected strings from the check file. - ErrorOr> CheckFileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); - if (std::error_code EC = CheckFileOrErr.getError()) { - errs() << "Could not open check file '" << CheckFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &CheckFile = *CheckFileOrErr.get(); - - SmallString<4096> CheckFileBuffer; - StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); - - std::vector CheckStrings; - if (FC.ReadCheckFile(SM, CheckFileText, PrefixRE, CheckStrings)) - return 2; - - // Open the file to check and add it to SourceMgr. - ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - if (std::error_code EC = InputFileOrErr.getError()) { - errs() << "Could not open input file '" << InputFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &InputFile = *InputFileOrErr.get(); - - if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { - errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; - DumpCommandLine(argc, argv); - return 2; - } - - SmallString<4096> InputFileBuffer; - StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - InputFileText, InputFile.getBufferIdentifier()), - SMLoc()); - - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - - std::vector Diags; - int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, - DumpInput == DumpInputNever ? nullptr : &Diags) - ? EXIT_SUCCESS - : 1; - if (DumpInput == DumpInputAlways || - (ExitCode == 1 && DumpInput == DumpInputFail)) { - errs() << "\n" - << "Input file: " - << (InputFilename == "-" ? "" : InputFilename.getValue()) - << "\n" - << "Check file: " << CheckFilename << "\n" - << "\n" - << "-dump-input=help describes the format of the following dump.\n" - << "\n"; - std::vector Annotations; - unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); - } - - return ExitCode; -} From 8e1fc9575d06718edf6c0bc37d511c42b54ba8e4 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 01:15:35 +0200 Subject: [PATCH 264/301] CI: Install distro-LLVM 11 for Ubuntu 20 jobs --- .circleci/config.yml | 4 ++-- .cirrus.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9db6d69899d..5618d024854 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,7 +108,7 @@ jobs: environment: - PARALLELISM: 4 - CI_OS: linux - - EXTRA_APT_PACKAGES: llvm-dev libclang-common-10-dev + - EXTRA_APT_PACKAGES: llvm-11-dev libclang-common-11-dev - HOST_LDC_VERSION: 1.24.0 - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" Ubuntu-20.04-sharedLibsOnly-gdmd: @@ -119,7 +119,7 @@ jobs: environment: - PARALLELISM: 4 - CI_OS: linux - - EXTRA_APT_PACKAGES: gdmd llvm-dev libclang-common-10-dev + - EXTRA_APT_PACKAGES: gdmd llvm-11-dev libclang-common-11-dev - HOST_LDC_VERSION: 1.24.0 - EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" macOS-x64: diff --git a/.cirrus.yml b/.cirrus.yml index f9d528f0758..1110ddd937e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -247,7 +247,7 @@ task: environment: CI_ARCH: x86_64 CI_OS: linux - EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" + EXTRA_APT_PACKAGES: "llvm-11-dev libclang-common-11-dev" EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" PARALLELISM: 8 << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE @@ -284,7 +284,7 @@ task: CI_ARCH: x86_64 CI_OS: linux HOST_LDC_VERSION: 1.9.0 - EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" + EXTRA_APT_PACKAGES: "llvm-11-dev libclang-common-11-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_LTO_LIBS=ON" PARALLELISM: 8 << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE From f6702cfbf5463bef846591642f8982c26f63b25b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 01:52:58 +0200 Subject: [PATCH 265/301] Remove legacy llvm::make_unique --- driver/linker-gcc.cpp | 4 ++-- gen/irstate.cpp | 4 ++-- gen/llvm.h | 7 ------- gen/pgo_ASTbased.cpp | 2 +- runtime/jit-rt/cpp-so/disassembler.cpp | 10 +++------- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 938dc25cb1e..9105cbb4932 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -831,10 +831,10 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, std::unique_ptr argsBuilder; if (global.params.targetTriple->isOSBinFormatWasm()) { tool = getProgram("wasm-ld", &opts::linker); - argsBuilder = llvm::make_unique(); + argsBuilder = std::make_unique(); } else { tool = getGcc(); - argsBuilder = llvm::make_unique(); + argsBuilder = std::make_unique(); } // build arguments diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 5894088dbba..1d424e4dbe4 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -48,14 +48,14 @@ llvm::Function *IRState::topfunc() { return func()->getLLVMFunc(); } llvm::Instruction *IRState::topallocapoint() { return funcGen().allocapoint; } std::unique_ptr IRState::setInsertPoint(llvm::BasicBlock *bb) { - auto savedScope = llvm::make_unique(builder); + auto savedScope = std::make_unique(builder); builder.SetInsertPoint(bb); return savedScope; } std::unique_ptr IRState::saveInsertPoint() { - return llvm::make_unique(builder); + return std::make_unique(builder); } bool IRState::scopereturned() { diff --git a/gen/llvm.h b/gen/llvm.h index 86dd6cdd47c..ef50be53c35 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -31,13 +31,6 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/DebugInfo.h" -// LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back -// and point to std::make_unique. -#include -namespace llvm { -using std::make_unique; -} - using llvm::APFloat; using llvm::APInt; using llvm::IRBuilder; diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index 299d8be0ba3..f1307e236c9 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -955,7 +955,7 @@ void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader, } ProfRecord = - llvm::make_unique(std::move(RecordExpected.get())); + std::make_unique(std::move(RecordExpected.get())); RegionCounts = ProfRecord->Counts; IF_LOG Logger::println("Loaded profile data for function: %s", diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp index 32a817c570d..e4b73e46d5d 100644 --- a/runtime/jit-rt/cpp-so/disassembler.cpp +++ b/runtime/jit-rt/cpp-so/disassembler.cpp @@ -35,10 +35,6 @@ #endif #include "llvm/Target/TargetMachine.h" -namespace llvm { -using std::make_unique; -} - namespace { template std::unique_ptr unique(T *ptr) { return std::unique_ptr(ptr); @@ -263,8 +259,8 @@ void disassemble(const llvm::TargetMachine &tm, } SymTable symTable(ctx); - disasm->setSymbolizer(llvm::make_unique( - ctx, llvm::make_unique(ctx), symTable)); + disasm->setSymbolizer(std::make_unique( + ctx, std::make_unique(ctx), symTable)); auto mcia = unique(target.createMCInstrAnalysis(mii)); if (nullptr == mcia) { @@ -285,7 +281,7 @@ void disassemble(const llvm::TargetMachine &tm, // Streamer takes ownership of mip mab auto asmStreamer = unique(target.createAsmStreamer( - ctx, llvm::make_unique(os), true, true, + ctx, std::make_unique(os), true, true, mip.release(), nullptr, std::move(mab), false)); if (nullptr == asmStreamer) { return; From 5d3ded9eeba3f03a9614517ce8b0ff5557651dbf Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 01:58:14 +0200 Subject: [PATCH 266/301] Remove legacy LL[Maybe]Align --- gen/abi/x86.cpp | 2 +- gen/coverage.cpp | 9 +++++---- gen/dvalue.cpp | 7 ++++--- gen/irstate.cpp | 2 +- gen/llvm.h | 3 --- gen/llvmhelpers.cpp | 4 ++-- gen/passes/SimplifyDRuntimeCalls.cpp | 2 +- gen/rttibuilder.cpp | 4 ++-- gen/tocall.cpp | 4 ++-- gen/toconstelem.cpp | 2 +- gen/toir.cpp | 2 +- gen/tollvm.cpp | 9 +++++---- ir/iraggr.cpp | 2 +- ir/irvar.cpp | 2 +- runtime/jit-rt/cpp-so/bind.cpp | 7 ++----- 15 files changed, 29 insertions(+), 32 deletions(-) diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index 1f5357e6074..366bfad1423 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -266,7 +266,7 @@ struct X86TargetABI : TargetABI { #else // Keep alignment for LLVM 13+, to prevent invalid `movaps` etc., // but limit to 4 (required according to runnable/ldc_cabi1.d). - auto align4 = LLAlign(4); + auto align4 = llvm::Align(4); if (arg->attrs.getAlignment(). #if LDC_LLVM_VER >= 1500 value_or diff --git a/gen/coverage.cpp b/gen/coverage.cpp index 2d1a0b6745c..89db3f1758b 100644 --- a/gen/coverage.cpp +++ b/gen/coverage.cpp @@ -44,16 +44,17 @@ void emitCoverageLinecountInc(const Loc &loc) { // Do an atomic increment, so this works when multiple threads are executed. gIR->ir->CreateAtomicRMW(llvm::AtomicRMWInst::Add, ptr, DtoConstUint(1), #if LDC_LLVM_VER >= 1300 - LLAlign(4), + llvm::Align(4), #endif llvm::AtomicOrdering::Monotonic); break; case opts::CoverageIncrement::nonatomic: { // Do a non-atomic increment, user is responsible for correct results with // multithreaded execution - llvm::LoadInst *load = gIR->ir->CreateAlignedLoad(i32Type, ptr, LLAlign(4)); + llvm::LoadInst *load = + gIR->ir->CreateAlignedLoad(i32Type, ptr, llvm::Align(4)); llvm::StoreInst *store = gIR->ir->CreateAlignedStore( - gIR->ir->CreateAdd(load, DtoConstUint(1)), ptr, LLAlign(4)); + gIR->ir->CreateAdd(load, DtoConstUint(1)), ptr, llvm::Align(4)); // add !nontemporal attribute, to inform the optimizer that caching is not // needed llvm::MDNode *node = llvm::MDNode::get( @@ -66,7 +67,7 @@ void emitCoverageLinecountInc(const Loc &loc) { // Do a boolean set, avoiding a memory read (blocking) and threading issues // at the cost of not "counting" llvm::StoreInst *store = - gIR->ir->CreateAlignedStore(DtoConstUint(1), ptr, LLAlign(4)); + gIR->ir->CreateAlignedStore(DtoConstUint(1), ptr, llvm::Align(4)); // add !nontemporal attribute, to inform the optimizer that caching is not // needed llvm::MDNode *node = llvm::MDNode::get( diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index 02b4166dd06..8ef2c3911fd 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -180,7 +180,7 @@ DBitFieldLValue::DBitFieldLValue(Type *t, LLValue *ptr, BitFieldDeclaration *bf) DRValue *DBitFieldLValue::getRVal() { const auto sizeInBits = intType->getBitWidth(); const auto ptr = DtoBitCast(val, getPtrToType(intType)); - LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, LLMaybeAlign(1)); + LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1)); if (bf->type->isunsigned()) { if (auto n = bf->bitOffset) @@ -208,7 +208,8 @@ void DBitFieldLValue::store(LLValue *value) { const auto mask = llvm::APInt::getLowBitsSet(intType->getBitWidth(), bf->fieldWidth); - const auto oldVal = gIR->ir->CreateAlignedLoad(intType, ptr, LLMaybeAlign(1)); + const auto oldVal = + gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1)); const auto maskedOldVal = gIR->ir->CreateAnd(oldVal, ~(mask << bf->bitOffset)); @@ -218,7 +219,7 @@ void DBitFieldLValue::store(LLValue *value) { bfVal = gIR->ir->CreateShl(bfVal, n); const auto newVal = gIR->ir->CreateOr(maskedOldVal, bfVal); - gIR->ir->CreateAlignedStore(newVal, ptr, LLMaybeAlign(1)); + gIR->ir->CreateAlignedStore(newVal, ptr, llvm::MaybeAlign(1)); } DDcomputeLValue::DDcomputeLValue(Type *t, llvm::Type * llt, LLValue *v) : DLValue(t, v) { diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 1d424e4dbe4..5cbd2d685ae 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -147,7 +147,7 @@ IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar, module, initializer->getType(), globalVar->isConstant(), globalVar->getLinkage(), nullptr, "", nullptr, globalVar->getThreadLocalMode()); - globalHelperVar->setAlignment(LLMaybeAlign(globalVar->getAlignment())); + globalHelperVar->setAlignment(llvm::MaybeAlign(globalVar->getAlignment())); globalHelperVar->setComdat(globalVar->getComdat()); globalHelperVar->setDLLStorageClass(globalVar->getDLLStorageClass()); globalHelperVar->setSection(globalVar->getSection()); diff --git a/gen/llvm.h b/gen/llvm.h index ef50be53c35..a746a2bea1e 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -35,9 +35,6 @@ using llvm::APFloat; using llvm::APInt; using llvm::IRBuilder; -#define LLAlign llvm::Align -#define LLMaybeAlign llvm::MaybeAlign - #define GET_INTRINSIC_DECL(_X) \ (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X)) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index ae9bed70cdc..6e517591702 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -191,7 +191,7 @@ llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize, lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), DtoConstUint(arraysize), name, gIR->topallocapoint()); if (auto alignment = DtoAlignment(type)) { - ai->setAlignment(LLAlign(alignment)); + ai->setAlignment(llvm::Align(alignment)); } return ai; } @@ -202,7 +202,7 @@ llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment, lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), name, gIR->topallocapoint()); if (alignment) { - ai->setAlignment(LLAlign(alignment)); + ai->setAlignment(llvm::Align(alignment)); } return ai; } diff --git a/gen/passes/SimplifyDRuntimeCalls.cpp b/gen/passes/SimplifyDRuntimeCalls.cpp index 181b925ffbc..998df4ca2f3 100644 --- a/gen/passes/SimplifyDRuntimeCalls.cpp +++ b/gen/passes/SimplifyDRuntimeCalls.cpp @@ -60,7 +60,7 @@ Value *LibCallOptimization::CastToCStr(Value *V, IRBuilder<> &B) { /// expects that the size has type 'intptr_t' and Dst/Src are pointers. Value *LibCallOptimization::EmitMemCpy(Value *Dst, Value *Src, Value *Len, unsigned Align, IRBuilder<> &B) { - auto A = LLMaybeAlign(Align); + auto A = llvm::MaybeAlign(Align); return B.CreateMemCpy(CastToCStr(Dst, B), A, CastToCStr(Src, B), A, Len, false); } diff --git a/gen/rttibuilder.cpp b/gen/rttibuilder.cpp index fcdde1bb72b..4a3a1adb327 100644 --- a/gen/rttibuilder.cpp +++ b/gen/rttibuilder.cpp @@ -86,7 +86,7 @@ void RTTIBuilder::push_void_array(llvm::Constant *CI, Type *valtype, auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI, initname.peekChars()); setLinkage(lwc, G); - G->setAlignment(LLMaybeAlign(DtoAlignment(valtype))); + G->setAlignment(llvm::MaybeAlign(DtoAlignment(valtype))); push_void_array(getTypeAllocSize(CI->getType()), G); } @@ -112,7 +112,7 @@ void RTTIBuilder::push_array(llvm::Constant *CI, uint64_t dim, Type *valtype, auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI, initname.peekChars()); setLinkage(lwc, G); - G->setAlignment(LLMaybeAlign(DtoAlignment(valtype))); + G->setAlignment(llvm::MaybeAlign(DtoAlignment(valtype))); push_array(dim, DtoBitCast(G, DtoType(valtype->pointerTo()))); } diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 16da4ba086d..ca26de01f64 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -415,7 +415,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, llvm::StoreInst *ret = p->ir->CreateStore(val, ptr); ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); if (auto alignment = getTypeAllocSize(val->getType())) { - ret->setAlignment(LLAlign(alignment)); + ret->setAlignment(llvm::Align(alignment)); } return true; } @@ -449,7 +449,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, llvm::LoadInst *load = p->ir->CreateLoad(loadedType, ptr); if (auto alignment = getTypeAllocSize(loadedType)) { - load->setAlignment(LLAlign(alignment)); + load->setAlignment(llvm::Align(alignment)); } load->setAtomic(llvm::AtomicOrdering(atomicOrdering)); llvm::Value *val = load; diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 4a4e8422772..a392b5fcc4e 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -443,7 +443,7 @@ class ToConstElemVisitor : public Visitor { auto globalVar = new llvm::GlobalVariable( p->module, DtoType(se->type), false, llvm::GlobalValue::InternalLinkage, nullptr, ".structliteral"); - globalVar->setAlignment(LLMaybeAlign(DtoAlignment(se->type))); + globalVar->setAlignment(llvm::MaybeAlign(DtoAlignment(se->type))); p->setStructLiteralConstant(se, globalVar); llvm::Constant *constValue = toConstElem(se, p); diff --git a/gen/toir.cpp b/gen/toir.cpp index 5433060b144..6ea93a69823 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -164,7 +164,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, IF_LOG Logger::cout() << "merged IR value: " << *val << '\n'; gIR->ir->CreateAlignedStore(val, DtoBitCast(ptr, getPtrToType(intType)), - LLMaybeAlign(1)); + llvm::MaybeAlign(1)); offset += group.sizeInBytes; i += group.bitFields.size() - 1; // skip the other bit fields of the group diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 06eab6d90d0..6da53b4fa63 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -402,7 +402,8 @@ void DtoMemSet(LLValue *dst, LLValue *val, LLValue *nbytes, unsigned align) { dst = DtoBitCast(dst, VoidPtrTy); - gIR->ir->CreateMemSet(dst, val, nbytes, LLMaybeAlign(align), false /*isVolatile*/); + gIR->ir->CreateMemSet(dst, val, nbytes, llvm::MaybeAlign(align), + false /*isVolatile*/); } //////////////////////////////////////////////////////////////////////////////// @@ -424,7 +425,7 @@ void DtoMemCpy(LLValue *dst, LLValue *src, LLValue *nbytes, unsigned align) { dst = DtoBitCast(dst, VoidPtrTy); src = DtoBitCast(src, VoidPtrTy); - auto A = LLMaybeAlign(align); + auto A = llvm::MaybeAlign(align); gIR->ir->CreateMemCpy(dst, A, src, A, nbytes, false /*isVolatile*/); } @@ -532,7 +533,7 @@ LLValue *DtoLoad(DLValue *src, const char *name) { LLValue *DtoAlignedLoad(LLType *type, LLValue *src, const char *name) { llvm::LoadInst *ld = DtoLoadImpl(type, src, name); if (auto alignment = getABITypeAlign(ld->getType())) { - ld->setAlignment(LLAlign(alignment)); + ld->setAlignment(llvm::Align(alignment)); } return ld; } @@ -570,7 +571,7 @@ void DtoAlignedStore(LLValue *src, LLValue *dst) { "Should store bools as i8 instead of i1."); llvm::StoreInst *st = gIR->ir->CreateStore(src, dst); if (auto alignment = getABITypeAlign(src->getType())) { - st->setAlignment(LLAlign(alignment)); + st->setAlignment(llvm::Align(alignment)); } } diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp index e7f428846d0..ab6c572ea7e 100644 --- a/ir/iraggr.cpp +++ b/ir/iraggr.cpp @@ -87,7 +87,7 @@ LLConstant *IrAggr::getInitSymbol(bool define) { irMangle, isConstant, false, useDLLImport()); } - initGlobal->setAlignment(LLMaybeAlign(DtoAlignment(type))); + initGlobal->setAlignment(llvm::MaybeAlign(DtoAlignment(type))); init = initGlobal; diff --git a/ir/irvar.cpp b/ir/irvar.cpp index 16343658d64..5f4084f4b59 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -114,7 +114,7 @@ void IrGlobal::declare() { // Set the alignment (it is important not to use type->alignsize because // VarDeclarations can have an align() attribute independent of the type // as well). - gvar->setAlignment(LLMaybeAlign(DtoAlignment(V))); + gvar->setAlignment(llvm::MaybeAlign(DtoAlignment(V))); applyVarDeclUDAs(V, gvar); diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index 7f0347d9ab8..e29d2275608 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -18,9 +18,6 @@ #include "valueparser.h" -#define LLAlign llvm::Align -#define LLMaybeAlign llvm::MaybeAlign - namespace { enum { SmallParamsCount = 5 }; @@ -75,7 +72,7 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, auto elemType = srcType.getPointerElementType(); auto stackArg = builder.CreateAlloca(elemType); if (auto alignment = layout.getABITypeAlignment(elemType)) - stackArg->setAlignment(LLAlign(alignment)); + stackArg->setAlignment(llvm::Align(alignment)); auto init = parseInitializer(layout, *elemType, param.data, errHandler, override); builder.CreateStore(init, stackArg); @@ -83,7 +80,7 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, } auto stackArg = builder.CreateAlloca(&srcType); if (auto alignment = layout.getABITypeAlignment(&srcType)) - stackArg->setAlignment(LLAlign(alignment)); + stackArg->setAlignment(llvm::Align(alignment)); auto init = parseInitializer(layout, srcType, param.data, errHandler, override); builder.CreateStore(init, stackArg); From 69d636a15af1d340c85254d24051c6a4210bf96d Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 02:08:37 +0200 Subject: [PATCH 267/301] Remove legacy LLCallBasePtr --- gen/funcgenstate.cpp | 8 ++++---- gen/funcgenstate.h | 8 ++++---- gen/llvm.h | 2 -- gen/passes/GarbageCollect2Stack.cpp | 16 ++++++++-------- gen/passes/GarbageCollect2Stack.h | 16 ++++++++-------- gen/tocall.cpp | 4 ++-- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index 52cf698e40b..879e7cf7a02 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -103,10 +103,10 @@ FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) : irFunc(irFunc), scopes(irs), localVariableLifetimeAnnotator(irs), jumpTargets(scopes), switchTargets(), irs(irs) {} -LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, - llvm::FunctionType *calleeType, - llvm::ArrayRef args, - const char *name, bool isNothrow) { +llvm::CallBase *FuncGenState::callOrInvoke(llvm::Value *callee, + llvm::FunctionType *calleeType, + llvm::ArrayRef args, + const char *name, bool isNothrow) { // If this is a direct call, we might be able to use the callee attributes // to our advantage. llvm::Function *calleeFn = llvm::dyn_cast(callee); diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h index 0c5759d3a81..634286c5668 100644 --- a/gen/funcgenstate.h +++ b/gen/funcgenstate.h @@ -202,10 +202,10 @@ class FuncGenState { /// Emits a call or invoke to the given callee, depending on whether there /// are catches/cleanups active or not. - LLCallBasePtr callOrInvoke(llvm::Value *callee, - llvm::FunctionType *calleeType, - llvm::ArrayRef args, - const char *name = "", bool isNothrow = false); + llvm::CallBase *callOrInvoke(llvm::Value *callee, + llvm::FunctionType *calleeType, + llvm::ArrayRef args, + const char *name = "", bool isNothrow = false); private: IRState &irs; diff --git a/gen/llvm.h b/gen/llvm.h index a746a2bea1e..6cd420340bc 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -60,5 +60,3 @@ using llvm::IRBuilder; #define LLConstantFP llvm::ConstantFP #define LLSmallVector llvm::SmallVector - -using LLCallBasePtr = llvm::CallBase *; diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index a6946dbfc73..af710b18f3e 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -91,7 +91,7 @@ static void EmitMemZero(IRBuilder<> &B, Value *Dst, Value *Len, //namespace { -Value* FunctionInfo::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* FunctionInfo::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { NumGcToStack++; auto &BB = CB->getCaller()->getEntryBlock(); @@ -128,7 +128,7 @@ static bool isKnownLessThan(Value *Val, uint64_t Limit, const G2StackAnalysis &A return true; } -bool TypeInfoFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool TypeInfoFI::analyze(CallBase *CB, const G2StackAnalysis &A) { Value *TypeInfo = CB->getArgOperand(TypeInfoArgNr); Ty = A.getTypeFor(TypeInfo, 0); if (!Ty) { @@ -137,7 +137,7 @@ bool TypeInfoFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { return A.DL.getTypeAllocSize(Ty) < SizeLimit; } -bool ArrayFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool ArrayFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (!TypeInfoFI::analyze(CB, A)) { return false; } @@ -160,7 +160,7 @@ bool ArrayFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { return true; } -Value* ArrayFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* ArrayFI::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { // If the allocation is of constant size it's best to put it in the // entry block, so do so if we're not already there. // For dynamically-sized allocations it's best to avoid the overhead @@ -203,7 +203,7 @@ Value* ArrayFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis return alloca; } -bool AllocClassFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool AllocClassFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (CB->arg_size() != 1) { return false; } @@ -238,7 +238,7 @@ bool AllocClassFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { ->getType(); return A.DL.getTypeAllocSize(Ty) < SizeLimit; } -bool UntypedMemoryFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool UntypedMemoryFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (CB->arg_size() < SizeArgNr + 1) { return false; } @@ -260,7 +260,7 @@ bool UntypedMemoryFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { Ty = llvm::Type::getInt8Ty(CB->getContext()); return true; } -Value* UntypedMemoryFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* UntypedMemoryFI::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { // If the allocation is of constant size it's best to put it in the // entry block, so do so if we're not already there. // For dynamically-sized allocations it's best to avoid the overhead @@ -341,7 +341,7 @@ GarbageCollect2Stack::GarbageCollect2Stack() NewArrayT(ReturnType::Array, 0, 1, true), AllocMemory(0) { } -static void RemoveCall(LLCallBasePtr CB, const G2StackAnalysis &A) { +static void RemoveCall(CallBase *CB, const G2StackAnalysis &A) { // For an invoke instruction, we insert a branch to the normal target BB // immediately before it. Ideally, we would find a way to not invalidate // the dominator tree here. diff --git a/gen/passes/GarbageCollect2Stack.h b/gen/passes/GarbageCollect2Stack.h index 8feef11fc37..0024e23e546 100644 --- a/gen/passes/GarbageCollect2Stack.h +++ b/gen/passes/GarbageCollect2Stack.h @@ -27,11 +27,11 @@ class FunctionInfo { // Analyze the current call, filling in some fields. Returns true if // this is an allocation we can stack-allocate. - virtual bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) = 0; + virtual bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) = 0; // Returns the alloca to replace this call. // It will always be inserted before the call. - virtual llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A); + virtual llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A); explicit FunctionInfo(ReturnType::Type returnType) : ReturnType(returnType) {} virtual ~FunctionInfo() = default; @@ -43,7 +43,7 @@ class TypeInfoFI : public FunctionInfo { TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; }; class ArrayFI : public TypeInfoFI { int ArrSizeArgNr; @@ -56,15 +56,15 @@ class ArrayFI : public TypeInfoFI { : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), Initialized(initialized) {} - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; - llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) override; + llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) override; }; // FunctionInfo for _d_allocclass class AllocClassFI : public FunctionInfo { public: - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; // The default promote() should be fine. @@ -77,9 +77,9 @@ class UntypedMemoryFI : public FunctionInfo { llvm::Value *SizeArg; public: - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; - llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) override; + llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) override; explicit UntypedMemoryFI(unsigned sizeArgNr) : FunctionInfo(ReturnType::Pointer), SizeArgNr(sizeArgNr) {} diff --git a/gen/tocall.cpp b/gen/tocall.cpp index ca26de01f64..47259c3d596 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -916,8 +916,8 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, } // call the function - LLCallBasePtr call = gIR->funcGen().callOrInvoke(callable, callableTy, args, - "", tf->isnothrow()); + llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args, + "", tf->isnothrow()); // PGO: Insert instrumentation or attach profile metadata at indirect call // sites. From 4b8e8cf71a1ed33351f1c9c3b6f9515bfa20f71e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 02:39:24 +0200 Subject: [PATCH 268/301] Clean up legacy lit substitutions --- tests/PGO/allstatementtypes.d | 4 ++-- tests/PGO/branching_switch.d | 4 ++-- tests/PGO/break.d | 4 ++-- tests/PGO/functions.d | 4 ++-- tests/PGO/hash.d | 4 ++-- tests/PGO/lit.local.cfg | 2 -- tests/PGO/unrolledloopstatement_gh3375.d | 2 +- tests/codegen/attr_targetoptions.d | 4 ++-- tests/codegen/exception_stack_trace.d | 2 +- tests/codegen/frame_pointer_x86.d | 4 ++-- tests/lit.site.cfg.in | 4 ---- tests/sanitizers/fuzz_asan.d | 4 ++-- 12 files changed, 18 insertions(+), 24 deletions(-) diff --git a/tests/PGO/allstatementtypes.d b/tests/PGO/allstatementtypes.d index 9cf8cf0823c..fffe2f14a1c 100644 --- a/tests/PGO/allstatementtypes.d +++ b/tests/PGO/allstatementtypes.d @@ -6,14 +6,14 @@ // See boundscheck.d for boundschecking instrumenation tests. // RUN: %ldc -boundscheck=off -c -output-ll -fprofile-instr-generate="hoihoihoi" -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // PROFGEN-DAG: @[[FILENAME:.+]] ={{.*}} constant{{.*}} c"hoihoihoi\00" // RUN: %ldc -boundscheck=off -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/branching_switch.d b/tests/PGO/branching_switch.d index e8ba5745524..3e2f8a0837a 100644 --- a/tests/PGO/branching_switch.d +++ b/tests/PGO/branching_switch.d @@ -3,12 +3,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/break.d b/tests/PGO/break.d index 0164fb82f29..20ef7d6c639 100644 --- a/tests/PGO/break.d +++ b/tests/PGO/break.d @@ -3,12 +3,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/functions.d b/tests/PGO/functions.d index bce08f63c2b..2c5d24114f0 100644 --- a/tests/PGO/functions.d +++ b/tests/PGO/functions.d @@ -6,12 +6,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll // PROFGEN-DAG: @[[SMPL:__(llvm_profile_counters|profc).*simplefunction[A-Za-z0-9]*]] ={{.*}} [2 x i64] zeroinitializer // PROFGEN-DAG: @[[TMPL:__(llvm_profile_counters|profc).*templatefunc[A-Za-z0-9]*]] ={{.*}} [2 x i64] zeroinitializer diff --git a/tests/PGO/hash.d b/tests/PGO/hash.d index e14f27bbc86..623b7e5088e 100644 --- a/tests/PGO/hash.d +++ b/tests/PGO/hash.d @@ -7,9 +7,9 @@ // RUN: %ldc -d-version=ProfData -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -d-version=ProfData -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFDATA < %t2.ll \ +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFDATA < %t2.ll \ // RUN: && %ldc -wi -c -output-ll -of=%t3.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=NODATA < %t3.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=NODATA < %t3.ll extern(C): diff --git a/tests/PGO/lit.local.cfg b/tests/PGO/lit.local.cfg index d5d1d48297c..9638e26779b 100644 --- a/tests/PGO/lit.local.cfg +++ b/tests/PGO/lit.local.cfg @@ -1,4 +1,2 @@ # Add "PGO_RT" feature, assuming the `profile` compiler-rt library is available config.available_features.add('PGO_RT') - -config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) diff --git a/tests/PGO/unrolledloopstatement_gh3375.d b/tests/PGO/unrolledloopstatement_gh3375.d index 4a3511c6e57..02b10825f39 100644 --- a/tests/PGO/unrolledloopstatement_gh3375.d +++ b/tests/PGO/unrolledloopstatement_gh3375.d @@ -5,7 +5,7 @@ // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s // RUN: %profdata merge %t.profraw -o %t.profdata // RUN: %ldc -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s -// RUN: FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll alias AliasSeq(TList...) = TList; diff --git a/tests/codegen/attr_targetoptions.d b/tests/codegen/attr_targetoptions.d index c5f1ba1a8c3..8e6e21f2e2d 100644 --- a/tests/codegen/attr_targetoptions.d +++ b/tests/codegen/attr_targetoptions.d @@ -4,9 +4,9 @@ // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=WITH_FP < %t.ll // RUN: %ldc -c -output-ll -of=%t.ll %s -O2 // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=NO_FP < %t.ll -// RUN: %ldc -c -output-ll -of=%t.ll %s -O2 %disable_fp_elim +// RUN: %ldc -c -output-ll -of=%t.ll %s -O2 -frame-pointer=all // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=WITH_FP < %t.ll -// RUN: %ldc -c -output-ll -of=%t.ll %s %enable_fp_elim -mattr=test +// RUN: %ldc -c -output-ll -of=%t.ll %s -frame-pointer=none -mattr=test // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=NO_FP --check-prefix=ATTR < %t.ll // COMMON: define{{.*}} @{{.*}}3fooFZv{{.*}} #[[KEYVALUE:[0-9]+]] diff --git a/tests/codegen/exception_stack_trace.d b/tests/codegen/exception_stack_trace.d index 1594ac5a7f9..71002d74e9b 100644 --- a/tests/codegen/exception_stack_trace.d +++ b/tests/codegen/exception_stack_trace.d @@ -1,4 +1,4 @@ -// RUN: %ldc -g %disable_fp_elim -link-defaultlib-debug %s -of=%t%exe +// RUN: %ldc -g -frame-pointer=all -link-defaultlib-debug %s -of=%t%exe // RUN: %t%exe | FileCheck %s void bar() diff --git a/tests/codegen/frame_pointer_x86.d b/tests/codegen/frame_pointer_x86.d index 470b425f413..c0450591f78 100644 --- a/tests/codegen/frame_pointer_x86.d +++ b/tests/codegen/frame_pointer_x86.d @@ -4,9 +4,9 @@ // RUN: FileCheck %s --check-prefixes=COMMON,FP < %t.s // RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -O2 // RUN: FileCheck %s --check-prefixes=COMMON,NO_FP < %t.s -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -O2 %disable_fp_elim +// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -O2 -frame-pointer=all // RUN: FileCheck %s --check-prefixes=COMMON,FP < %t.s -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s %enable_fp_elim +// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -frame-pointer=none // RUN: FileCheck %s --check-prefixes=COMMON,NO_FP < %t.s // COMMON-LABEL: _D17frame_pointer_x8613inlineAsmLeafFZv: diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index 484b7246de2..5d8fdde37fd 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -216,10 +216,6 @@ if (platform.system() != 'Windows') and lit.util.which('gdb', config.environment config.substitutions.append( ('%_gdb_dflags', gdb_dflags) ) -# Add substitutions for functionality across different LLVM versions -config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) -config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) - if 'LD_LIBRARY_PATH' in os.environ: libs = [] for lib_path in [s for s in os.environ['LD_LIBRARY_PATH'].split(':') if s]: diff --git a/tests/sanitizers/fuzz_asan.d b/tests/sanitizers/fuzz_asan.d index 1fc05e8ae65..3a1711b1537 100644 --- a/tests/sanitizers/fuzz_asan.d +++ b/tests/sanitizers/fuzz_asan.d @@ -2,9 +2,9 @@ // REQUIRES: Fuzzer, ASan -// See https://github.com/ldc-developers/ldc/issues/2222 for %disable_fp_elim +// See https://github.com/ldc-developers/ldc/issues/2222 for -frame-pointer=all // See https://github.com/ldc-developers/ldc/pull/4328 for -fsanitize-address-use-after-return=never -// RUN: %ldc -g -fsanitize=address,fuzzer -fsanitize-address-use-after-return=never %disable_fp_elim %s -of=%t%exe +// RUN: %ldc -g -fsanitize=address,fuzzer -fsanitize-address-use-after-return=never -frame-pointer=all %s -of=%t%exe // RUN: not %t%exe 2>&1 | FileCheck %s bool FuzzMe(ubyte* data, size_t dataSize) From 8b3e36fca85e07e356a641d4cfe612c6b13f5d1a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 01:31:16 +0200 Subject: [PATCH 269/301] GH Actions Vanilla LLVM: Switch LLVM 14 job from Linux to macOS --- .github/workflows/supported_llvm_versions.yml | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 6be3c8b38b9..41be7382b92 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -19,12 +19,6 @@ jobs: os: ubuntu-20.04 host_dc: ldc-beta llvm_version: 15.0.6 - - job_name: Ubuntu 20.04, LLVM 14, latest LDC beta - os: ubuntu-20.04 - host_dc: ldc-beta - llvm_version: 14.0.0 - # the compiler-rt libs installation is somehow non-standard - cmake_flags: -DLDC_INSTALL_LLVM_RUNTIME_LIBS_OS=x86_64-unknown-linux-gnu -DLDC_INSTALL_LLVM_RUNTIME_LIBS_ARCH="" - job_name: Ubuntu 20.04, LLVM 12, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta @@ -35,18 +29,20 @@ jobs: host_dc: dmd-beta llvm_version: 11.1.0 cmake_flags: -DBUILD_SHARED_LIBS=OFF -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON - #- job_name: macOS 11, LLVM 10, latest DMD beta - # os: macos-11 - # host_dc: dmd-beta - # llvm_version: 10.0.1 - # cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 - - job_name: macOS 11, LLVM 13, latest LDC beta + - job_name: macOS 11, LLVM 13, latest DMD beta os: macos-11 - host_dc: ldc-beta + host_dc: dmd-beta llvm_version: 13.0.1 - cmake_flags: -DBUILD_SHARED_LIBS=OFF -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 + cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc + - job_name: macOS 11, LLVM 14, latest LDC beta + os: macos-11 + host_dc: ldc-beta + llvm_version: 14.0.6 + cmake_flags: -DBUILD_SHARED_LIBS=OFF -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ name: ${{ matrix.job_name }} runs-on: ${{ matrix.os }} + env: + MACOSX_DEPLOYMENT_TARGET: 11.6 # silence `ld: warning: object file (…) was built for newer macOS version (…) than being linked (…)` steps: - uses: actions/checkout@v3 with: From 5ef25a2ab3be2b6dda81f22e2bf789df0cd510bb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 23:32:36 +0200 Subject: [PATCH 270/301] Cirrus CI: Try switching AArch64 job from container to VM (#4457) --- .cirrus.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index f9d528f0758..84e583c2189 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -65,6 +65,9 @@ common_steps_template: &COMMON_STEPS_TEMPLATE excludes+='|^std.math.exponential(-shared)?$' # FIXME: failure excludes+='|^druntime-test-exceptions-debug$' + # std.path unittests apparently need HOME, which happens not to be set + export HOME=~ + echo "Setting HOME to '$HOME'" elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then # FIXME: sporadic segfaults/bus errors with enabled optimizations excludes+='|^core.thread.fiber(-shared)?$' @@ -321,10 +324,14 @@ task: task: name: Ubuntu 20.04 aarch64 - arm_container: - image: ubuntu:20.04 + compute_engine_instance: + image_project: ubuntu-os-cloud + image: family/ubuntu-2004-lts-arm64 + platform: linux + architecture: arm64 cpu: 4 memory: 8G + disk: 20 timeout_in: 60m environment: CI_ARCH: aarch64 From 822bf04ebec35bda67d88ae9bcfad2f86addeb74 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 29 Jul 2023 23:46:35 +0200 Subject: [PATCH 271/301] Cirrus CI: Switch to custom ninja, trying to schedule ldc2.o compilation early --- .cirrus.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 84e583c2189..47ddcd96a82 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -27,7 +27,7 @@ common_steps_template: &COMMON_STEPS_TEMPLATE -DINCLUDE_INSTALL_DIR=$installDir/import \ -DLDC_LINK_MANUALLY=OFF \ "${extraFlags[@]}" - ninja -j$PARALLELISM all ldc2-unittest all-test-runners + ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version always: run_compiler_unittests_script: | @@ -194,6 +194,13 @@ install_ubuntu_prerequisites_template: &INSTALL_UBUNTU_PREREQUISITES_TEMPLATE zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES python3 --version + # Download & extract custom Ninja on x86_64 + if [[ "$CI_ARCH" == "x86_64" ]]; then + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip + mkdir ninja + unzip ninja-linux.zip -d ninja + rm ninja-linux.zip + fi # Download & extract host LDC curl -fL --retry 3 --max-time 300 -o ldc2.tar.xz https://github.com/ldc-developers/ldc/releases/download/v$HOST_LDC_VERSION/ldc2-$HOST_LDC_VERSION-linux-$CI_ARCH.tar.xz mkdir host-ldc @@ -212,7 +219,7 @@ install_macos_prerequisites_template: &INSTALL_MACOS_PREREQUISITES_TEMPLATE tar -xf cmake.tar.gz --strip 3 -C cmake rm cmake.tar.gz # Download & extract Ninja - curl -fL --retry 3 --max-time 60 -O https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-mac.zip mkdir ninja tar -xf ninja-mac.zip -C ninja rm ninja-mac.zip @@ -253,6 +260,7 @@ task: EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" PARALLELISM: 8 + PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE @@ -271,6 +279,7 @@ task: EXTRA_APT_PACKAGES: "gdmd llvm-dev libclang-common-15-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" PARALLELISM: 8 + PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} # for gdmd: LANG: C.UTF-8 << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE @@ -290,6 +299,7 @@ task: EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_LTO_LIBS=ON" PARALLELISM: 8 + PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE @@ -390,7 +400,7 @@ task: -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_LTO_LIBS=ON - ninja -j$PARALLELISM + ninja -j$PARALLELISM obj/ldc2.o all bin/ldc2 -version << : *COMMON_STEPS_TEMPLATE << : *PACKAGING_STEPS_TEMPLATE From 531ba869a47a7dabcad5d18c00fe7f183a8d16a4 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 00:29:09 +0200 Subject: [PATCH 272/301] [GHA Main] --- .github/actions/1-setup/action.yml | 29 +++++++++++++++++++-- .github/actions/helper-build-ldc/action.yml | 6 ++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index cb140e00041..d2e8f7d46c4 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -102,8 +102,33 @@ runs: sudo ln -sf "$(dirname "$PWD")/clang/bin/ld.lld" /usr/bin/ld ld --version - - name: Install ninja - uses: seanmiddleditch/gha-setup-ninja@v3 + - name: Install ninja # into ../ninja + shell: bash + run: | + set -eux + cd .. + + os=linux + if [[ '${{ runner.os }}' == Windows ]]; then + os=win + elif [[ '${{ runner.os }}' == macOS ]]; then + os=mac + fi + + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-$os.zip + mkdir ninja + unzip ninja-$os.zip -d ninja + rm ninja-$os.zip + + ninja/ninja --version + + if [[ '${{ runner.os }}' == Windows ]]; then + # HACK: copy to directory in PATH and hope it's early enough + cp ninja/ninja.exe /c/vcpkg + ninja --version + else + echo "PATH=$PWD/ninja:$PATH" >> $GITHUB_ENV + fi - name: Install D host compiler uses: dlang-community/setup-dlang@v1 diff --git a/.github/actions/helper-build-ldc/action.yml b/.github/actions/helper-build-ldc/action.yml index ed31a3e9860..3fafd106f0c 100644 --- a/.github/actions/helper-build-ldc/action.yml +++ b/.github/actions/helper-build-ldc/action.yml @@ -15,7 +15,7 @@ inputs: default: '' build_targets: required: false - default: '' # all + default: 'all' arch: required: false # Windows only runs: @@ -39,7 +39,7 @@ runs: ${{ inputs.specify_install_dir == 'true' && '-DINCLUDE_INSTALL_DIR="$installDir/import"' || '' }} \ ${{ inputs.cmake_flags }} - ninja ${{ inputs.build_targets }} + ninja obj/ldc2.o ${{ inputs.build_targets }} # Windows: invoke CMake & ninja in MSVC env - if: runner.os == 'Windows' @@ -62,4 +62,4 @@ runs: ${{ inputs.cmake_flags }} if %errorlevel% neq 0 exit /b %errorlevel% - ninja ${{ inputs.build_targets }} || exit /b + ninja obj/ldc2.obj ${{ inputs.build_targets }} || exit /b From 782edf13b38ecd3ae50545908b35c52d2d772062 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 11:48:34 +0200 Subject: [PATCH 273/301] [Circle CI] --- .circleci/config.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9db6d69899d..28ab752686d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,9 +18,16 @@ commonSteps: &commonSteps fi apt-get -q update apt-get -yq install \ - git-core cmake ninja-build $gcc_pkg \ + git-core cmake $gcc_pkg \ zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES + # Download & extract Ninja + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip + mkdir ninja + unzip ninja-linux.zip -d ninja + rm ninja-linux.zip + # Add Ninja to PATH for future steps + echo "export PATH=$PWD/ninja:$PATH" >> $BASH_ENV else # Download & extract CMake curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.20.3/cmake-3.20.3-macos-universal.tar.gz @@ -28,9 +35,9 @@ commonSteps: &commonSteps tar -xf cmake.tar.gz --strip 3 -C cmake rm cmake.tar.gz # Download & extract Ninja - curl -fL --retry 3 --max-time 60 -O https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-mac.zip mkdir ninja - tar -xf ninja-mac.zip -C ninja + unzip ninja-mac.zip -d ninja rm ninja-mac.zip # Download & extract LDC-flavoured LLVM with enabled assertions curl -fL --retry 3 --max-time 300 -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-osx-x86_64-withAsserts.tar.xz @@ -68,12 +75,7 @@ commonSteps: &commonSteps -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DLDC_LINK_MANUALLY=OFF \ $EXTRA_CMAKE_FLAGS - # Work around out-of-memory errors - retry once with parallelization and one last time serially - targets='all ldc2-unittest all-test-runners' - for i in {1..2}; do - ninja -j$PARALLELISM -k0 $targets && break || true - done - ninja -j1 $targets + ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version - run: name: Run LDC D unittests From 112eb164231f16b4df830c7f3ed2904fde93de9e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 12:28:01 +0200 Subject: [PATCH 274/301] [GHA] --- .github/actions/1-setup/action.yml | 29 ++----------------- .github/workflows/supported_llvm_versions.yml | 4 +-- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index d2e8f7d46c4..4699f77a3e5 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -102,33 +102,8 @@ runs: sudo ln -sf "$(dirname "$PWD")/clang/bin/ld.lld" /usr/bin/ld ld --version - - name: Install ninja # into ../ninja - shell: bash - run: | - set -eux - cd .. - - os=linux - if [[ '${{ runner.os }}' == Windows ]]; then - os=win - elif [[ '${{ runner.os }}' == macOS ]]; then - os=mac - fi - - curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-$os.zip - mkdir ninja - unzip ninja-$os.zip -d ninja - rm ninja-$os.zip - - ninja/ninja --version - - if [[ '${{ runner.os }}' == Windows ]]; then - # HACK: copy to directory in PATH and hope it's early enough - cp ninja/ninja.exe /c/vcpkg - ninja --version - else - echo "PATH=$PWD/ninja:$PATH" >> $GITHUB_ENV - fi + - name: Install ninja + uses: symmetryinvestments/gha-setup-ninja@v1 - name: Install D host compiler uses: dlang-community/setup-dlang@v1 diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 9b9ff4021f0..3d7b697d887 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -58,7 +58,7 @@ jobs: submodules: true fetch-depth: 50 - name: Install ninja - uses: seanmiddleditch/gha-setup-ninja@v3 + uses: symmetryinvestments/gha-setup-ninja@v1 - name: Install D host compiler uses: dlang-community/setup-dlang@v1 with: @@ -122,7 +122,7 @@ jobs: -DLLVM_ROOT_DIR="$PWD/llvm" \ -DLDC_LINK_MANUALLY=OFF \ ${{ matrix.cmake_flags }} - ninja all ldc2-unittest all-test-runners + ninja obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 --version - name: Run LDC D unittests From baddabe4100c21f141dc05e028dc8f9912298ba0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 19:53:32 +0200 Subject: [PATCH 275/301] Cirrus & Circle CI: Use latest CMake v3.27.1 --- .circleci/config.yml | 13 +++++++++---- .cirrus.yml | 16 +++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 28ab752686d..6854d630082 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,19 +18,24 @@ commonSteps: &commonSteps fi apt-get -q update apt-get -yq install \ - git-core cmake $gcc_pkg \ + git-core $gcc_pkg \ zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES + # Download & extract CMake + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-x86_64.tar.gz + mkdir cmake + tar -xf cmake.tar.gz --strip 1 -C cmake + rm cmake.tar.gz # Download & extract Ninja curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip mkdir ninja unzip ninja-linux.zip -d ninja rm ninja-linux.zip - # Add Ninja to PATH for future steps - echo "export PATH=$PWD/ninja:$PATH" >> $BASH_ENV + # Add CMake and Ninja to PATH for future steps + echo "export PATH=$PWD/cmake/bin:$PWD/ninja:$PATH" >> $BASH_ENV else # Download & extract CMake - curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.20.3/cmake-3.20.3-macos-universal.tar.gz + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-macos-universal.tar.gz mkdir cmake tar -xf cmake.tar.gz --strip 3 -C cmake rm cmake.tar.gz diff --git a/.cirrus.yml b/.cirrus.yml index 47ddcd96a82..01993862785 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -190,10 +190,15 @@ install_ubuntu_prerequisites_template: &INSTALL_UBUNTU_PREREQUISITES_TEMPLATE fi apt-get -q update apt-get -yq install \ - git-core cmake ninja-build $gcc_pkg \ + git-core ninja-build $gcc_pkg \ zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES python3 --version + # Download & extract CMake + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-$CI_ARCH.tar.gz + mkdir cmake + tar -xf cmake.tar.gz --strip 1 -C cmake + rm cmake.tar.gz # Download & extract custom Ninja on x86_64 if [[ "$CI_ARCH" == "x86_64" ]]; then curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip @@ -214,7 +219,7 @@ install_macos_prerequisites_template: &INSTALL_MACOS_PREREQUISITES_TEMPLATE cd $CIRRUS_WORKING_DIR/.. sysctl -n hw.logicalcpu # Download & extract CMake - curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.20.3/cmake-3.20.3-macos-universal.tar.gz + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-macos-universal.tar.gz mkdir cmake tar -xf cmake.tar.gz --strip 3 -C cmake rm cmake.tar.gz @@ -260,7 +265,7 @@ task: EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" PARALLELISM: 8 - PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE @@ -279,7 +284,7 @@ task: EXTRA_APT_PACKAGES: "gdmd llvm-dev libclang-common-15-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" PARALLELISM: 8 - PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} # for gdmd: LANG: C.UTF-8 << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE @@ -299,7 +304,7 @@ task: EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_LTO_LIBS=ON" PARALLELISM: 8 - PATH: ${CIRRUS_WORKING_DIR}/../ninja:${PATH} + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE @@ -357,6 +362,7 @@ task: -DLLVM_ROOT_DIR=$CIRRUS_WORKING_DIR/../llvm -DD_COMPILER=$CIRRUS_WORKING_DIR/../bootstrap-ldc/bin/ldmd2 PARALLELISM: 4 + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${PATH} CLANG_VERSION: '15.0.3' # 15.0.6 requires a more recent libstdc++.so.6 than shipped with Ubuntu 20 CC: $CIRRUS_WORKING_DIR/../clang/bin/clang CXX: $CIRRUS_WORKING_DIR/../clang/bin/clang++ From 5f96e1804f66919bba35a7dfb7b4d81602b11a3f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 19:59:33 +0200 Subject: [PATCH 276/301] CI: Use CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY tweak --- .circleci/config.yml | 1 + .cirrus.yml | 4 +++- .github/actions/helper-build-ldc/action.yml | 2 ++ .github/workflows/supported_llvm_versions.yml | 1 + runtime/CMakeLists.txt | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6854d630082..4c657374583 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,6 +79,7 @@ commonSteps: &commonSteps -DCMAKE_BUILD_TYPE=Release \ -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DLDC_LINK_MANUALLY=OFF \ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ $EXTRA_CMAKE_FLAGS ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version diff --git a/.cirrus.yml b/.cirrus.yml index 01993862785..128da121254 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -26,6 +26,7 @@ common_steps_template: &COMMON_STEPS_TEMPLATE -DCMAKE_INSTALL_PREFIX=$installDir \ -DINCLUDE_INSTALL_DIR=$installDir/import \ -DLDC_LINK_MANUALLY=OFF \ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ "${extraFlags[@]}" ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version @@ -405,7 +406,8 @@ task: -DLLVM_ROOT_DIR=$PWD/../llvm \ -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_LTO_LIBS=ON + -DBUILD_LTO_LIBS=ON \ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON ninja -j$PARALLELISM obj/ldc2.o all bin/ldc2 -version << : *COMMON_STEPS_TEMPLATE diff --git a/.github/actions/helper-build-ldc/action.yml b/.github/actions/helper-build-ldc/action.yml index 3fafd106f0c..3d5fff18aed 100644 --- a/.github/actions/helper-build-ldc/action.yml +++ b/.github/actions/helper-build-ldc/action.yml @@ -35,6 +35,7 @@ runs: -DLLVM_ROOT_DIR="$PWD/../${{ inputs.llvm_dir }}" \ -DD_COMPILER='${{ inputs.host_dc }}' \ -DLDC_LINK_MANUALLY=OFF \ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ ${{ inputs.specify_install_dir == 'true' && '-DCMAKE_INSTALL_PREFIX="$installDir"' || '' }} \ ${{ inputs.specify_install_dir == 'true' && '-DINCLUDE_INSTALL_DIR="$installDir/import"' || '' }} \ ${{ inputs.cmake_flags }} @@ -57,6 +58,7 @@ runs: "-DD_COMPILER=${{ inputs.host_dc }}" ^ -DCMAKE_C_COMPILER=clang-cl ^ -DCMAKE_CXX_COMPILER=clang-cl ^ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON ^ ${{ inputs.specify_install_dir == 'true' && '"-DCMAKE_INSTALL_PREFIX=%installDir%"' || '' }} ^ ${{ inputs.specify_install_dir == 'true' && '"-DINCLUDE_INSTALL_DIR=%installDir%\import"' || '' }} ^ ${{ inputs.cmake_flags }} diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 3d7b697d887..e8d96eb0982 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -121,6 +121,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ROOT_DIR="$PWD/llvm" \ -DLDC_LINK_MANUALLY=OFF \ + -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ ${{ matrix.cmake_flags }} ninja obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 --version diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index c6334de4be8..abcc804aae2 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -335,6 +335,7 @@ if(TARGET gen_gccbuiltins) add_custom_command( OUTPUT ${module} COMMAND gen_gccbuiltins ${module} "${name}" + DEPENDS gen_gccbuiltins ) endfunction() From 1860eae8f97e0207c28d952261f60b8094c338c6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 30 Jul 2023 21:09:54 +0200 Subject: [PATCH 277/301] CMake: Enforce CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON with v3.27+ --- .circleci/config.yml | 1 - .cirrus.yml | 4 +--- .github/actions/helper-build-ldc/action.yml | 2 -- .github/workflows/supported_llvm_versions.yml | 1 - CMakeLists.txt | 8 +++++++- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4c657374583..6854d630082 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,7 +79,6 @@ commonSteps: &commonSteps -DCMAKE_BUILD_TYPE=Release \ -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DLDC_LINK_MANUALLY=OFF \ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ $EXTRA_CMAKE_FLAGS ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version diff --git a/.cirrus.yml b/.cirrus.yml index 128da121254..01993862785 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -26,7 +26,6 @@ common_steps_template: &COMMON_STEPS_TEMPLATE -DCMAKE_INSTALL_PREFIX=$installDir \ -DINCLUDE_INSTALL_DIR=$installDir/import \ -DLDC_LINK_MANUALLY=OFF \ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ "${extraFlags[@]}" ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version @@ -406,8 +405,7 @@ task: -DLLVM_ROOT_DIR=$PWD/../llvm \ -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_LTO_LIBS=ON \ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON + -DBUILD_LTO_LIBS=ON ninja -j$PARALLELISM obj/ldc2.o all bin/ldc2 -version << : *COMMON_STEPS_TEMPLATE diff --git a/.github/actions/helper-build-ldc/action.yml b/.github/actions/helper-build-ldc/action.yml index 3d5fff18aed..3fafd106f0c 100644 --- a/.github/actions/helper-build-ldc/action.yml +++ b/.github/actions/helper-build-ldc/action.yml @@ -35,7 +35,6 @@ runs: -DLLVM_ROOT_DIR="$PWD/../${{ inputs.llvm_dir }}" \ -DD_COMPILER='${{ inputs.host_dc }}' \ -DLDC_LINK_MANUALLY=OFF \ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ ${{ inputs.specify_install_dir == 'true' && '-DCMAKE_INSTALL_PREFIX="$installDir"' || '' }} \ ${{ inputs.specify_install_dir == 'true' && '-DINCLUDE_INSTALL_DIR="$installDir/import"' || '' }} \ ${{ inputs.cmake_flags }} @@ -58,7 +57,6 @@ runs: "-DD_COMPILER=${{ inputs.host_dc }}" ^ -DCMAKE_C_COMPILER=clang-cl ^ -DCMAKE_CXX_COMPILER=clang-cl ^ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON ^ ${{ inputs.specify_install_dir == 'true' && '"-DCMAKE_INSTALL_PREFIX=%installDir%"' || '' }} ^ ${{ inputs.specify_install_dir == 'true' && '"-DINCLUDE_INSTALL_DIR=%installDir%\import"' || '' }} ^ ${{ inputs.cmake_flags }} diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index e8d96eb0982..3d7b697d887 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -121,7 +121,6 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ROOT_DIR="$PWD/llvm" \ -DLDC_LINK_MANUALLY=OFF \ - -DCMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY=ON \ ${{ matrix.cmake_flags }} ninja obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 --version diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c34a3e7710..42b50a5cfb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.4.3) if(POLICY CMP0025) - cmake_policy(SET CMP0025 NEW) + cmake_policy(SET CMP0025 NEW) +endif() +if(${CMAKE_VERSION} VERSION_GREATER "3.26.9") + # Prevent implicit dependencies for custom commands, e.g., + # `obj/ldc2.o` depending on `lib/libldc.a` with LDC_LINK_MANUALLY=ON. + # Only supported since CMake v3.27 unfortunately. + set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY ON) endif() project(ldc) From cf32bac668540f3dd022493b3ba89b598a485dfb Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Tue, 1 Aug 2023 22:20:32 +0200 Subject: [PATCH 278/301] Do not prefix static libs from pragma(lib) with `-l` (#4460) --- gen/declarations.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 585a33e7b84..c0d2ad3ad47 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -419,12 +419,24 @@ class CodegenVisitor : public Visitor { std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str(); gIR->addLinkerOption(llvm::StringRef(arg)); } else { - size_t const n = name.size() + 3; - char *arg = static_cast(mem.xmalloc(n)); - arg[0] = '-'; - arg[1] = 'l'; - memcpy(arg + 2, name.data(), name.size()); - arg[n - 1] = 0; + bool isStaticLib = name.endswith(".a"); + + size_t const nameLen = name.size(); + size_t const n = nameLen + 3; + char *arg = nullptr; + + if (isStaticLib == false) { + arg = static_cast(mem.xmalloc(n)); + arg[0] = '-'; + arg[1] = 'l'; + memcpy(arg + 2, name.data(), name.size()); + arg[n - 1] = 0; + } else { + arg = static_cast((mem.xmalloc(nameLen + 1))); + memcpy(arg, name.data(), nameLen); + arg[nameLen] = 0; + } + global.params.linkswitches.push(arg); if (triple.isOSBinFormatMachO()) { From 4d5553f545cd9170b3b373a7501a22023a5f0f73 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 5 Aug 2023 12:21:26 +0200 Subject: [PATCH 279/301] [slight cleanup] --- gen/declarations.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gen/declarations.cpp b/gen/declarations.cpp index c0d2ad3ad47..d98652a2ebb 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -419,17 +419,16 @@ class CodegenVisitor : public Visitor { std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str(); gIR->addLinkerOption(llvm::StringRef(arg)); } else { - bool isStaticLib = name.endswith(".a"); + const bool isStaticLib = name.endswith(".a"); + const size_t nameLen = name.size(); - size_t const nameLen = name.size(); - size_t const n = nameLen + 3; char *arg = nullptr; - - if (isStaticLib == false) { + if (!isStaticLib) { // name => -lname + const size_t n = nameLen + 3; arg = static_cast(mem.xmalloc(n)); arg[0] = '-'; arg[1] = 'l'; - memcpy(arg + 2, name.data(), name.size()); + memcpy(arg + 2, name.data(), nameLen); arg[n - 1] = 0; } else { arg = static_cast((mem.xmalloc(nameLen + 1))); From 87b22436e41c6d71fec7c35ff855aa74ca7346d0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 5 Aug 2023 12:12:07 +0200 Subject: [PATCH 280/301] Fix function pointers/delegates on Harvard architectures Resolves issue #4432 by setting the address space of such IR pointers. --- gen/tocall.cpp | 6 +++--- ir/irtype.cpp | 7 ++++++- ir/irtypefunction.cpp | 3 ++- tests/compilable/funcptr_harvard_gh4432.d | 24 +++++++++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 tests/compilable/funcptr_harvard_gh4432.d diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 47259c3d596..615aa7ba3ea 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -819,7 +819,7 @@ class ImplicitArgumentsBuilder { //////////////////////////////////////////////////////////////////////////////// -static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) { +static LLValue *DtoCallableValue(DValue *fn) { Type *type = fn->type->toBasetype(); if (type->ty == TY::Tfunction) { return DtoRVal(fn); @@ -829,7 +829,7 @@ static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) { LLValue *dg = DtoLVal(fn); llvm::StructType *st = isaStruct(DtoType(fn->type)); LLValue *funcptr = DtoGEP(st, dg, 0, 1); - return DtoLoad(ft->getPointerTo(), funcptr, ".funcptr"); + return DtoLoad(st->getElementType(1), funcptr, ".funcptr"); } LLValue *dg = DtoRVal(fn); assert(isaStruct(dg)); @@ -862,7 +862,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, } // get callee llvm value - LLValue *callable = DtoCallableValue(irFty.funcType, fnval); + LLValue *callable = DtoCallableValue(fnval); LLFunctionType *callableTy = irFty.funcType; if (dfnval && dfnval->func->isCsymbol()) { // See note in DtoDeclareFunction about K&R foward declared (void) functions diff --git a/ir/irtype.cpp b/ir/irtype.cpp index 0cf987d9284..d00f35c5386 100644 --- a/ir/irtype.cpp +++ b/ir/irtype.cpp @@ -118,10 +118,14 @@ IrTypePointer *IrTypePointer::get(Type *dt) { assert(!ctype); LLType *elemType; + unsigned addressSpace = 0; if (dt->ty == TY::Tnull) { elemType = llvm::Type::getInt8Ty(getGlobalContext()); } else { elemType = DtoMemType(dt->nextOf()); + if (dt->nextOf()->ty == TY::Tfunction) { + addressSpace = gDataLayout->getProgramAddressSpace(); + } // DtoType could have already created the same type, e.g. for // dt == Node* in struct Node { Node* n; }. @@ -130,7 +134,8 @@ IrTypePointer *IrTypePointer::get(Type *dt) { } } - auto t = new IrTypePointer(dt, llvm::PointerType::get(elemType, 0)); + auto t = + new IrTypePointer(dt, llvm::PointerType::get(elemType, addressSpace)); ctype = t; return t; } diff --git a/ir/irtypefunction.cpp b/ir/irtypefunction.cpp index 8a1a4ba9a9d..0e830f187af 100644 --- a/ir/irtypefunction.cpp +++ b/ir/irtypefunction.cpp @@ -53,7 +53,8 @@ IrTypeDelegate *IrTypeDelegate::get(Type *t) { IrFuncTy irFty(tf); llvm::Type *ltf = DtoFunctionType(tf, irFty, nullptr, Type::tvoid->pointerTo()); - llvm::Type *types[] = {getVoidPtrType(), getPtrToType(ltf)}; + llvm::Type *fptr = ltf->getPointerTo(gDataLayout->getProgramAddressSpace()); + llvm::Type *types[] = {getVoidPtrType(), fptr}; LLStructType *lt = LLStructType::get(gIR->context(), types, false); // Could have already built the type as part of a struct forward reference, diff --git a/tests/compilable/funcptr_harvard_gh4432.d b/tests/compilable/funcptr_harvard_gh4432.d new file mode 100644 index 00000000000..74d6b9f2c62 --- /dev/null +++ b/tests/compilable/funcptr_harvard_gh4432.d @@ -0,0 +1,24 @@ +// A minimal test for function pointers/delegates on a Harvard architecture, +// with code residing in a separate address space. + +// REQUIRES: target_AVR +// RUN: %ldc -mtriple=avr -betterC -c %s + +alias FP = void function(); +alias DG = void delegate(); + +void foo(FP fp, DG dg) +{ + fp(); + dg(); +} + +void bar() +{ + foo(() {}, delegate() {}); + + FP fp = &bar; + DG dg; + dg.funcptr = &bar; + foo(fp, dg); +} From 0d5ea6a2e97c765fb2d33058813e2c4b838c9560 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 5 Aug 2023 15:53:58 +0200 Subject: [PATCH 281/301] [extend test] --- tests/codegen/funcptr_harvard_gh4432.d | 37 +++++++++++++++++++++++ tests/compilable/funcptr_harvard_gh4432.d | 24 --------------- 2 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 tests/codegen/funcptr_harvard_gh4432.d delete mode 100644 tests/compilable/funcptr_harvard_gh4432.d diff --git a/tests/codegen/funcptr_harvard_gh4432.d b/tests/codegen/funcptr_harvard_gh4432.d new file mode 100644 index 00000000000..9400d982b07 --- /dev/null +++ b/tests/codegen/funcptr_harvard_gh4432.d @@ -0,0 +1,37 @@ +// Tests function pointers/delegates on a Harvard architecture, +// with code residing in a separate address space. + +// REQUIRES: target_AVR +// RUN: %ldc -mtriple=avr -betterC -output-ll -of=%t.ll %s && FileCheck %s < %t.ll +// RUN: %ldc -mtriple=avr -betterC -c %s + +alias FP = void function(); +alias DG = void delegate(); + +// CHECK: @_D22funcptr_harvard_gh44328globalFPPFZv = global {{void \(\) addrspace\(1\)\*|ptr addrspace\(1\)}} @_D22funcptr_harvard_gh44323barFZv, align 2 +__gshared FP globalFP = &bar; +// CHECK: @_D22funcptr_harvard_gh443217globalDataPointerPPFZv = global {{void \(\) addrspace\(1\)\*\*|ptr}} @_D22funcptr_harvard_gh44328globalFPPFZv, align 2 +__gshared FP* globalDataPointer = &globalFP; + +// CHECK: define void @_D22funcptr_harvard_gh44323fooFPFZvDQeZv({{.*}} addrspace(1){{\*?}} %fp_arg, { {{.*}} addrspace(1){{\*?}} } %dg_arg) addrspace(1) +void foo(FP fp, DG dg) +{ + // CHECK: call addrspace(1) void %1() + fp(); + // CHECK: call addrspace(1) void %.funcptr + dg(); + // CHECK-NEXT: ret void +} + +void bar() +{ + foo(() {}, delegate() {}); + + FP fp = &bar; + DG dg; + dg.funcptr = &bar; + foo(fp, dg); + + dg.funcptr = *globalDataPointer; + foo(globalFP, dg); +} diff --git a/tests/compilable/funcptr_harvard_gh4432.d b/tests/compilable/funcptr_harvard_gh4432.d deleted file mode 100644 index 74d6b9f2c62..00000000000 --- a/tests/compilable/funcptr_harvard_gh4432.d +++ /dev/null @@ -1,24 +0,0 @@ -// A minimal test for function pointers/delegates on a Harvard architecture, -// with code residing in a separate address space. - -// REQUIRES: target_AVR -// RUN: %ldc -mtriple=avr -betterC -c %s - -alias FP = void function(); -alias DG = void delegate(); - -void foo(FP fp, DG dg) -{ - fp(); - dg(); -} - -void bar() -{ - foo(() {}, delegate() {}); - - FP fp = &bar; - DG dg; - dg.funcptr = &bar; - foo(fp, dg); -} From ab137601df45bb65580fc5ad25b4c1f469f78f32 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 5 Aug 2023 16:10:16 +0200 Subject: [PATCH 282/301] [add changelog entry] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de9002914d1..5a9809c9d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Supports LLVM 11.0 - 15.0. #### Bug fixes +- Fix function pointers/delegates on Harvard architectures (e.g., AVR). (#4432, #4465) # LDC 1.33.0 (2023-07-23) From 8c9400e7f109a959d2204976436a488cfc5781f3 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 16:44:30 +0200 Subject: [PATCH 283/301] [whitespace nits] --- gen/ms-cxx-helper.cpp | 6 +++--- gen/optimizer.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 9f4eb7627f6..b66bc0cffd8 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -114,11 +114,11 @@ void cloneBlocks(const std::vector &srcblocks, if (!newInst) newInst = Inst->clone(); - #if LDC_LLVM_VER < 1600 +#if LDC_LLVM_VER < 1600 nbb->getInstList().push_back(newInst); - #else +#else newInst->insertInto(nbb, nbb->end()); - #endif +#endif VMap[Inst] = newInst; // Add instruction map to value. if (unwindTo) diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 6ed9a55d1b7..9f831c61139 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -626,11 +626,11 @@ void runOptimizationPasses(llvm::Module *M) { bool debugLogging = false; ppo.Indent = false; ppo.SkipAnalyses = false; - #if LDC_LLVM_VER < 1600 +#if LDC_LLVM_VER < 1600 StandardInstrumentations si(debugLogging, /*VerifyEach=*/false, ppo); - #else +#else StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo); - #endif +#endif si.registerCallbacks(pic, &fam); From a2cf7162073fc042e1c9d1afa3688c6a7375f780 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 16:45:14 +0200 Subject: [PATCH 284/301] Hide 2 new LLVM 16 cmdline options --- driver/cl_options.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index ac003542b41..1a0ac4933aa 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -850,7 +850,8 @@ void hideLLVMOptions() { "internalize-public-api-list", "iterative-counter-promotion", "join-liveintervals", "jump-table-type", "limit-float-precision", "lower-global-dtors-via-cxa-atexit", - "lto-embed-bitcode", "matrix-default-layout", "matrix-propagate-shape", + "lto-embed-bitcode", "matrix-default-layout", + "matrix-print-after-transpose-opt", "matrix-propagate-shape", "max-counter-promotions", "max-counter-promotions-per-loop", "mc-relax-all", "mc-x86-disable-arith-relaxation", "mcabac", "meabi", "memop-size-large", "memop-size-range", "merror-missing-parenthesis", @@ -882,9 +883,9 @@ void hideLLVMOptions() { "speculative-counter-promotion-to-loop", "spiller", "spirv-debug", "spirv-erase-cl-md", "spirv-lower-const-expr", "spirv-mem2reg", "spirv-no-deref-attr", "spirv-text", "spirv-verify-regularize-passes", - "split-machine-functions", "spv-lower-saddwithoverflow-validate", - "spvbool-validate", "spvmemmove-validate", - "stack-alignment", "stack-protector-guard", + "split-machine-functions", "spv-dump-deps", + "spv-lower-saddwithoverflow-validate", "spvbool-validate", + "spvmemmove-validate", "stack-alignment", "stack-protector-guard", "stack-protector-guard-offset", "stack-protector-guard-reg", "stack-size-section", "stack-symbol-ordering", "stackmap-version", "static-func-full-module-prefix", From 5a5475ad043885db23be9471df925ff6e5b82ebb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 16:45:35 +0200 Subject: [PATCH 285/301] README: Update min LLVM version and git submodules --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 390a1409f04..f13d12a0a94 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,8 @@ libraries is available on the project wiki for [Windows](http://wiki.dlang.org/Building_and_hacking_LDC_on_Windows_using_MSVC). If you have a working C++/D build environment, CMake, and a recent LLVM -version (≥ 6.0) available, there should be no big surprises. Do not -forget to make sure all the submodules (druntime, phobos, dmd-testsuite) -are up to date: +version (≥ 11) available, there should be no big surprises. Do not +forget to make sure the Phobos submodule is up to date: $ cd ldc $ git submodule update --init From 2e39008fadcf5a5c786bfaeffcb2bf40276e39c2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 19:18:18 +0200 Subject: [PATCH 286/301] CMake: Autodetect LLVM 16 --- cmake/Modules/FindLLVM.cmake | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake index e55fb6301ab..6add0c18448 100644 --- a/cmake/Modules/FindLLVM.cmake +++ b/cmake/Modules/FindLLVM.cmake @@ -32,7 +32,8 @@ # We also want an user-specified LLVM_ROOT_DIR to take precedence over the # system default locations such as /usr/local/bin. Executing find_program() # multiples times is the approach recommended in the docs. -set(llvm_config_names llvm-config-15.0 llvm-config150 llvm-config-15 +set(llvm_config_names llvm-config-16.0 llvm-config160 llvm-config-16 + llvm-config-15.0 llvm-config150 llvm-config-15 llvm-config-14.0 llvm-config140 llvm-config-14 llvm-config-13.0 llvm-config130 llvm-config-13 llvm-config-12.0 llvm-config120 llvm-config-12 @@ -47,13 +48,13 @@ if(APPLE) # extra fallbacks for MacPorts & Homebrew find_program(LLVM_CONFIG NAMES ${llvm_config_names} - PATHS /opt/local/libexec/llvm-15/bin - /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin /opt/local/libexec/llvm-12/bin - /opt/local/libexec/llvm-11/bin + PATHS /opt/local/libexec/llvm-16/bin /opt/local/libexec/llvm-15/bin + /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin + /opt/local/libexec/llvm-12/bin /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm/bin - /usr/local/opt/llvm@15/bin - /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin /usr/local/opt/llvm@12/bin - /usr/local/opt/llvm@11/bin + /usr/local/opt/llvm@16/bin /usr/local/opt/llvm@15/bin + /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin + /usr/local/opt/llvm@12/bin /usr/local/opt/llvm@11/bin /usr/local/opt/llvm/bin NO_DEFAULT_PATH) endif() From c8857e8cb88617aafdb8ffbf0ac0499c254af811 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 22:49:19 +0200 Subject: [PATCH 287/301] Fix some LLVM 16 deprecations wrt. DataLayout::getABITypeAlignment() and llvm::makeArrayRef() --- gen/arrays.cpp | 5 ++--- gen/pgo_ASTbased.cpp | 4 ++-- gen/rttibuilder.cpp | 2 +- gen/target.cpp | 4 ++-- gen/tollvm.cpp | 10 +++------- ir/irclass.cpp | 6 ++---- runtime/jit-rt/cpp-so/bind.cpp | 6 ++---- 7 files changed, 14 insertions(+), 23 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 16abb3c9aca..da319d5e3a7 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -624,11 +624,10 @@ void initializeArrayLiteral(IRState *p, ArrayLiteralExp *ale, //////////////////////////////////////////////////////////////////////////////// LLConstant *DtoConstSlice(LLConstant *dim, LLConstant *ptr, Type *type) { LLConstant *values[2] = {dim, ptr}; - llvm::ArrayRef valuesRef = llvm::makeArrayRef(values, 2); LLStructType *lltype = type ? isaStruct(DtoType(type)) - : LLConstantStruct::getTypeForElements(gIR->context(), valuesRef); - return LLConstantStruct::get(lltype, valuesRef); + : LLConstantStruct::getTypeForElements(gIR->context(), values); + return LLConstantStruct::get(lltype, values); } //////////////////////////////////////////////////////////////////////////////// diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index f1307e236c9..6f704b896dc 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -117,7 +117,7 @@ class PGOHash { if (Count && Count % NumTypesPerWord == 0) { using namespace llvm::support; uint64_t Swapped = endian::byte_swap(Working); - MD5.update(llvm::makeArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); + MD5.update(llvm::ArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); Working = 0; } @@ -138,7 +138,7 @@ class PGOHash { if (Working) { using namespace llvm::support; uint64_t Swapped = endian::byte_swap(Working); - MD5.update(llvm::makeArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); + MD5.update(llvm::ArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); } // Finalize the MD5 and return the hash. diff --git a/gen/rttibuilder.cpp b/gen/rttibuilder.cpp index 4a3a1adb327..b8ba7006943 100644 --- a/gen/rttibuilder.cpp +++ b/gen/rttibuilder.cpp @@ -46,7 +46,7 @@ void RTTIBuilder::push(llvm::Constant *C) { // We need to explicitly zero any padding bytes as per TDPL §7.1.1 (and // also match the struct type lowering code here). const uint64_t fieldStart = llvm::alignTo( - prevFieldEnd, gDataLayout->getABITypeAlignment(C->getType())); + prevFieldEnd, gDataLayout->getABITypeAlign(C->getType()).value()); const uint64_t paddingBytes = fieldStart - prevFieldEnd; if (paddingBytes) { diff --git a/gen/target.cpp b/gen/target.cpp index 6285c9f1ea6..8b81afe12e8 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -102,7 +102,7 @@ void Target::_init(const Param ¶ms) { realType = getRealType(triple); realsize = gDataLayout->getTypeAllocSize(realType); realpad = realsize - gDataLayout->getTypeStoreSize(realType); - realalignsize = gDataLayout->getABITypeAlignment(realType); + realalignsize = gDataLayout->getABITypeAlign(realType).value(); classinfosize = 0; // unused maxStaticDataSize = std::numeric_limits::max(); @@ -211,7 +211,7 @@ unsigned Target::alignsize(Type *type) { if (type->ty == TY::Tvoid) { return 1; } - return gDataLayout->getABITypeAlignment(DtoType(type)); + return gDataLayout->getABITypeAlign(DtoType(type)).value(); } /****************************** diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 2fc705c05f9..4f039293dd4 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -532,9 +532,7 @@ LLValue *DtoLoad(DLValue *src, const char *name) { // the type. LLValue *DtoAlignedLoad(LLType *type, LLValue *src, const char *name) { llvm::LoadInst *ld = DtoLoadImpl(type, src, name); - if (auto alignment = getABITypeAlign(ld->getType())) { - ld->setAlignment(llvm::Align(alignment)); - } + ld->setAlignment(gDataLayout->getABITypeAlign(ld->getType())); return ld; } @@ -570,9 +568,7 @@ void DtoAlignedStore(LLValue *src, LLValue *dst) { assert(!src->getType()->isIntegerTy(1) && "Should store bools as i8 instead of i1."); llvm::StoreInst *st = gIR->ir->CreateStore(src, dst); - if (auto alignment = getABITypeAlign(src->getType())) { - st->setAlignment(llvm::Align(alignment)); - } + st->setAlignment(gDataLayout->getABITypeAlign(src->getType())); } //////////////////////////////////////////////////////////////////////////////// @@ -747,7 +743,7 @@ size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); } size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); } unsigned int getABITypeAlign(LLType *t) { - return gDataLayout->getABITypeAlignment(t); + return gDataLayout->getABITypeAlign(t).value(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 88e54d089db..edd3d27c352 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -121,8 +121,7 @@ LLGlobalVariable *IrClass::getClassInfoSymbol(bool define) { // Construct the metadata and insert it into the module. const auto metaname = getMetadataName(CD_PREFIX, typeInfo); llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata(metaname); - node->addOperand(llvm::MDNode::get( - gIR->context(), llvm::makeArrayRef(mdVals, CD_NumFields))); + node->addOperand(llvm::MDNode::get(gIR->context(), mdVals)); } if (!define) @@ -738,8 +737,7 @@ LLConstant *IrClass::getClassInfoInterfaces() { // create Interface struct LLConstant *inits[3] = {ci, vtb, off}; - LLConstant *entry = - LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); + LLConstant *entry = LLConstantStruct::get(interface_type, inits); constants.push_back(entry); } diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index e29d2275608..ff87e81b2ef 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -71,16 +71,14 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, if (param.type == ParamType::Aggregate && srcType.isPointerTy()) { auto elemType = srcType.getPointerElementType(); auto stackArg = builder.CreateAlloca(elemType); - if (auto alignment = layout.getABITypeAlignment(elemType)) - stackArg->setAlignment(llvm::Align(alignment)); + stackArg->setAlignment(layout.getABITypeAlign(elemType)); auto init = parseInitializer(layout, *elemType, param.data, errHandler, override); builder.CreateStore(init, stackArg); return stackArg; } auto stackArg = builder.CreateAlloca(&srcType); - if (auto alignment = layout.getABITypeAlignment(&srcType)) - stackArg->setAlignment(llvm::Align(alignment)); + stackArg->setAlignment(layout.getABITypeAlign(&srcType)); auto init = parseInitializer(layout, srcType, param.data, errHandler, override); builder.CreateStore(init, stackArg); From 448207826d0ccb3381683b65d06159e4df721a88 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 16:46:08 +0200 Subject: [PATCH 288/301] Bump LDC-LLVM to v16.0.6 for official packages --- .cirrus.yml | 2 +- .github/actions/1-setup/action.yml | 2 +- .github/workflows/main.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index bb0e6e832c5..a0e942896ed 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -249,7 +249,7 @@ install_macos_prerequisites_template: &INSTALL_MACOS_PREREQUISITES_TEMPLATE environment: CIRRUS_CLONE_DEPTH: 50 HOST_LDC_VERSION: 1.31.0 - LLVM_VERSION: 15.0.7 + LLVM_VERSION: 16.0.6 GITHUB_TOKEN: ENCRYPTED[0955bd48c8d4e5391446fc0149d0719ad0b63df27ec9e6c180a5730a5b10dc7f28f09d1383423db158d21380ee2b022a] task: diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index 4699f77a3e5..b8e9bf55da2 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -99,7 +99,7 @@ runs: shell: bash run: | set -eux - sudo ln -sf "$(dirname "$PWD")/clang/bin/ld.lld" /usr/bin/ld + sudo ln -sf "$(dirname "$PWD")/llvm/bin/ld.lld" /usr/bin/ld ld --version - name: Install ninja diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d0d059fff50..2faa2b50bbb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,8 @@ concurrency: cancel-in-progress: true env: - CLANG_VERSION: 16.0.0 - LLVM_VERSION: c12d3509 + CLANG_VERSION: 15.0.6 + LLVM_VERSION: 16.0.6 jobs: build-native: From e3be77442dbe23ce5f53c3b224f2ebb921eb1fea Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 12 Aug 2023 23:18:22 +0200 Subject: [PATCH 289/301] CI: Disable function specializations with LLVM 16 --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2faa2b50bbb..005d5a0c277 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,7 +44,7 @@ jobs: -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -func-specialization-size-threshold=1000000000 -L-mllvm -L-func-specialization-size-threshold=1000000000" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true @@ -54,7 +54,7 @@ jobs: bootstrap_cmake_flags: -DBUILD_LTO_LIBS=ON extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - "-DD_COMPILER_FLAGS=-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + "-DD_COMPILER_FLAGS=-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -func-specialization-size-threshold=1000000000" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true From 38ed66fa6af1d9df2da0c946230532e7cc479d93 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 13 Aug 2023 17:32:32 +0200 Subject: [PATCH 290/301] LLVM 16: Disable function specializations by default In ldc2.conf for LDC itself, and in the linker cmdline for LTO plugins. --- .github/actions/5a-ios/action.yml | 1 + .github/actions/merge-macos/action.yml | 4 ++++ .github/workflows/main.yml | 4 ++-- driver/linker-gcc.cpp | 10 ++++++++++ runtime/CMakeLists.txt | 6 ++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/actions/5a-ios/action.yml b/.github/actions/5a-ios/action.yml index 3d41c168be5..cc01e0384bd 100644 --- a/.github/actions/5a-ios/action.yml +++ b/.github/actions/5a-ios/action.yml @@ -41,6 +41,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=$triple\", \"-Xcc=-miphoneos-version-min=$deployment_target\", diff --git a/.github/actions/merge-macos/action.yml b/.github/actions/merge-macos/action.yml index 7efd0e4a906..b94eea63837 100644 --- a/.github/actions/merge-macos/action.yml +++ b/.github/actions/merge-macos/action.yml @@ -48,6 +48,7 @@ runs: // default switches injected before all explicit command-line switches switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", ]; // default switches appended after all explicit command-line switches post-switches = [ @@ -63,6 +64,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=x86_64-apple-macos\", ]; @@ -76,6 +78,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=arm64-apple-macos\", ]; @@ -89,6 +92,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=arm64-apple-ios$ios_version\", \"-Xcc=-miphoneos-version-min=$ios_version\", diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 005d5a0c277..2faa2b50bbb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,7 +44,7 @@ jobs: -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -func-specialization-size-threshold=1000000000 -L-mllvm -L-func-specialization-size-threshold=1000000000" + -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true @@ -54,7 +54,7 @@ jobs: bootstrap_cmake_flags: -DBUILD_LTO_LIBS=ON extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - "-DD_COMPILER_FLAGS=-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -func-specialization-size-threshold=1000000000" + "-DD_COMPILER_FLAGS=-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 9105cbb4932..2aef6402578 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -149,6 +149,11 @@ void ArgsBuilder::addLTOGoldPluginFlags(bool requirePlugin) { addLdFlag("-plugin-opt=-function-sections"); if (TO.DataSections) addLdFlag("-plugin-opt=-data-sections"); + +#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700 + // LLVM 16: disable function specializations by default + addLdFlag("-plugin-opt=-func-specialization-size-threshold=1000000000"); +#endif } // Returns an empty string when libLTO.dylib was not specified nor found. @@ -179,6 +184,11 @@ void ArgsBuilder::addDarwinLTOFlags() { std::string dylibPath = getLTOdylibPath(); if (!dylibPath.empty()) { addLdFlag("-lto_library", dylibPath); + +#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700 + // LLVM 16: disable function specializations by default + addLdFlag("-mllvm", "-func-specialization-size-threshold=1000000000"); +#endif } } diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index abcc804aae2..5766d6211fa 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -251,6 +251,12 @@ elseif(${BUILD_SHARED_LIBS} STREQUAL "OFF") set(ADDITIONAL_DEFAULT_LDC_SWITCHES "${ADDITIONAL_DEFAULT_LDC_SWITCHES}\n \"-link-defaultlib-shared=false\",") endif() +# LLVM 16: Disable function specializations by default. +# They cause miscompiles of e.g. the frontend for some targets (macOS x86_64 and Windows x64). +if(LDC_LLVM_VER GREATER 1599 AND LDC_LLVM_VER LESS 1700) + set(ADDITIONAL_DEFAULT_LDC_SWITCHES "${ADDITIONAL_DEFAULT_LDC_SWITCHES}\n \"-func-specialization-size-threshold=1000000000\",") +endif() + # Default wasm stack is only 64kb, this is rather small, let's bump it to 1mb set(WASM_DEFAULT_LDC_SWITCHES "${WASM_DEFAULT_LDC_SWITCHES}\n \"-L-z\", \"-Lstack-size=1048576\",") # Protect from stack overflow overwriting global memory From 1f5c8ba6241eb6ecbbe89d314188adea628afeab Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 13 Aug 2023 19:24:47 +0200 Subject: [PATCH 291/301] GHA: Switch vanilla LLVM 16.0.0 job to LDC-LLVM 16.0.6 --- .github/workflows/supported_llvm_versions.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 918b2b6ca53..888aabe1cd7 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -18,7 +18,7 @@ jobs: - job_name: Ubuntu 20.04, LLVM 16, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta - llvm_version: 16.0.0 + llvm_version: 16.0.6 # LDC-LLVM - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta @@ -95,8 +95,12 @@ jobs: else suffix='x86_64-linux-gnu-ubuntu-16.04' fi - curl -fL --retry 3 --max-time 300 -o llvm.tar.xz \ - https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/clang+llvm-$version-$suffix.tar.xz + url="https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/clang+llvm-$version-$suffix.tar.xz" + # FIXME: weird crashes with official v16.0.0 archive; use LDC-LLVM instead + if [[ "$version" =~ ^16\. ]]; then + url="https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$version/llvm-$version-linux-x86_64.tar.xz" + fi + curl -fL --retry 3 --max-time 300 -o llvm.tar.xz "$url" mkdir llvm tar -xf llvm.tar.xz --strip 1 -C llvm rm llvm.tar.xz From 4aa5bc149cc3ca844f449ec80d0a1c732c264ef4 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 14 Aug 2023 11:43:09 +0200 Subject: [PATCH 292/301] [add changelog entry] --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de9002914d1..e0b1432456f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ #### Big news - Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) +- Support for [LLVM 16](https://releases.llvm.org/16.0.0/docs/ReleaseNotes.html). The prebuilt packages use v16.0.6. (#4411, #4423) + - We have come across miscompiles with LLVM 16's newly-enabled-by-default function specializations (on Win64 and macOS). To be on the safe side, LDC disables them by default for all targets via `-func-specialization-size-threshold=1000000000` in `etc/ldc2.conf` (and separately for LTO on Posix platforms). To enable the function specializations, explicitly override it with e.g. `-func-specialization-size-threshold=100` (the LLVM 16 default) and, for LTO on Posix, a similar LTO plugin option in the linker cmdline (see linker cmdline with `-v`). #### Platform support -- Supports LLVM 11.0 - 15.0. +- Supports LLVM 11.0 - 16.0. #### Bug fixes From 1d88f3f24e9820680d81e22337af483c672b79ac Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 17 Aug 2023 01:42:53 +0200 Subject: [PATCH 293/301] CircleCI: Remove Mac jobs (#4469) Mac isn't covered by the open-source credit budget, and apparently the monthly 30k credits for these 2 jobs are exhaused rather soon; there hasn't been *that* much activity lately, but the jobs fail to trigger for some days now. On some days, we've apparently reached 8k credits. The 'Vanilla LLVM' GHA Mac x64 jobs are similar (e.g., testing BUILD_SHARED_LIBS). --- .circleci/config.yml | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ad4cc3548c..f0ea4449621 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,24 +33,6 @@ commonSteps: &commonSteps rm ninja-linux.zip # Add CMake and Ninja to PATH for future steps echo "export PATH=$PWD/cmake/bin:$PWD/ninja:$PATH" >> $BASH_ENV - else - # Download & extract CMake - curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-macos-universal.tar.gz - mkdir cmake - tar -xf cmake.tar.gz --strip 3 -C cmake - rm cmake.tar.gz - # Download & extract Ninja - curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-mac.zip - mkdir ninja - unzip ninja-mac.zip -d ninja - rm ninja-mac.zip - # Download & extract LDC-flavoured LLVM with enabled assertions - curl -fL --retry 3 --max-time 300 -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-osx-x86_64-withAsserts.tar.xz - mkdir llvm - tar -xf llvm.tar.xz --strip 1 -C llvm - rm llvm.tar.xz - # Add CMake, Ninja and LLVM to PATH for future steps - echo "export PATH=$PWD/cmake/bin:$PWD/ninja:$PWD/llvm/bin:$PATH" >> $BASH_ENV fi # Install lit python3 --version @@ -129,28 +111,6 @@ jobs: - EXTRA_APT_PACKAGES: gdmd llvm-11-dev libclang-common-11-dev - HOST_LDC_VERSION: 1.24.0 - EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" - macOS-x64: - <<: *commonSteps - macos: - xcode: "13.2.1" - environment: - - PARALLELISM: 4 - - CI_OS: osx - - LLVM_VERSION: 15.0.7 - - HOST_LDC_VERSION: 1.24.0 - - EXTRA_CMAKE_FLAGS: "-DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DBUILD_LTO_LIBS=ON" - - MACOSX_DEPLOYMENT_TARGET: 11.0 # silence `ld: warning: object file (…/libphobos2-ldc.a(adler32.c.o)) was built for newer macOS version (11.6) than being linked (11.0)` - macOS-x64-sharedLibsOnly: - <<: *commonSteps - macos: - xcode: "13.2.1" - environment: - - PARALLELISM: 4 - - CI_OS: osx - - LLVM_VERSION: 15.0.7 - - HOST_LDC_VERSION: 1.24.0 - - EXTRA_CMAKE_FLAGS: "-DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON" - - MACOSX_DEPLOYMENT_TARGET: 11.0 workflows: version: 2 @@ -158,5 +118,3 @@ workflows: jobs: - Ubuntu-20.04-multilib-rtSanitizers - Ubuntu-20.04-sharedLibsOnly-gdmd - - macOS-x64 - - macOS-x64-sharedLibsOnly From 36596cf165642bd2102fe831b497253f5b849c30 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 17 Aug 2023 02:07:31 +0200 Subject: [PATCH 294/301] Cirrus CI: Remove job 'Ubuntu 20.04 x64 multilib rtSanitizers' As preparation for the upcoming resource limits starting in September. This job was a duplicate of the Circle CI one, except for having twice as many CPU cores and using LDC v1.31 as host compiler, not v1.24 like Circle CI. --- .cirrus.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a0e942896ed..a6657ceec62 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -252,23 +252,6 @@ environment: LLVM_VERSION: 16.0.6 GITHUB_TOKEN: ENCRYPTED[0955bd48c8d4e5391446fc0149d0719ad0b63df27ec9e6c180a5730a5b10dc7f28f09d1383423db158d21380ee2b022a] -task: - name: Ubuntu 20.04 x64 multilib rtSanitizers - container: - image: ubuntu:20.04 - cpu: 8 - memory: 16G - timeout_in: 20m - environment: - CI_ARCH: x86_64 - CI_OS: linux - EXTRA_APT_PACKAGES: "llvm-11-dev libclang-common-11-dev" - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" - PARALLELISM: 8 - PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} - << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE - << : *COMMON_STEPS_TEMPLATE - task: name: Ubuntu rolling x64 shared-libs-only gdmd # allow failures - gdb v10 came with regressions From e46f979b749ed920c5325459795fc53b4f735914 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 17 Aug 2023 02:10:49 +0200 Subject: [PATCH 295/301] Cirrus CI: Remove job 'Ubuntu 20.04 x64 bootstrap' Testing bootstrappability from old LDC v1.9.0 in a 'Vanilla LLVM' GHA job instead. --- .cirrus.yml | 18 ------------------ .github/workflows/supported_llvm_versions.yml | 6 +++--- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a6657ceec62..79649e2c0ac 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -273,24 +273,6 @@ task: << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE -task: - name: Ubuntu 20.04 x64 bootstrap - container: - image: ubuntu:20.04 - cpu: 8 - memory: 16G - timeout_in: 15m - environment: - CI_ARCH: x86_64 - CI_OS: linux - HOST_LDC_VERSION: 1.9.0 - EXTRA_APT_PACKAGES: "llvm-11-dev libclang-common-11-dev" - EXTRA_CMAKE_FLAGS: "-DBUILD_LTO_LIBS=ON" - PARALLELISM: 8 - PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} - << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE - << : *COMMON_STEPS_TEMPLATE - task: name: macOS 12 $TASK_NAME_SUFFIX macos_instance: diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 888aabe1cd7..475223b2cfa 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -23,9 +23,9 @@ jobs: os: ubuntu-20.04 host_dc: ldc-beta llvm_version: 15.0.6 - - job_name: Ubuntu 20.04, LLVM 12, latest LDC beta + - job_name: Ubuntu 20.04, LLVM 12, bootstrap LDC os: ubuntu-20.04 - host_dc: ldc-beta + host_dc: ldc-1.9.0 llvm_version: 12.0.1 cmake_flags: -DBUILD_SHARED_LIBS=ON -DLIB_SUFFIX=64 - job_name: Ubuntu 20.04, LLVM 11, latest DMD beta @@ -106,7 +106,7 @@ jobs: rm llvm.tar.xz - name: 'Linux: Make lld the default linker' - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.host_dc != 'ldc-1.9.0' run: | set -eux echo "Using lld to work around sporadic failures" From 8838d5faaa628e6967ec6725270b66cc512ea209 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 17 Aug 2023 12:55:34 +0200 Subject: [PATCH 296/301] GH Actions: Export ~all symbols from prebuilt LTO'd macOS binaries (#4468) Fixes #4462 for our official packages. --- .github/workflows/main.yml | 8 ++++++-- tests/plugins/basic_sema_plugin.d | 3 --- tests/plugins/visitor_example.d | 3 --- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2faa2b50bbb..b4dcffd598b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,9 +42,12 @@ jobs: bootstrap_cmake_flags: >- -DBUILD_LTO_LIBS=ON -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ + # https://github.com/ldc-developers/ldc/issues/4462: + # When using LTO, we need to explicitly export ~all symbols for plugin support via `ld64 -exported_symbol '__*'`. + # Additionally `-w` to suppress resulting linker warnings. extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L-exported_symbol '-L__*' -L-w" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true @@ -162,9 +165,10 @@ jobs: os: osx arch: arm64 bootstrap_cmake_flags: -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ + # see native macOS job comment for extra flags (https://github.com/ldc-developers/ldc/issues/4462) extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + -DD_COMPILER_FLAGS="-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L-exported_symbol '-L__*' -L-w" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d index 873c2584df3..071f1dcf8fd 100644 --- a/tests/plugins/basic_sema_plugin.d +++ b/tests/plugins/basic_sema_plugin.d @@ -1,8 +1,5 @@ // REQUIRES: Plugins -// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86 (but not for all CI testers...) -// UNSUPPORTED: Darwin && host_X86 - // RUN: split-file %s %t --leading-lines // RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d index 1b106444e58..ec8af7d2c90 100644 --- a/tests/plugins/visitor_example.d +++ b/tests/plugins/visitor_example.d @@ -1,9 +1,6 @@ // REQUIRES: Plugins // REQUIRES: ABI_compatible_with_host_D -// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86. -// UNSUPPORTED: Darwin && host_X86 - // RUN: split-file %s %t --leading-lines // RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build // RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d From aea290d49523e425b42c8c4215ca0767499f118a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 17 Aug 2023 18:20:32 +0200 Subject: [PATCH 297/301] Bump bundled reggae to latest master (#4471) There have been significant improvements recently. --- packaging/reggae_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/reggae_version b/packaging/reggae_version index ceb52eaf4dd..dafb90e96a6 100644 --- a/packaging/reggae_version +++ b/packaging/reggae_version @@ -1 +1 @@ -fb18f3eb6cb0cd82ea401750f4e200fa82d91e39 \ No newline at end of file +051e7d192155693d309cf09439e395bccb4292cf \ No newline at end of file From cb33ca6ddcbd4cbcde04fd987cf1965d70ed4f51 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Sun, 16 Apr 2023 22:28:41 +0200 Subject: [PATCH 298/301] Default to rv64gc for hosted riscv64 target Also select the correct ABI by default matching the enabled features (double, float or no floating point). Fixes #4375 --- CHANGELOG.md | 1 + driver/targetmachine.cpp | 41 ++++++++++++++++++++++++++++++++++----- driver/targetmachine.h | 5 ++++- driver/tool.cpp | 6 +++--- tests/driver/riscv_abi4.d | 6 ++++++ tests/driver/riscv_abi5.d | 6 ++++++ 6 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 tests/driver/riscv_abi4.d create mode 100644 tests/driver/riscv_abi5.d diff --git a/CHANGELOG.md b/CHANGELOG.md index 26a0cd92927..bead694fa1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ #### Platform support - Supports LLVM 11.0 - 16.0. +- When nothing else specified now defaults to `-mattr=+m,+a,+f,+d,+c` when targeting hosted 64-bit RISC-V. (#4390) #### Bug fixes - Fix function pointers/delegates on Harvard architectures (e.g., AVR). (#4432, #4465) diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 3f16e20813c..74f9efbc06a 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -57,7 +57,7 @@ static llvm::cl::opt preserveDwarfLineSection( llvm::cl::init(false)); #endif -const char *getABI(const llvm::Triple &triple) { +const char *getABI(const llvm::Triple &triple, const llvm::SmallVector &features) { llvm::StringRef ABIName(opts::mABI); if (ABIName != "") { switch (triple.getArch()) { @@ -111,6 +111,17 @@ const char *getABI(const llvm::Triple &triple) { ABIName.str().c_str()); } + // checks if the features include ± + auto hasFeature = [&features](llvm::StringRef feature) { + for (int i = features.size() - 1; i >= 0; i--) { + auto f = features[i]; + if (f.substr(1) == feature) { + return f[0] == '+'; + } + } + return false; + }; + switch (triple.getArch()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: @@ -120,6 +131,10 @@ const char *getABI(const llvm::Triple &triple) { case llvm::Triple::ppc64le: return "elfv2"; case llvm::Triple::riscv64: + if (hasFeature("d")) + return "lp64d"; + if (hasFeature("f")) + return "lp64f"; return "lp64"; case llvm::Triple::riscv32: return "ilp32"; @@ -430,9 +445,13 @@ createTargetMachine(const std::string targetTriple, const std::string arch, // checks if the features include ± auto hasFeature = [&features](llvm::StringRef feature) { - return std::any_of( - features.begin(), features.end(), - [feature](llvm::StringRef f) { return f.substr(1) == feature; }); + for (int i = features.size() - 1; i >= 0; i--) { + auto f = features[i]; + if (f.substr(1) == feature) { + return f[0] == '+'; + } + } + return false; }; // cmpxchg16b is not available on old 64bit CPUs. Enable code generation @@ -441,6 +460,18 @@ createTargetMachine(const std::string targetTriple, const std::string arch, features.push_back("+cx16"); } + // For a hosted RISC-V 64-bit target default to rv64gc if nothing has + // been selected + if (triple.getArch() == llvm::Triple::riscv64 && + triple.getOS() != llvm::Triple::UnknownOS && + features.empty()) { + features.push_back("+m"); + features.push_back("+a"); + features.push_back("+f"); + features.push_back("+d"); + features.push_back("+c"); + } + // Handle cases where LLVM picks wrong default relocModel #if LDC_LLVM_VER >= 1600 if (relocModel.has_value()) {} @@ -478,7 +509,7 @@ createTargetMachine(const std::string targetTriple, const std::string arch, opts::InitTargetOptionsFromCodeGenFlags(triple); if (targetOptions.MCOptions.ABIName.empty()) - targetOptions.MCOptions.ABIName = getABI(triple); + targetOptions.MCOptions.ABIName = getABI(triple, features); if (floatABI == FloatABI::Default) { switch (triple.getArch()) { diff --git a/driver/targetmachine.h b/driver/targetmachine.h index 17e664a2bf8..66a84cfd826 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -15,6 +15,8 @@ #pragma once #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/CodeGen.h" #include #include @@ -73,4 +75,5 @@ MipsABI::Type getMipsABI(); const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple, std::string &errorMsg); -const char *getABI(const llvm::Triple &triple); +const char *getABI(const llvm::Triple &triple, + const llvm::SmallVector &features); diff --git a/driver/tool.cpp b/driver/tool.cpp index 806e365996e..cf633499a8f 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -123,14 +123,14 @@ void appendTargetArgsForGcc(std::vector &args) { case Triple::riscv64: { - std::string mabi = getABI(triple); - args.push_back("-mabi=" + mabi); - extern llvm::TargetMachine* gTargetMachine; auto featuresStr = gTargetMachine->getTargetFeatureString(); llvm::SmallVector features; featuresStr.split(features, ",", -1, false); + std::string mabi = getABI(triple, features); + args.push_back("-mabi=" + mabi); + // Returns true if 'feature' is enabled and false otherwise. Handles the // case where the feature is specified multiple times ('+m,-m'), and // takes the last occurrence. diff --git a/tests/driver/riscv_abi4.d b/tests/driver/riscv_abi4.d new file mode 100644 index 00000000000..a5c93bab615 --- /dev/null +++ b/tests/driver/riscv_abi4.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-linux --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64d -march=rv64gc + +void main() {} diff --git a/tests/driver/riscv_abi5.d b/tests/driver/riscv_abi5.d new file mode 100644 index 00000000000..11cd97c41c5 --- /dev/null +++ b/tests/driver/riscv_abi5.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-linux -mattr=+m,+a,+f,-d --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64f -march=rv64imaf_zicsr_zifencei + +void main() {} From a2c63b6fea727720938e91079deb5b9e25a4d5cd Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 25 Aug 2023 13:42:00 +0200 Subject: [PATCH 299/301] [slightly revise] --- CHANGELOG.md | 2 +- driver/targetmachine.cpp | 54 +++++++++++------------- driver/targetmachine.h | 5 ++- driver/tool.cpp | 91 ++++++++++++++++------------------------ 4 files changed, 67 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bead694fa1d..b90c785e78e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ #### Platform support - Supports LLVM 11.0 - 16.0. -- When nothing else specified now defaults to `-mattr=+m,+a,+f,+d,+c` when targeting hosted 64-bit RISC-V. (#4390) +- 64-bit RISC-V: Now defaults to `-mattr=+m,+a,+f,+d,+c` ('rv64gc' ABI) for non-bare-metal targets, i.e., if the target triple includes a valid operating system. (#4390) #### Bug fixes - Fix function pointers/delegates on Harvard architectures (e.g., AVR). (#4432, #4465) diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 74f9efbc06a..b454fa2c8e1 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -57,7 +57,20 @@ static llvm::cl::opt preserveDwarfLineSection( llvm::cl::init(false)); #endif -const char *getABI(const llvm::Triple &triple, const llvm::SmallVector &features) { +// Returns true if 'feature' is enabled and false otherwise. Handles the +// case where the feature is specified multiple times ('+m,-m'), and +// takes the last occurrence. +bool isFeatureEnabled(const llvm::SmallVectorImpl &features, + llvm::StringRef feature) { + for (auto it = features.rbegin(), end = features.rend(); it != end; ++it) { + if (it->substr(1) == feature) { + return (*it)[0] == '+'; + } + } + return false; +}; + +const char *getABI(const llvm::Triple &triple, const llvm::SmallVectorImpl &features) { llvm::StringRef ABIName(opts::mABI); if (ABIName != "") { switch (triple.getArch()) { @@ -111,17 +124,6 @@ const char *getABI(const llvm::Triple &triple, const llvm::SmallVector - auto hasFeature = [&features](llvm::StringRef feature) { - for (int i = features.size() - 1; i >= 0; i--) { - auto f = features[i]; - if (f.substr(1) == feature) { - return f[0] == '+'; - } - } - return false; - }; - switch (triple.getArch()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: @@ -131,9 +133,9 @@ const char *getABI(const llvm::Triple &triple, const llvm::SmallVector auto hasFeature = [&features](llvm::StringRef feature) { - for (int i = features.size() - 1; i >= 0; i--) { - auto f = features[i]; - if (f.substr(1) == feature) { - return f[0] == '+'; - } - } - return false; + return std::any_of( + features.begin(), features.end(), + [feature](llvm::StringRef f) { return f.substr(1) == feature; }); }; // cmpxchg16b is not available on old 64bit CPUs. Enable code generation @@ -462,14 +460,12 @@ createTargetMachine(const std::string targetTriple, const std::string arch, // For a hosted RISC-V 64-bit target default to rv64gc if nothing has // been selected - if (triple.getArch() == llvm::Triple::riscv64 && - triple.getOS() != llvm::Triple::UnknownOS && - features.empty()) { - features.push_back("+m"); - features.push_back("+a"); - features.push_back("+f"); - features.push_back("+d"); - features.push_back("+c"); + if (triple.getArch() == llvm::Triple::riscv64 && features.empty()) { + const llvm::StringRef os = triple.getOSName(); + const bool isFreeStanding = os.empty() || os == "unknown" || os == "none"; + if (!isFreeStanding) { + features = {"+m", "+a", "+f", "+d", "+c"}; + } } // Handle cases where LLVM picks wrong default relocModel diff --git a/driver/targetmachine.h b/driver/targetmachine.h index 66a84cfd826..66e1a20be8e 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -76,4 +76,7 @@ const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple, std::string &errorMsg); const char *getABI(const llvm::Triple &triple, - const llvm::SmallVector &features); + const llvm::SmallVectorImpl &features); + +bool isFeatureEnabled(const llvm::SmallVectorImpl &features, + llvm::StringRef feature); diff --git a/driver/tool.cpp b/driver/tool.cpp index cf633499a8f..dea42d05a58 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -121,62 +121,45 @@ void appendTargetArgsForGcc(std::vector &args) { } return; - case Triple::riscv64: - { - extern llvm::TargetMachine* gTargetMachine; - auto featuresStr = gTargetMachine->getTargetFeatureString(); - llvm::SmallVector features; - featuresStr.split(features, ",", -1, false); - - std::string mabi = getABI(triple, features); - args.push_back("-mabi=" + mabi); - - // Returns true if 'feature' is enabled and false otherwise. Handles the - // case where the feature is specified multiple times ('+m,-m'), and - // takes the last occurrence. - auto hasFeature = [&features](llvm::StringRef feature) { - for (int i = features.size() - 1; i >= 0; i--) { - auto f = features[i]; - if (f.substr(1) == feature) { - return f[0] == '+'; - } - } - return false; - }; - - std::string march; - if (triple.isArch64Bit()) - march = "rv64"; - else - march = "rv32"; - bool m = hasFeature("m"); - bool a = hasFeature("a"); - bool f = hasFeature("f"); - bool d = hasFeature("d"); - bool c = hasFeature("c"); - bool g = false; - - if (m && a && f && d) { - march += "g"; - g = true; - } else { - march += "i"; - if (m) - march += "m"; - if (a) - march += "a"; - if (f) - march += "f"; - if (d) - march += "d"; - } - if (c) - march += "c"; - if (!g) - march += "_zicsr_zifencei"; - args.push_back("-march=" + march); + case Triple::riscv64: { + extern llvm::TargetMachine* gTargetMachine; + const auto featuresStr = gTargetMachine->getTargetFeatureString(); + llvm::SmallVector features; + featuresStr.split(features, ",", -1, false); + + const std::string mabi = getABI(triple, features); + args.push_back("-mabi=" + mabi); + + std::string march = triple.isArch64Bit() ? "rv64" : "rv32"; + const bool m = isFeatureEnabled(features, "m"); + const bool a = isFeatureEnabled(features, "a"); + const bool f = isFeatureEnabled(features, "f"); + const bool d = isFeatureEnabled(features, "d"); + const bool c = isFeatureEnabled(features, "c"); + bool g = false; + + if (m && a && f && d) { + march += "g"; + g = true; + } else { + march += "i"; + if (m) + march += "m"; + if (a) + march += "a"; + if (f) + march += "f"; + if (d) + march += "d"; } + if (c) + march += "c"; + if (!g) + march += "_zicsr_zifencei"; + args.push_back("-march=" + march); return; + } + default: break; } From cc8b4c5e7d4263d348b610331fcdcaaae65bc6aa Mon Sep 17 00:00:00 2001 From: Dennis Date: Thu, 13 Jul 2023 09:52:12 +0200 Subject: [PATCH 300/301] Fix 24044 - Support float opCmp(...) with array (dlang/dmd!15407) --- .../src/core/internal/array/comparison.d | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/src/core/internal/array/comparison.d b/runtime/druntime/src/core/internal/array/comparison.d index 821f96e25c0..94fa2433da8 100644 --- a/runtime/druntime/src/core/internal/array/comparison.d +++ b/runtime/druntime/src/core/internal/array/comparison.d @@ -83,7 +83,7 @@ int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted // This function is called by the compiler when dealing with array // comparisons in the semantic analysis phase of CmpExp. The ordering // comparison is lowered to a call to this template. -int __cmp(T1, T2)(T1[] s1, T2[] s2) +auto __cmp(T1, T2)(T1[] s1, T2[] s2) if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) { import core.internal.traits : Unqual; @@ -237,3 +237,26 @@ if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) auto vb = [cast(void[])b[0], b[1]]; assert(less2(va, vb)); } + +// custom aggregate types +@safe unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=24044 + // Support float opCmp(...) with array + static struct F + { + float f; + float opCmp(F other) const { return this.f - other.f; } + } + + F[2] a = [F(1.0f), F(float.nan)]; + F[2] b = [F(1.0f), F(1.0f)]; + F[1] c = [F(1.0f)]; + + bool isNan(float f) { return f != f; } + + assert(isNan(__cmp(a, b))); + assert(isNan(__cmp(a, a))); + assert(__cmp(b, b) == 0); + assert(__cmp(a, c) > 0); +} From b0af0b3e3674a6cd801b558de2b13c635be825d0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 26 Aug 2023 13:25:41 +0200 Subject: [PATCH 301/301] Finalize v1.34.0 changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b90c785e78e..34cf2a2f368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,20 @@ # LDC master +#### Big news + +#### Platform support + +#### Bug fixes + +# LDC 1.34.0 (2023-08-26) + #### Big news - Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) - Support for [LLVM 16](https://releases.llvm.org/16.0.0/docs/ReleaseNotes.html). The prebuilt packages use v16.0.6. (#4411, #4423) - We have come across miscompiles with LLVM 16's newly-enabled-by-default function specializations (on Win64 and macOS). To be on the safe side, LDC disables them by default for all targets via `-func-specialization-size-threshold=1000000000` in `etc/ldc2.conf` (and separately for LTO on Posix platforms). To enable the function specializations, explicitly override it with e.g. `-func-specialization-size-threshold=100` (the LLVM 16 default) and, for LTO on Posix, a similar LTO plugin option in the linker cmdline (see linker cmdline with `-v`). #### Platform support -- Supports LLVM 11.0 - 16.0. +- Supports LLVM 11.0 - 16.0. Support for LLVM 9 and 10 was dropped. - 64-bit RISC-V: Now defaults to `-mattr=+m,+a,+f,+d,+c` ('rv64gc' ABI) for non-bare-metal targets, i.e., if the target triple includes a valid operating system. (#4390) #### Bug fixes