diff options
101 files changed, 5812 insertions, 5674 deletions
diff --git a/gcc/d/d-ctfloat.cc b/gcc/d/d-ctfloat.cc index c4d9a44..a434af9 100644 --- a/gcc/d/d-ctfloat.cc +++ b/gcc/d/d-ctfloat.cc @@ -85,14 +85,13 @@ CTFloat::isInfinity (real_t r) /* Return a real_t value from string BUFFER rounded to long double mode. */ real_t -CTFloat::parse (const char *buffer, bool *overflow) +CTFloat::parse (const char *buffer, bool &overflow) { real_t r; real_from_string3 (&r.rv (), buffer, TYPE_MODE (long_double_type_node)); /* Front-end checks overflow to see if the value is representable. */ - if (overflow && r == target.RealProperties.infinity) - *overflow = true; + overflow = (r == target.RealProperties.infinity) ? true : false; return r; } diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 04147ed..dcc465f 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -1191,7 +1191,6 @@ d_parse_file (void) } /* Do deferred semantic analysis. */ - Module::dprogress = 1; Module::runDeferredSemantic (); if (Module::deferred.length) diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index c358b69..85fc49d 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -d7772a236983ec37b92d21b28bad3cd2de57b945 +817610b16d0f0f469b9fbb28c000956fb910c43f The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index 50c5ac3..1c7e1dd 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -18,10 +18,10 @@ this license for that file. | Folder | Purpose | |--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [dmd/](https://github.com/dlang/dmd/tree/master/src/dmd) | The dmd driver and front-end | -| [dmd/backend/](https://github.com/dlang/dmd/tree/master/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). | -| [dmd/common/](https://github.com/dlang/dmd/tree/master/src/dmd/common) | Code shared by the front-end and back-end | -| [dmd/root/](https://github.com/dlang/dmd/tree/master/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). | +| [dmd/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd) | The dmd driver and front-end | +| [dmd/backend/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). | +| [dmd/common/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/common) | Code shared by the front-end and back-end | +| [dmd/root/](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). | DMD has a mostly flat directory structure, so this section aims to divide all source files into logical groups for easier navigation. The groups are roughly ordered by how late they appear in the compilation process. @@ -31,26 +31,26 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------|-----------------------------------------------------------------------| -| [mars.d](https://github.com/dlang/dmd/blob/master/src/dmd/mars.d) | The entry point. Contains `main`. | -| [cli.d](https://github.com/dlang/dmd/blob/master/src/dmd/cli.d) | Define the command line interface | -| [dmdparams.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmdparams.d) | DMD-specific parameters | -| [globals.d](https://github.com/dlang/dmd/blob/master/src/dmd/globals.d) | Define a structure storing command line options | -| [dinifile.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) | -| [vsoptions.d](https://github.com/dlang/dmd/blob/master/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking | -| [frontend.d](https://github.com/dlang/dmd/blob/master/src/dmd/frontend.d) | An interface for using DMD as a library | -| [errors.d](https://github.com/dlang/dmd/blob/master/src/dmd/errors.d) | Error reporting functionality | -| [target.d](https://github.com/dlang/dmd/blob/master/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | -| [compiler.d](https://github.com/dlang/dmd/blob/master/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions | +| [mars.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d) | The entry point. Contains `main`. | +| [cli.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cli.d) | Define the command line interface | +| [dmdparams.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmdparams.d) | DMD-specific parameters | +| [globals.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/globals.d) | Define a structure storing command line options | +| [dinifile.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) | +| [vsoptions.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking | +| [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library | +| [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting functionality | +| [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | +| [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions | ### Lexing / parsing | File | Purpose | |-----------------------------------------------------------------------|----------------------------------------------------------------------| -| [lexer.d](https://github.com/dlang/dmd/blob/master/src/dmd/lexer.d) | Convert source code into tokens for the D and ImportC parsers | -| [entity.d](https://github.com/dlang/dmd/blob/master/src/dmd/entity.d) | Define "\\&Entity;" escape sequence for strings / character literals | -| [tokens.d](https://github.com/dlang/dmd/blob/master/src/dmd/tokens.d) | Define lexical tokens. | -| [parse.d](https://github.com/dlang/dmd/blob/master/src/dmd/parse.d) | D parser, converting tokens into an Abstract Syntax Tree (AST) | -| [cparse.d](https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d) | ImportC parser, converting tokens into an Abstract Syntax Tree (AST) | +| [lexer.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lexer.d) | Convert source code into tokens for the D and ImportC parsers | +| [entity.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/entity.d) | Define "\\&Entity;" escape sequence for strings / character literals | +| [tokens.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tokens.d) | Define lexical tokens. | +| [parse.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parse.d) | D parser, converting tokens into an Abstract Syntax Tree (AST) | +| [cparse.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cparse.d) | ImportC parser, converting tokens into an Abstract Syntax Tree (AST) | ### Semantic analysis @@ -58,88 +58,88 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| [dsymbol.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d) | Base class for a D symbol, e.g. a variable, function, module, enum etc. | -| [identifier.d](https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d) | Represents the name of a `Dsymbol` | -| [id.d](https://github.com/dlang/dmd/blob/master/src/dmd/id.d) | Define strings for pre-defined identifiers (e.g. `sizeof`, `string`) | -| [dscope.d](https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d) | Define a 'scope' on which symbol lookup can be performed | -| [dtemplate.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d) | A template declaration or instance | -| [dmodule.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d) | Define a package and module | -| [mtype.d](https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d) | Define expression types such as `int`, `char[]`, `void function()` | -| [arraytypes.d](https://github.com/dlang/dmd/blob/master/src/dmd/arraytypes.d) | For certain Declaration nodes of type `T`, provides aliases for `Array!T` | -| [declaration.d](https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d) | Misc. declarations of `alias`, variables, type tuples, `ClassInfo` etc. | -| [denum.d](https://github.com/dlang/dmd/blob/master/src/dmd/denum.d) | Defines `enum` declarations and enum members | -| [attrib.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | Declarations of 'attributes' such as `private`, `pragma()`, `immutable`, `@UDA`, `align`, `extern(C++)` and more | -| [func.d](https://github.com/dlang/dmd/blob/master/src/dmd/func.d) | Define a function declaration (includes function literals, `invariant`, `unittest`) | -| [dversion.d](https://github.com/dlang/dmd/blob/master/src/dmd/dversion.d) | Defines a version symbol, e.g. `version = ident`, `debug = ident` | +| [dsymbol.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbol.d) | Base class for a D symbol, e.g. a variable, function, module, enum etc. | +| [identifier.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/identifier.d) | Represents the name of a `Dsymbol` | +| [id.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/id.d) | Define strings for pre-defined identifiers (e.g. `sizeof`, `string`) | +| [dscope.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dscope.d) | Define a 'scope' on which symbol lookup can be performed | +| [dtemplate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtemplate.d) | A template declaration or instance | +| [dmodule.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmodule.d) | Define a package and module | +| [mtype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mtype.d) | Define expression types such as `int`, `char[]`, `void function()` | +| [arraytypes.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arraytypes.d) | For certain Declaration nodes of type `T`, provides aliases for `Array!T` | +| [declaration.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/declaration.d) | Misc. declarations of `alias`, variables, type tuples, `ClassInfo` etc. | +| [denum.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/denum.d) | Defines `enum` declarations and enum members | +| [attrib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nogc.d) | Declarations of 'attributes' such as `private`, `pragma()`, `immutable`, `@UDA`, `align`, `extern(C++)` and more | +| [func.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/func.d) | Define a function declaration (includes function literals, `invariant`, `unittest`) | +| [dversion.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dversion.d) | Defines a version symbol, e.g. `version = ident`, `debug = ident` | **AST nodes** | File | Purpose | |-----------------------------------------------------------------------------------|-------------------------------------------------------------| -| [ast_node.d](https://github.com/dlang/dmd/blob/master/src/dmd/ast_node.d) | Define an abstract AST node class | -| [astbase.d](https://github.com/dlang/dmd/blob/master/src/dmd/astbase.d) | Namespace of AST nodes that can be produced by the parser | -| [astcodegen.d](https://github.com/dlang/dmd/blob/master/src/dmd/astcodegen.d) | Namespace of AST nodes of a AST ready for code generation | -| [astenums.d](https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d) | Enums common to DMD and AST | -| [expression.d](https://github.com/dlang/dmd/blob/master/src/dmd/expression.d) | Define expression AST nodes | -| [statement.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement.d) | Define statement AST nodes | -| [staticassert.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.d) | Define a `static assert` AST node | -| [aggregate.d](https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d) | Define an aggregate (`struct`, `union` or `class`) AST node | -| [dclass.d](https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d) | Define a `class` AST node | -| [dstruct.d](https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d) | Define a `struct` or `union` AST node | -| [init.d](https://github.com/dlang/dmd/blob/master/src/dmd/init.d) | Define variable initializers | +| [ast_node.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ast_node.d) | Define an abstract AST node class | +| [astbase.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astbase.d) | Namespace of AST nodes that can be produced by the parser | +| [astcodegen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astcodegen.d) | Namespace of AST nodes of a AST ready for code generation | +| [astenums.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astenums.d) | Enums common to DMD and AST | +| [expression.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expression.d) | Define expression AST nodes | +| [statement.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement.d) | Define statement AST nodes | +| [staticassert.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticassert.d) | Define a `static assert` AST node | +| [aggregate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aggregate.d) | Define an aggregate (`struct`, `union` or `class`) AST node | +| [dclass.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dclass.d) | Define a `class` AST node | +| [dstruct.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dstruct.d) | Define a `struct` or `union` AST node | +| [init.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/init.d) | Define variable initializers | **AST visitors** | File | Purpose | |-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| -| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | -| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | -| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node | -| [visitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler | -| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST | -| [apply.d](https://github.com/dlang/dmd/blob/master/src/dmd/apply.d) | Depth-first expression visitor | -| [sapply.d](https://github.com/dlang/dmd/blob/master/src/dmd/sapply.d) | Depth-first statement visitor | -| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | +| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | +| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node | +| [visitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler | +| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST | +| [apply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/apply.d) | Depth-first expression visitor | +| [sapply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sapply.d) | Depth-first statement visitor | +| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | **Semantic passes** | File | Purpose | |-------------------------------------------------------------------------------------------|-------------------------------------------------------------------| -| [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) | -| [semantic2.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic2.d) | Do semantic 2 pass (symbol initializers) | -| [semantic3.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d) | Do semantic 3 pass (function bodies) | -| [inline.d](https://github.com/dlang/dmd/blob/master/src/dmd/inline.d) | Do inline pass (optimization pass that dmd does in the front-end) | -| [inlinecost.d](https://github.com/dlang/dmd/blob/master/src/dmd/inlinecost.d) | Compute the cost of inlining a function call. | -| [expressionsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d) | Do semantic analysis for expressions | -| [statementsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d) | Do semantic analysis for statements | -| [initsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d) | Do semantic analysis for initializers | -| [templateparamsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters | -| [typesem.d](https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d) | Do semantic analysis for types | +| [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) | +| [semantic2.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic2.d) | Do semantic 2 pass (symbol initializers) | +| [semantic3.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic3.d) | Do semantic 3 pass (function bodies) | +| [inline.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inline.d) | Do inline pass (optimization pass that dmd does in the front-end) | +| [inlinecost.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inlinecost.d) | Compute the cost of inlining a function call. | +| [expressionsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expressionsem.d) | Do semantic analysis for expressions | +| [statementsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statementsem.d) | Do semantic analysis for statements | +| [initsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/initsem.d) | Do semantic analysis for initializers | +| [templateparamsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters | +| [typesem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typesem.d) | Do semantic analysis for types | **Semantic helpers** | File | Purpose | |-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| [opover.d](https://github.com/dlang/dmd/blob/master/src/dmd/opover.d) | Operator overloading | -| [clone.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Generate automatic `opEquals`, `opAssign` and constructors for structs | -| [blockexit.d](https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d) | Find out in what ways control flow can exit a block | -| [ctorflow.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d) | Control flow in constructors | -| [constfold.d](https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d) | Do constant folding of arithmetic expressions | -| [optimize.d](https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d) | Do constant folding more generally | -| [dcast.d](https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions | -| [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types | -| [importc.d](https://github.com/dlang/dmd/blob/master/src/dmd/importc.d) | Helpers specific to ImportC | -| [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. | -| [mustuse.d](https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d) | Helpers related to the `@mustuse` attribute | +| [opover.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/opover.d) | Operator overloading | +| [clone.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d) | Generate automatic `opEquals`, `opAssign` and constructors for structs | +| [blockexit.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/blockexit.d) | Find out in what ways control flow can exit a block | +| [ctorflow.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctorflow.d) | Control flow in constructors | +| [constfold.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/constfold.d) | Do constant folding of arithmetic expressions | +| [optimize.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/optimize.d) | Do constant folding more generally | +| [dcast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions | +| [impcnvtab.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types | +| [importc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/importc.d) | Helpers specific to ImportC | +| [sideeffect.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. | +| [mustuse.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mustuse.d) | Helpers related to the `@mustuse` attribute | **Compile Time Function Execution (CTFE)** | File | Purpose | |-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| -| [dinterpret.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d) | CTFE entry point | -| [ctfeexpr.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d) | CTFE for expressions involving pointers, slices, array concatenation etc. | -| [builtin.d](https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d) | Allow CTFE of certain external functions (`core.math`, `std.math` and `core.bitop`) | +| [dinterpret.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinterpret.d) | CTFE entry point | +| [ctfeexpr.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctfeexpr.d) | CTFE for expressions involving pointers, slices, array concatenation etc. | +| [builtin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/builtin.d) | Allow CTFE of certain external functions (`core.math`, `std.math` and `core.bitop`) | ### Specific language features @@ -147,116 +147,116 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |---------------------------------------------------------------------------|----------------------------------------| -| [nogc.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | `@nogc` checks | -| [safe.d](https://github.com/dlang/dmd/blob/master/src/dmd/safe.d) | `@safe` checks | -| [canthrow.d](https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d) | `nothrow` checks | -| [escape.d](https://github.com/dlang/dmd/blob/master/src/dmd/escape.d) | `scope` checks | -| [access.d](https://github.com/dlang/dmd/blob/master/src/dmd/access.d) | `public` / `private` checks | -| [ob.d](https://github.com/dlang/dmd/blob/master/src/dmd/ob.d) | Ownership / borrowing (`@live`) checks | +| [nogc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nogc.d) | `@nogc` checks | +| [safe.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/safe.d) | `@safe` checks | +| [canthrow.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/canthrow.d) | `nothrow` checks | +| [escape.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/escape.d) | `scope` checks | +| [access.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/access.d) | `public` / `private` checks | +| [ob.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ob.d) | Ownership / borrowing (`@live`) checks | **Inline Assembly** | File | Purpose | |-------------------------------------------------------------------------|-------------------------------------------| -| [iasm.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d) | Inline assembly depending on the compiler | -| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmdmd.d) | Inline assembly for DMD | -| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d) | Inline assembly for GDC | +| [iasm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm.d) | Inline assembly depending on the compiler | +| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmdmd.d) | Inline assembly for DMD | +| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d) | Inline assembly for GDC | **Other** | File | Purpose | |--------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| -| [aliasthis.d](https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d) | Resolve implicit conversions for `alias X this` | -| [traits.d](https://github.com/dlang/dmd/blob/master/src/dmd/traits.d) | `__traits()` | -| [lambdacomp.d](https://github.com/dlang/dmd/blob/master/src/dmd/lambdacomp.d) | `__traits(isSame, x => y, z => w)` | -| [cond.d](https://github.com/dlang/dmd/blob/master/src/dmd/cond.d) | Evaluate `static if`, `version` `debug ` | -| [staticcond.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d) | Lazily evaluate static conditions for `static if`, `static assert` and template constraints | -| [delegatize.d](https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d) | Converts expression to delegates for `lazy` parameters | -| [eh.d](https://github.com/dlang/dmd/blob/master/src/dmd/eh.d) | Generate tables for exception handling | -| [nspace.d](https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d) | Namespace for `extern (C++, Module)` | -| [intrange.d](https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d) | [Value range propagation](https://digitalmars.com/articles/b62.html) | -| [dimport.d](https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d) | Renamed imports (`import aliasSymbol = pkg1.pkg2.symbol`) | -| [arrayop.d](https://github.com/dlang/dmd/blob/master/src/dmd/arrayop.d) | Array operations (`a[] = b[] + c[]`) | -| [cpreprocess.d](https://github.com/dlang/dmd/blob/master/src/dmd/cpreprocess.d)| Run the C preprocessor on C source files | -| [typinf.d](https://github.com/dlang/dmd/blob/master/src/dmd/typinf.d) | Generate typeinfo for `typeid()` (as well as internals) | +| [aliasthis.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aliasthis.d) | Resolve implicit conversions for `alias X this` | +| [traits.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/traits.d) | `__traits()` | +| [lambdacomp.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lambdacomp.d) | `__traits(isSame, x => y, z => w)` | +| [cond.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cond.d) | Evaluate `static if`, `version` `debug ` | +| [staticcond.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticcond.d) | Lazily evaluate static conditions for `static if`, `static assert` and template constraints | +| [delegatize.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/delegatize.d) | Converts expression to delegates for `lazy` parameters | +| [eh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/eh.d) | Generate tables for exception handling | +| [nspace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nspace.d) | Namespace for `extern (C++, Module)` | +| [intrange.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/intrange.d) | [Value range propagation](https://digitalmars.com/articles/b62.html) | +| [dimport.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dimport.d) | Renamed imports (`import aliasSymbol = pkg1.pkg2.symbol`) | +| [arrayop.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arrayop.d) | Array operations (`a[] = b[] + c[]`) | +| [cpreprocess.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cpreprocess.d)| Run the C preprocessor on C source files | +| [typinf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typinf.d) | Generate typeinfo for `typeid()` (as well as internals) | | File | Purpose | |-----------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| [chkformat.d](https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d) | Validate arguments with format specifiers for `printf` / `scanf` etc. | -| [imphint.d](https://github.com/dlang/dmd/blob/master/src/dmd/imphint.d) | Give a suggestion to e.g. `import std.stdio` when `writeln` could not be resolved. | +| [chkformat.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/chkformat.d) | Validate arguments with format specifiers for `printf` / `scanf` etc. | +| [imphint.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/imphint.d) | Give a suggestion to e.g. `import std.stdio` when `writeln` could not be resolved. | ### Library files | File | Purpose | |-------------------------------------------------------------------------------|------------------------------------------------------| -| [lib.d](https://github.com/dlang/dmd/blob/master/src/dmd/lib.d) | Abstract library class | -| [libelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libelf.d) | Library in ELF format (Unix) | -| [libmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmach.d) | Library in Mach-O format (macOS) | -| [libmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) | -| [libomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libomf.d) | Library in OMF format (legacy 32-bit Windows) | -| [scanelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format | -| [scanmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format | -| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format | -| [scanomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanomf.d) | Extract symbol names from a library in OMF format | +| [lib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib.d) | Abstract library class | +| [libelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libelf.d) | Library in ELF format (Unix) | +| [libmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmach.d) | Library in Mach-O format (macOS) | +| [libmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) | +| [libomf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libomf.d) | Library in OMF format (legacy 32-bit Windows) | +| [scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format | +| [scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format | +| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format | +| [scanomf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanomf.d) | Extract symbol names from a library in OMF format | ### Code generation / back-end interfacing | File | Purpose | |---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| -| [dmsc.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmsc.d) | Configures and initializes the back-end | -| [toobj.d](https://github.com/dlang/dmd/blob/master/src/dmd/toobj.d) | Convert an AST that went through all semantic phases into an object file | -| [toir.d](https://github.com/dlang/dmd/blob/master/src/dmd/toir.d) | Convert Dsymbols intermediate representation | -| [e2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/e2ir.d) | Convert Expressions to intermediate representation | -| [s2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/s2ir.d) | Convert Statements to intermediate representation | -| [stmtstate.d](https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph | -| [toctype.d](https://github.com/dlang/dmd/blob/master/src/dmd/toctype.d) | Convert a D type to a type the back-end understands | -| [tocsym.d](https://github.com/dlang/dmd/blob/master/src/dmd/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) | -| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI | -| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI | -| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI | -| [glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/glue.d) | Generate the object file for function declarations | -| [gluelayer.d](https://github.com/dlang/dmd/blob/master/src/dmd/gluelayer.d) | Declarations for back-end functions that the front-end invokes | -| [todt.d](https://github.com/dlang/dmd/blob/master/src/dmd/todt.d) | Convert initializers into structures that the back-end will add to the data segment | -| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/src/dmd/tovcdebug.d) | Generate debug info in the CV4 debug format. | -| [objc.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc.d) | Objective-C interfacing | -| [objc_glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d) | Glue code for Objective-C interop. | +| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d) | Configures and initializes the back-end | +| [toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toobj.d) | Convert an AST that went through all semantic phases into an object file | +| [toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toir.d) | Convert Dsymbols intermediate representation | +| [e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/e2ir.d) | Convert Expressions to intermediate representation | +| [s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/s2ir.d) | Convert Statements to intermediate representation | +| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph | +| [toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toctype.d) | Convert a D type to a type the back-end understands | +| [tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) | +| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI | +| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI | +| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI | +| [glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/glue.d) | Generate the object file for function declarations | +| [gluelayer.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d) | Declarations for back-end functions that the front-end invokes | +| [todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/todt.d) | Convert initializers into structures that the back-end will add to the data segment | +| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tovcdebug.d) | Generate debug info in the CV4 debug format. | +| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d) | Objective-C interfacing | +| [objc_glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc_glue.d) | Glue code for Objective-C interop. | **Name mangling** | File | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------------------------| -| [cppmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d) | C++ name mangling | -| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | -| [dmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | +| [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling | +| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | +| [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | ### Linking | File | Purpose | |-------------------------------------------------------------------|-----------------------------------------| -| [link.d](https://github.com/dlang/dmd/blob/master/src/dmd/link.d) | Invoke the linker as a separate process | +| [link.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/link.d) | Invoke the linker as a separate process | ### Special output | File | Purpose | |-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| -| [doc.d](https://github.com/dlang/dmd/blob/master/src/dmd/doc.d) | [Documentation generation](https://dlang.org/spec/ddoc.html) | -| [dmacro.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d) | DDoc macro processing | -| [hdrgen.d](https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages | -| [json.d](https://github.com/dlang/dmd/blob/master/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag | -| [dtoh.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtoh.d) | C++ header generation from D source files | +| [doc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/doc.d) | [Documentation generation](https://dlang.org/spec/ddoc.html) | +| [dmacro.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmacro.d) | DDoc macro processing | +| [hdrgen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages | +| [json.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag | +| [dtoh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d) | C++ header generation from D source files | ### Utility -Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/src/dmd/root). +Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/compiler/src/dmd/root). | File | Purpose | |-----------------------------------------------------------------------------------|---------------------------------------------------| -| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color | -| [file_manager.d](https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d) | Keep file contents in memory | -| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths | +| [console.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/console.d) | Print error messages in color | +| [file_manager.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/file_manager.d) | Keep file contents in memory | +| [utils.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/utils.d) | Utility functions related to files and file paths | | File | Purpose | |---------------------------------------------------------------------------------|---------------------------------------------------------------| -| [asttypename.d](https://github.com/dlang/dmd/blob/master/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) | -| [printast.d](https://github.com/dlang/dmd/blob/master/src/dmd/printast.d) | Print the AST data structure | -| [foreachvar.d](https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | +| [asttypename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) | +| [printast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d) | Print the AST data structure | +| [foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d index bf19693..091e96a 100644 --- a/gcc/d/dmd/astenums.d +++ b/gcc/d/dmd/astenums.d @@ -68,7 +68,6 @@ enum STC : ulong // transfer changes to declaration.h ref_ = 0x4_0000, /// `ref` scope_ = 0x8_0000, /// `scope` - maybescope = 0x10_0000, /// parameter might be `scope` scopeinferred = 0x20_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set return_ = 0x40_0000, /// 'return ref' or 'return scope' for function parameters returnScope = 0x80_0000, /// if `ref return scope` then resolve to `ref` and `return scope` diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index fe6e1e3..088ca61 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -114,8 +114,10 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN import dmd.id : Id; auto sd = ts.sym; + const id = ce.f.ident; if (sd.postblit && - (ce.f.ident == Id._d_arrayctor || ce.f.ident == Id._d_arraysetctor)) + (id == Id._d_arrayctor || id == Id._d_arraysetctor || + id == Id._d_arrayassign_l || id == Id._d_arrayassign_r)) { checkFuncThrows(ce, sd.postblit); return; diff --git a/gcc/d/dmd/common/README.md b/gcc/d/dmd/common/README.md index fb282dc..853fd4f 100644 --- a/gcc/d/dmd/common/README.md +++ b/gcc/d/dmd/common/README.md @@ -2,7 +2,7 @@ | File | Purpose | |------------------------------------------------------------------------------------|-----------------------------------------------------------------| -| [bitfields.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d) | Pack multiple boolean fields into bit fields | -| [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d) | Functions and objects dedicated to file I/O and management | -| [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.d) | An expandable buffer in which you can write text or binary data | -| [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d) | Common string functions including filename manipulation | +| [bitfields.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/bitfields.d) | Pack multiple boolean fields into bit fields | +| [file.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/file.d) | Functions and objects dedicated to file I/O and management | +| [outbuffer.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/outbuffer.d) | An expandable buffer in which you can write text or binary data | +| [string.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/string.d) | Common string functions including filename manipulation | diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index 9a5bd82..9d544a4 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -109,12 +109,12 @@ struct OutBuffer } /// For porting with ease from dmd.backend.outbuf.Outbuffer - ubyte* buf() nothrow { + ubyte* buf() nothrow @system { return data.ptr; } /// For porting with ease from dmd.backend.outbuf.Outbuffer - ubyte** bufptr() nothrow { + ubyte** bufptr() nothrow @system { static struct Array { size_t length; ubyte* ptr; } auto a = cast(Array*) &data; assert(a.length == data.length && a.ptr == data.ptr); @@ -156,7 +156,7 @@ struct OutBuffer Params: nbytes = the number of additional bytes to reserve */ - extern (C++) void reserve(size_t nbytes) pure nothrow + extern (C++) void reserve(size_t nbytes) pure nothrow @trusted { //debug (stomp) printf("OutBuffer::reserve: size = %lld, offset = %lld, nbytes = %lld\n", data.length, offset, nbytes); const minSize = offset + nbytes; @@ -210,7 +210,7 @@ struct OutBuffer offset = 0; } - private void indent() pure nothrow + private void indent() pure nothrow @safe { if (level) { @@ -223,19 +223,19 @@ struct OutBuffer } // Write an array to the buffer, no reserve check - @trusted nothrow + @system nothrow void writen(const void *b, size_t len) { memcpy(data.ptr + offset, b, len); offset += len; } - extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow + extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow @system { write(data[0 .. nbytes]); } - void write(const(void)[] buf) pure nothrow + void write(scope const(void)[] buf) pure nothrow @trusted { if (doindent && !notlinehead) indent(); @@ -282,7 +282,7 @@ struct OutBuffer } /// NOT zero-terminated - extern (C++) void writestring(const(char)* s) pure nothrow + extern (C++) void writestring(const(char)* s) pure nothrow @system { if (!s) return; @@ -291,19 +291,19 @@ struct OutBuffer } /// ditto - void writestring(const(char)[] s) pure nothrow + void writestring(scope const(char)[] s) pure nothrow @safe { write(s); } /// ditto - void writestring(string s) pure nothrow + void writestring(scope string s) pure nothrow @safe { write(s); } /// NOT zero-terminated, followed by newline - void writestringln(const(char)[] s) pure nothrow + void writestringln(const(char)[] s) pure nothrow @safe { writestring(s); writenl(); @@ -311,25 +311,25 @@ struct OutBuffer /** Write string to buffer, ensure it is zero terminated */ - void writeStringz(const(char)* s) pure nothrow @trusted + void writeStringz(const(char)* s) pure nothrow @system { write(s[0 .. strlen(s)+1]); } /// ditto - void writeStringz(const(char)[] s) pure nothrow + void writeStringz(const(char)[] s) pure nothrow @safe { write(s); writeByte(0); } /// ditto - void writeStringz(string s) pure nothrow + void writeStringz(string s) pure nothrow @safe { writeStringz(cast(const(char)[])(s)); } - extern (C++) void prependstring(const(char)* string) pure nothrow + extern (C++) void prependstring(const(char)* string) pure nothrow @system { size_t len = strlen(string); reserve(len); @@ -339,7 +339,7 @@ struct OutBuffer } /// write newline - extern (C++) void writenl() pure nothrow + extern (C++) void writenl() pure nothrow @safe { version (Windows) { @@ -385,7 +385,7 @@ struct OutBuffer this.data[offset++] = cast(ubyte) b; } - extern (C++) void writeByte(uint b) pure nothrow + extern (C++) void writeByte(uint b) pure nothrow @safe { if (doindent && !notlinehead && b != '\n') indent(); @@ -394,7 +394,7 @@ struct OutBuffer offset++; } - extern (C++) void writeUTF8(uint b) pure nothrow + extern (C++) void writeUTF8(uint b) pure nothrow @safe { reserve(6); if (b <= 0x7F) @@ -427,7 +427,7 @@ struct OutBuffer assert(0); } - extern (C++) void prependbyte(uint b) pure nothrow + extern (C++) void prependbyte(uint b) pure nothrow @trusted { reserve(1); memmove(data.ptr + 1, data.ptr, offset); @@ -435,7 +435,7 @@ struct OutBuffer offset++; } - extern (C++) void writewchar(uint w) pure nothrow + extern (C++) void writewchar(uint w) pure nothrow @safe { version (Windows) { @@ -447,7 +447,7 @@ struct OutBuffer } } - extern (C++) void writeword(uint w) pure nothrow + extern (C++) void writeword(uint w) pure nothrow @trusted { version (Windows) { @@ -465,7 +465,7 @@ struct OutBuffer offset += 2; } - extern (C++) void writeUTF16(uint w) pure nothrow + extern (C++) void writeUTF16(uint w) pure nothrow @trusted { reserve(4); if (w <= 0xFFFF) @@ -483,7 +483,7 @@ struct OutBuffer assert(0); } - extern (C++) void write4(uint w) pure nothrow + extern (C++) void write4(uint w) pure nothrow @trusted { version (Windows) { @@ -500,7 +500,7 @@ struct OutBuffer offset += 4; } - extern (C++) void write(const OutBuffer* buf) pure nothrow + extern (C++) void write(const OutBuffer* buf) pure nothrow @trusted { if (buf) { @@ -510,7 +510,7 @@ struct OutBuffer } } - extern (C++) void fill0(size_t nbytes) pure nothrow + extern (C++) void fill0(size_t nbytes) pure nothrow @trusted { reserve(nbytes); memset(data.ptr + offset, 0, nbytes); @@ -531,7 +531,7 @@ struct OutBuffer return cast(char[])data[offset - nbytes .. offset]; } - extern (C++) void vprintf(const(char)* format, va_list args) nothrow + extern (C++) void vprintf(const(char)* format, va_list args) nothrow @system { int count; if (doindent && !notlinehead) @@ -567,7 +567,7 @@ struct OutBuffer static if (__VERSION__ < 2092) { - extern (C++) void printf(const(char)* format, ...) nothrow + extern (C++) void printf(const(char)* format, ...) nothrow @system { va_list ap; va_start(ap, format); @@ -577,7 +577,7 @@ struct OutBuffer } else { - pragma(printf) extern (C++) void printf(const(char)* format, ...) nothrow + pragma(printf) extern (C++) void printf(const(char)* format, ...) nothrow @system { va_list ap; va_start(ap, format); @@ -591,13 +591,13 @@ struct OutBuffer * Params: * u = integral value to append */ - extern (C++) void print(ulong u) pure nothrow + extern (C++) void print(ulong u) pure nothrow @safe { UnsignedStringBuf buf = void; writestring(unsignedToTempString(u, buf)); } - extern (C++) void bracket(char left, char right) pure nothrow + extern (C++) void bracket(char left, char right) pure nothrow @trusted { reserve(2); memmove(data.ptr + 1, data.ptr, offset); @@ -610,7 +610,7 @@ struct OutBuffer * Insert left at i, and right at j. * Return index just past right. */ - extern (C++) size_t bracket(size_t i, const(char)* left, size_t j, const(char)* right) pure nothrow + extern (C++) size_t bracket(size_t i, const(char)* left, size_t j, const(char)* right) pure nothrow @system { size_t leftlen = strlen(left); size_t rightlen = strlen(right); @@ -620,7 +620,7 @@ struct OutBuffer return j + leftlen + rightlen; } - extern (C++) void spread(size_t offset, size_t nbytes) pure nothrow + extern (C++) void spread(size_t offset, size_t nbytes) pure nothrow @system { reserve(nbytes); memmove(data.ptr + offset + nbytes, data.ptr + offset, this.offset - offset); @@ -630,19 +630,19 @@ struct OutBuffer /**************************************** * Returns: offset + nbytes */ - extern (C++) size_t insert(size_t offset, const(void)* p, size_t nbytes) pure nothrow + extern (C++) size_t insert(size_t offset, const(void)* p, size_t nbytes) pure nothrow @system { spread(offset, nbytes); memmove(data.ptr + offset, p, nbytes); return offset + nbytes; } - size_t insert(size_t offset, const(char)[] s) pure nothrow + size_t insert(size_t offset, const(char)[] s) pure nothrow @system { return insert(offset, s.ptr, s.length); } - extern (C++) void remove(size_t offset, size_t nbytes) pure nothrow @nogc + extern (C++) void remove(size_t offset, size_t nbytes) pure nothrow @nogc @system { memmove(data.ptr + offset, data.ptr + offset + nbytes, this.offset - (offset + nbytes)); this.offset -= nbytes; @@ -716,7 +716,7 @@ struct OutBuffer return extractData(); } - void writesLEB128(int value) pure nothrow + void writesLEB128(int value) pure nothrow @safe { while (1) { @@ -733,7 +733,7 @@ struct OutBuffer } } - void writeuLEB128(uint value) pure nothrow + void writeuLEB128(uint value) pure nothrow @safe { do { @@ -758,7 +758,7 @@ struct OutBuffer Returns: `true` iff the operation succeeded. */ - extern(D) bool moveToFile(const char* filename) + extern(D) bool moveToFile(const char* filename) @system { bool result = true; const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; @@ -799,7 +799,7 @@ private: alias UnsignedStringBuf = char[20]; -char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure nothrow @nogc +char[] unsignedToTempString(ulong value, return scope char[] buf, uint radix = 10) @safe pure nothrow @nogc { size_t i = buf.length; do diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 11229d4..6243e74 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -357,15 +357,31 @@ UnionExp copyLiteral(Expression e) r.origin = sle.origin; return ue; } - if (e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.symbolOffset || e.op == EXP.null_ || e.op == EXP.variable || e.op == EXP.dotVariable || e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.char_ || e.op == EXP.complex80 || e.op == EXP.void_ || e.op == EXP.vector || e.op == EXP.typeid_) - { + + switch(e.op) + { + case EXP.function_: + case EXP.delegate_: + case EXP.symbolOffset: + case EXP.null_: + case EXP.variable: + case EXP.dotVariable: + case EXP.int64: + case EXP.float64: + case EXP.char_: + case EXP.complex80: + case EXP.void_: + case EXP.vector: + case EXP.typeid_: // Simple value types // Keep e1 for DelegateExp and DotVarExp emplaceExp!(UnionExp)(&ue, e); Expression r = ue.exp(); r.type = e.type; return ue; + default: break; } + if (auto se = e.isSliceExp()) { if (se.type.toBasetype().ty == Tsarray) diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index ffb33d3..ff27e37 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -563,8 +563,9 @@ extern (C++) abstract class Declaration : Dsymbol extern (C++) final class TupleDeclaration : Declaration { Objects* objects; - bool isexp; // true: expression tuple TypeTuple tupletype; // !=null if this is a type tuple + bool isexp; // true: expression tuple + bool building; // it's growing in AliasAssign semantic extern (D) this(const ref Loc loc, Identifier ident, Objects* objects) { @@ -588,7 +589,7 @@ extern (C++) final class TupleDeclaration : Declaration */ //printf("TupleDeclaration::getType() %s\n", toChars()); - if (isexp) + if (isexp || building) return null; if (!tupletype) { @@ -931,6 +932,19 @@ extern (C++) final class AliasDeclaration : Declaration } else { + // stop AliasAssign tuple building + if (aliassym) + { + if (auto td = aliassym.isTupleDeclaration()) + { + if (td.building) + { + td.building = false; + semanticRun = PASS.semanticdone; + return td; + } + } + } if (_import && _import._scope) { /* If this is an internal alias for selective/renamed import, @@ -1076,7 +1090,7 @@ extern (C++) class VarDeclaration : Declaration VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range - VarDeclarations* maybes; // STC.maybescope variables that are assigned to this STC.maybescope variable + VarDeclarations* maybes; // maybeScope variables that are assigned to this maybeScope variable uint endlinnum; // line number of end of scope that this var lives in uint offset; @@ -1105,7 +1119,7 @@ extern (C++) class VarDeclaration : Declaration bool overlapped; /// if it is a field and has overlapping bool overlapUnsafe; /// if it is an overlapping field and the overlaps are unsafe - bool doNotInferScope; /// do not infer 'scope' for this variable + bool maybeScope; /// allow inferring 'scope' for this variable bool doNotInferReturn; /// do not infer 'return' for this variable bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index c8f6c2a..bc8db44 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -59,7 +59,6 @@ struct AttributeViolation; #define STCref 0x40000ULL /// `ref` #define STCscope 0x80000ULL /// `scope` - #define STCmaybescope 0x100000ULL /// parameter might be `scope` #define STCscopeinferred 0x200000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set #define STCreturn 0x400000ULL /// 'return ref' or 'return scope' for function parameters #define STCreturnScope 0x800000ULL /// if `ref return scope` then resolve to `ref` and `return scope` @@ -166,9 +165,9 @@ class TupleDeclaration final : public Declaration { public: Objects *objects; - bool isexp; // true: expression tuple - TypeTuple *tupletype; // !=NULL if this is a type tuple + bool isexp; // true: expression tuple + bool building; // it's growing in AliasAssign semantic TupleDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; @@ -264,8 +263,8 @@ public: bool overlapped(bool v); bool overlapUnsafe() const; // if it is an overlapping field and the overlaps are unsafe bool overlapUnsafe(bool v); - bool doNotInferScope() const; // do not infer 'scope' for this variable - bool doNotInferScope(bool v); + bool maybeScope() const; // allow inferring 'scope' for this variable + bool maybeScope(bool v); bool doNotInferReturn() const; // do not infer 'return' for this variable bool doNotInferReturn(bool v); bool isArgDtorVar() const; // temporary created to handle scope destruction of a function argument diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index 156cce2..5cc3772 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -223,7 +223,16 @@ extern (C++) final class Import : Dsymbol override void importAll(Scope* sc) { if (mod) return; // Already done - load(sc); + + /* + * https://issues.dlang.org/show_bug.cgi?id=15525 + * + * Loading the import has failed, + * most likely because of parsing errors. + * Therefore we cannot trust the resulting AST. + */ + if (load(sc)) return; + if (!mod) return; // Failed if (sc.stc & STC.static_) diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 890c3b6..a9fd0f5 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -4778,6 +4778,12 @@ public: // If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it. removeHookTraceImpl(e, fd); + bool isArrayConstructionOrAssign(FuncDeclaration fd) + { + return fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor || + fd.ident == Id._d_arrayassign_l || fd.ident == Id._d_arrayassign_r; + } + if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor) { assert(e.arguments.dim == 1); @@ -4831,27 +4837,36 @@ public: result = interpretRegion(ae, istate); return; } - else if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor) + else if (isArrayConstructionOrAssign(fd)) { - // 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_arraysetctor) - assert(e.arguments.dim == 2); - else + // In expressionsem.d, the following lowerings were performed: + // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`. + // * `ea = eb` (ea and eb are arrays) to `_d_arrayassign_{l,r}(ea[], eb[])`. + // The following code will rewrite them back to `ea = eb` and + // then interpret that expression. + + if (fd.ident == Id._d_arrayctor) assert(e.arguments.dim == 3); + else + assert(e.arguments.dim == 2); Expression ea = (*e.arguments)[0]; if (ea.isCastExp) ea = ea.isCastExp.e1; Expression eb = (*e.arguments)[1]; - if (eb.isCastExp && fd.ident == Id._d_arrayctor) + if (eb.isCastExp() && fd.ident != Id._d_arraysetctor) eb = eb.isCastExp.e1; - ConstructExp ce = new ConstructExp(e.loc, ea, eb); - ce.type = ea.type; + 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); + + rewrittenExp.type = ea.type; + result = interpret(rewrittenExp, istate); - result = interpret(ce, istate); return; } else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace) diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d index 9beac6b..358712b 100644 --- a/gcc/d/dmd/dmacro.d +++ b/gcc/d/dmd/dmacro.d @@ -14,8 +14,6 @@ module dmd.dmacro; import core.stdc.ctype; import core.stdc.string; import dmd.doc; -import dmd.errors; -import dmd.globals; import dmd.common.outbuffer; import dmd.root.rmem; @@ -28,7 +26,7 @@ extern (C++) struct MacroTable * name = name of macro * text = text of macro */ - extern (D) void define(const(char)[] name, const(char)[] text) + extern (D) void define(const(char)[] name, const(char)[] text) nothrow pure @safe { //printf("MacroTable::define('%.*s' = '%.*s')\n", cast(int)name.length, name.ptr, text.length, text.ptr); if (auto table = name in mactab) @@ -42,8 +40,10 @@ extern (C++) struct MacroTable /***************************************************** * Look for macros in buf and expand them in place. * Only look at the text in buf from start to pend. + * + * Returns: `true` on success, `false` when the recursion limit was reached */ - extern (D) void expand(ref OutBuffer buf, size_t start, ref size_t pend, const(char)[] arg) + extern (D) bool expand(ref OutBuffer buf, size_t start, ref size_t pend, const(char)[] arg, int recursionLimit) nothrow pure { version (none) { @@ -51,14 +51,10 @@ extern (C++) struct MacroTable printf("Buf is: '%.*s'\n", cast(int)(pend - start), buf.data + start); } // limit recursive expansion - __gshared int nest; - if (nest > global.recursionLimit) - { - error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", - global.recursionLimit); - return; - } - nest++; + recursionLimit--; + if (recursionLimit < 0) + return false; + size_t end = pend; assert(start <= end); assert(end <= buf.length); @@ -105,7 +101,9 @@ extern (C++) struct MacroTable end += marg.length - 2; // Scan replaced text for further expansion size_t mend = u + marg.length; - expand(buf, u, mend, null); + const success = expand(buf, u, mend, null, recursionLimit); + if (!success) + return false; end += mend - (u + marg.length); u = mend; } @@ -121,7 +119,9 @@ extern (C++) struct MacroTable end += -2 + 2 + marg.length + 2; // Scan replaced text for further expansion size_t mend = u + 2 + marg.length; - expand(buf, u + 2, mend, null); + const success = expand(buf, u + 2, mend, null, recursionLimit); + if (!success) + return false; end += mend - (u + 2 + marg.length); u = mend; } @@ -228,7 +228,9 @@ extern (C++) struct MacroTable // Scan replaced text for further expansion m.inuse++; size_t mend = v + 1 + 2 + m.text.length + 2; - expand(buf, v + 1, mend, marg); + const success = expand(buf, v + 1, mend, marg, recursionLimit); + if (!success) + return false; end += mend - (v + 1 + 2 + m.text.length + 2); m.inuse--; buf.remove(u, v + 1 - u); @@ -253,12 +255,12 @@ extern (C++) struct MacroTable } mem.xfree(cast(char*)arg); pend = end; - nest--; + return true; } private: - extern (D) Macro* search(const(char)[] name) + extern (D) Macro* search(const(char)[] name) @nogc nothrow pure @safe { //printf("Macro::search(%.*s)\n", cast(int)name.length, name.ptr); if (auto table = name in mactab) @@ -282,7 +284,7 @@ struct Macro const(char)[] text; // macro replacement text int inuse; // macro is in use (don't expand) - this(const(char)[] name, const(char)[] text) + this(const(char)[] name, const(char)[] text) @nogc nothrow pure @safe { this.name = name; this.text = text; @@ -297,7 +299,7 @@ struct Macro * copy allocated with mem.xmalloc() */ -char[] memdup(const(char)[] p) +char[] memdup(const(char)[] p) nothrow pure @trusted { size_t len = p.length; return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len]; @@ -312,7 +314,7 @@ char[] memdup(const(char)[] p) * 1..9: get nth argument * -1: get 2nd through end */ -size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) +size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure { /* Scan forward for matching right parenthesis. * Nest parentheses. diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 5e802da..2cb1cc7 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -325,7 +325,6 @@ extern (C++) final class Module : Package extern (C++) __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them extern (C++) __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them extern (C++) __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them - extern (C++) __gshared uint dprogress; // progress resolving the deferred list static void _init() { @@ -1300,19 +1299,22 @@ extern (C++) final class Module : Package extern (D) static void addDeferredSemantic(Dsymbol s) { //printf("Module::addDeferredSemantic('%s')\n", s.toChars()); - deferred.push(s); + if (!deferred.contains(s)) + deferred.push(s); } extern (D) static void addDeferredSemantic2(Dsymbol s) { //printf("Module::addDeferredSemantic2('%s')\n", s.toChars()); - deferred2.push(s); + if (!deferred2.contains(s)) + deferred2.push(s); } extern (D) static void addDeferredSemantic3(Dsymbol s) { //printf("Module::addDeferredSemantic3('%s')\n", s.toChars()); - deferred3.push(s); + if (!deferred.contains(s)) + deferred3.push(s); } /****************************************** @@ -1320,19 +1322,15 @@ extern (C++) final class Module : Package */ static void runDeferredSemantic() { - if (dprogress == 0) - return; - __gshared int nested; if (nested) return; - //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim); + //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.dim); nested++; size_t len; do { - dprogress = 0; len = deferred.dim; if (!len) break; @@ -1358,13 +1356,13 @@ extern (C++) final class Module : Package s.dsymbolSemantic(null); //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars()); } - //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress); + //printf("\tdeferred.dim = %ld, len = %ld\n", deferred.dim, len); if (todoalloc) free(todoalloc); } - while (deferred.dim < len || dprogress); // while making progress + while (deferred.dim != len); // while making progress nested--; - //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim); + //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.dim); } static void runDeferredSemantic2() diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index 6eb433e..ba83649 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -226,12 +226,12 @@ private final class ParamSection : Section buf.writestring("$(DDOC_PARAM_ID "); { size_t o = buf.length; - Parameter fparam = isFunctionParameter(a, namestart, namelen); + Parameter fparam = isFunctionParameter(a, namestart[0 .. namelen]); if (!fparam) { // Comments on a template might refer to function parameters within. // Search the parameters of nested eponymous functions (with the same name.) - fparam = isEponymousFunctionParameter(a, namestart, namelen); + fparam = isEponymousFunctionParameter(a, namestart[0 .. namelen]); } bool isCVariadic = isCVariadicParameter(a, namestart[0 .. namelen]); if (isCVariadic) @@ -328,7 +328,7 @@ private final class MacroSection : Section private alias Sections = Array!(Section); // Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one). -private bool isCVariadicParameter(Dsymbols* a, const(char)[] p) +private bool isCVariadicParameter(Dsymbols* a, const(char)[] p) @safe { foreach (member; *a) { @@ -339,7 +339,7 @@ private bool isCVariadicParameter(Dsymbols* a, const(char)[] p) return false; } -private Dsymbol getEponymousMember(TemplateDeclaration td) +private Dsymbol getEponymousMember(TemplateDeclaration td) @safe { if (!td.onemember) return null; @@ -456,7 +456,11 @@ extern(C++) void gendocfile(Module m) OutBuffer buf2; buf2.writestring("$(DDOC)"); size_t end = buf2.length; - m.macrotable.expand(buf2, 0, end, null); + + const success = m.macrotable.expand(buf2, 0, end, null, global.recursionLimit); + if (!success) + error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", global.recursionLimit); + version (all) { /* Remove all the escape sequences from buf2, @@ -2561,17 +2565,17 @@ private size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref /**************************************************** */ -private bool isIdentifier(Dsymbols* a, const(char)* p, size_t len) +private bool isIdentifier(Dsymbols* a, const(char)[] s) { foreach (member; *a) { if (auto imp = member.isImport()) { // For example: `public import str = core.stdc.string;` - // This checks if `p` is equal to `str` + // This checks if `s` is equal to `str` if (imp.aliasId) { - if (p[0 .. len] == imp.aliasId.toString()) + if (s == imp.aliasId.toString()) return true; } else @@ -2586,14 +2590,14 @@ private bool isIdentifier(Dsymbols* a, const(char)* p, size_t len) } fullyQualifiedImport ~= imp.id.toString(); - // Check if `p` == `core.stdc.string` - if (p[0 .. len] == fullyQualifiedImport) + // Check if `s` == `core.stdc.string` + if (s == fullyQualifiedImport) return true; } } else if (member.ident) { - if (p[0 .. len] == member.ident.toString()) + if (s == member.ident.toString()) return true; } @@ -2603,12 +2607,12 @@ private bool isIdentifier(Dsymbols* a, const(char)* p, size_t len) /**************************************************** */ -private bool isKeyword(const(char)* p, size_t len) +private bool isKeyword(const(char)[] str) @safe { immutable string[3] table = ["true", "false", "null"]; foreach (s; table) { - if (p[0 .. len] == s) + if (str == s) return true; } return false; @@ -2616,7 +2620,7 @@ private bool isKeyword(const(char)* p, size_t len) /**************************************************** */ -private TypeFunction isTypeFunction(Dsymbol s) +private TypeFunction isTypeFunction(Dsymbol s) @safe { FuncDeclaration f = s.isFuncDeclaration(); /* f.type may be NULL for template members. @@ -2632,14 +2636,14 @@ private TypeFunction isTypeFunction(Dsymbol s) /**************************************************** */ -private Parameter isFunctionParameter(Dsymbol s, const(char)* p, size_t len) +private Parameter isFunctionParameter(Dsymbol s, const(char)[] str) @safe { TypeFunction tf = isTypeFunction(s); if (tf && tf.parameterList.parameters) { foreach (fparam; *tf.parameterList.parameters) { - if (fparam.ident && p[0 .. len] == fparam.ident.toString()) + if (fparam.ident && str == fparam.ident.toString()) { return fparam; } @@ -2650,11 +2654,11 @@ private Parameter isFunctionParameter(Dsymbol s, const(char)* p, size_t len) /**************************************************** */ -private Parameter isFunctionParameter(Dsymbols* a, const(char)* p, size_t len) +private Parameter isFunctionParameter(Dsymbols* a, const(char)[] p) @safe { - for (size_t i = 0; i < a.dim; i++) + foreach (Dsymbol sym; *a) { - Parameter fparam = isFunctionParameter((*a)[i], p, len); + Parameter fparam = isFunctionParameter(sym, p); if (fparam) { return fparam; @@ -2665,11 +2669,11 @@ private Parameter isFunctionParameter(Dsymbols* a, const(char)* p, size_t len) /**************************************************** */ -private Parameter isEponymousFunctionParameter(Dsymbols *a, const(char) *p, size_t len) +private Parameter isEponymousFunctionParameter(Dsymbols *a, const(char)[] p) @safe { - for (size_t i = 0; i < a.dim; i++) + foreach (Dsymbol dsym; *a) { - TemplateDeclaration td = (*a)[i].isTemplateDeclaration(); + TemplateDeclaration td = dsym.isTemplateDeclaration(); if (td && td.onemember) { /* Case 1: we refer to a template declaration inside the template @@ -2688,7 +2692,7 @@ private Parameter isEponymousFunctionParameter(Dsymbols *a, const(char) *p, size /// ...ddoc... alias case2 = case1!int; */ - AliasDeclaration ad = (*a)[i].isAliasDeclaration(); + AliasDeclaration ad = dsym.isAliasDeclaration(); if (ad && ad.aliassym) { td = ad.aliassym.isTemplateDeclaration(); @@ -2699,7 +2703,7 @@ private Parameter isEponymousFunctionParameter(Dsymbols *a, const(char) *p, size Dsymbol sym = getEponymousMember(td); if (sym) { - Parameter fparam = isFunctionParameter(sym, p, len); + Parameter fparam = isFunctionParameter(sym, p); if (fparam) { return fparam; @@ -4956,17 +4960,17 @@ private void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, s i = buf.bracket(i, "$(DDOC_AUTO_PSYMBOL_SUPPRESS ", j - 1, ")") - 1; break; } - if (isIdentifier(a, start, len)) + if (isIdentifier(a, start[0 .. len])) { i = buf.bracket(i, "$(DDOC_AUTO_PSYMBOL ", j, ")") - 1; break; } - if (isKeyword(start, len)) + if (isKeyword(start[0 .. len])) { i = buf.bracket(i, "$(DDOC_AUTO_KEYWORD ", j, ")") - 1; break; } - if (isFunctionParameter(a, start, len)) + if (isFunctionParameter(a, start[0 .. len])) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j); i = buf.bracket(i, "$(DDOC_AUTO_PARAM ", j, ")") - 1; @@ -5055,7 +5059,7 @@ private void highlightCode(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t off if (i < j) { size_t len = j - i; - if (isIdentifier(a, start, len)) + if (isIdentifier(a, start[0 .. len])) { i = buf.bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; continue; @@ -5066,12 +5070,12 @@ private void highlightCode(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t off if (i < j) { size_t len = j - i; - if (isIdentifier(a, start, len)) + if (isIdentifier(a, start[0 .. len])) { i = buf.bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; continue; } - if (isFunctionParameter(a, start, len)) + if (isFunctionParameter(a, start[0 .. len])) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j); i = buf.bracket(i, "$(DDOC_PARAM ", j, ")") - 1; @@ -5195,12 +5199,12 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of if (!sc) break; size_t len = lex.p - tok.ptr; - if (isIdentifier(a, tok.ptr, len)) + if (isIdentifier(a, tok.ptr[0 .. len])) { highlight = "$(D_PSYMBOL "; break; } - if (isFunctionParameter(a, tok.ptr, len)) + if (isFunctionParameter(a, tok.ptr[0 .. len])) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j); highlight = "$(D_PARAM "; @@ -5246,7 +5250,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of /**************************************** * Determine if p points to the start of a "..." parameter identifier. */ -private bool isCVariadicArg(const(char)[] p) +private bool isCVariadicArg(const(char)[] p) @nogc nothrow pure @safe { return p.length >= 3 && p[0 .. 3] == "..."; } @@ -5254,7 +5258,7 @@ private bool isCVariadicArg(const(char)[] p) /**************************************** * Determine if p points to the start of an identifier. */ -bool isIdStart(const(char)* p) +bool isIdStart(const(char)* p) @nogc nothrow pure { dchar c = *p; if (isalpha(c) || c == '_') @@ -5273,7 +5277,7 @@ bool isIdStart(const(char)* p) /**************************************** * Determine if p points to the rest of an identifier. */ -bool isIdTail(const(char)* p) +bool isIdTail(const(char)* p) @nogc nothrow pure { dchar c = *p; if (isalnum(c) || c == '_') @@ -5292,7 +5296,7 @@ bool isIdTail(const(char)* p) /**************************************** * Determine if p points to the indentation space. */ -private bool isIndentWS(const(char)* p) +private bool isIndentWS(const(char)* p) @nogc nothrow pure @safe { return (*p == ' ') || (*p == '\t'); } @@ -5300,7 +5304,7 @@ private bool isIndentWS(const(char)* p) /***************************************** * Return number of bytes in UTF character. */ -int utfStride(const(char)* p) +int utfStride(const(char)* p) @nogc nothrow pure { dchar c = *p; if (c < 0x80) @@ -5310,7 +5314,7 @@ int utfStride(const(char)* p) return cast(int)i; } -private inout(char)* stripLeadingNewlines(inout(char)* s) +private inout(char)* stripLeadingNewlines(inout(char)* s) @nogc nothrow pure { while (s && *s == '\n' || *s == '\r') s++; diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 7f57cbe..c3424dc 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -359,6 +359,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.overlapped = true; dsym.sequenceNumber = global.varSequenceNumber++; + if (!dsym.isScope()) + dsym.maybeScope = true; Scope* scx = null; if (dsym._scope) @@ -2034,8 +2036,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ed.semanticRun = PASS.semanticdone; return; } - uint dprogress_save = Module.dprogress; - Scope* scx = null; if (ed._scope) { @@ -2093,7 +2093,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { // memtype is forward referenced, so try again later deferDsymbolSemantic(ed, scx); - Module.dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); ed.semanticRun = PASS.initial; return; @@ -2134,8 +2133,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done ed.semanticRun = PASS.semanticdone; - Module.dprogress++; - // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint // Deprecated in 2.100 // Make an error in 2.110 @@ -3945,7 +3942,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (funcdecl.canInferAttributes(sc)) funcdecl.initInferAttributes(); - Module.dprogress++; funcdecl.semanticRun = PASS.semanticdone; /* Save scope for possible later use (if we need the @@ -4669,7 +4665,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sd.inv = buildInv(sd, sc2); - Module.dprogress++; sd.semanticRun = PASS.semanticdone; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd.toChars()); @@ -5329,7 +5324,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor cldec.inv = buildInv(cldec, sc2); - Module.dprogress++; cldec.semanticRun = PASS.semanticdone; //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); @@ -5676,7 +5670,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) ); - Module.dprogress++; idec.semanticRun = PASS.semanticdone; //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); @@ -6332,6 +6325,10 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* * resolve the alias of eponymous member. */ tempinst.aliasdecl = tempinst.aliasdecl.toAlias2(); + + // stop AliasAssign tuple building + if (auto td = tempinst.aliasdecl.isTupleDeclaration()) + td.building = false; } Laftersemantic: @@ -6726,13 +6723,28 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) */ const errors = global.errors; + Dsymbol s; + + // Try AliasSeq optimization + if (auto ti = ds.type.isTypeInstance()) + { + if (!ti.tempinst.findTempDecl(sc, null)) + return errorRet(); + if (auto tempinst = isAliasSeq(sc, ti)) + { + s = aliasAssignInPlace(sc, tempinst, aliassym); + if (!s) + return errorRet(); + goto Lsymdone; + } + } /* This section is needed because Type.resolve() will: * const x = 3; * alias y = x; * try to convert identifier x to 3. */ - auto s = ds.type.toDsymbol(sc); + s = ds.type.toDsymbol(sc); if (errors != global.errors) return errorRet(); if (s == aliassym) @@ -6784,6 +6796,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) if (s) // it's a symbolic alias { + Lsymdone: //printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars()); aliassym.type = null; aliassym.aliassym = s; @@ -6813,6 +6826,135 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) } /*************************************** + * Expands template instance arguments inside 'alias assign' target declaration (aliassym), + * instead of inside 'tempinst.tiargs' every time. + * Params: + * tempinst = AliasSeq instance + * aliassym = the AliasDeclaration corresponding to AliasAssign + * Returns: + * null. + */ +private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst, + AliasDeclaration aliassym) +{ + // Mark instance with semantic done, not needed but just in case. + tempinst.inst = tempinst; + tempinst.semanticRun = PASS.semanticdone; + TupleDeclaration td; + if (aliassym.type) + { + // Convert TypeTuple to TupleDeclaration to avoid back and forth allocations + // in the assignment process + if (auto tt = aliassym.type.isTypeTuple()) + { + auto objs = new Objects(tt.arguments.length); + foreach (i, p; *tt.arguments) + (*objs)[i] = p.type; + td = new TupleDeclaration(tempinst.loc, aliassym.ident, objs); + td.storage_class |= STC.templateparameter; + td.building = true; + aliassym.type = null; + } + else if (aliassym.type.isTypeError()) + return null; + + } + else if (auto otd = aliassym.aliassym.isTupleDeclaration()) + { + if (otd.building) + td = otd; + else + { + td = new TupleDeclaration(tempinst.loc, aliassym.ident, otd.objects.copy()); + td.storage_class |= STC.templateparameter; + td.building = true; + } + } + // If starting from single element in aliassym (td == null) we need to build the tuple + // after semanticTiargs to keep same semantics (for example a FuncLiteraldeclaration + // template argument is converted to FuncExp) + if (td) + aliassym.aliassym = td; + aliassym.semanticRun = PASS.semanticdone; + if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td)) + { + tempinst.errors = true; + return null; + } + // The alias will stop tuple 'building' mode when used (in AliasDeclaration.toAlias(), + // then TupleDeclaration.getType() will work again) + aliassym.semanticRun = PASS.initial; + if (!td) + { + td = new TupleDeclaration(tempinst.loc, aliassym.ident, tempinst.tiargs); + td.storage_class |= STC.templateparameter; + td.building = true; + return td; + } + + auto tiargs = tempinst.tiargs; + size_t oldlen = td.objects.length; + size_t origstart; + size_t insertidx; + size_t insertlen; + foreach (i, o; *tiargs) + { + if (o !is td) + { + ++insertlen; + continue; + } + // tuple contains itself (tuple = AliasSeq!(..., tuple, ...)) + if (insertlen) // insert any left element before + { + td.objects.insert(insertidx, (*tiargs)[i - insertlen .. i]); + if (insertidx == 0) // reset original tuple start point + origstart = insertlen; + insertlen = 0; + } + if (insertidx) // insert tuple if found more than one time + { + td.objects.reserve(oldlen); // reserve first to assert a valid slice + td.objects.pushSlice((*td.objects)[origstart .. origstart + oldlen]); + } + insertidx = td.objects.length; + } + if (insertlen) + { + if (insertlen != tiargs.length) // insert any left element + td.objects.pushSlice((*tiargs)[$ - insertlen .. $]); + else + // just assign tiargs if tuple = AliasSeq!(nottuple, nottuple...) + td.objects = tempinst.tiargs; + } + return td; +} + +/*************************************** + * Check if a template instance is a trivial AliasSeq but without other overloads. + * We can only be 100% sure of being AliasSeq after running semanticTiargs() + * and findBestMatch() but this optimization must happen before that. + */ +private TemplateInstance isAliasSeq(Scope* sc, TypeInstance ti) +{ + auto tovers = ti.tempinst.tempdecl.isOverloadSet(); + foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1) + { + Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempinst.tempdecl; + int r = overloadApply(dstart, (Dsymbol s) + { + auto td = s.isTemplateDeclaration(); + if (!td || !td.isTrivialAliasSeq) + return 1; + return 0; + }); + if (r) + return null; + } + return ti.tempinst; +} + +/*************************************** * Find all instance fields in `ad`, then push them into `fields`. * * Runs semantic() for all instance field variables, but also diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 02f12e4..34cae1d 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -6564,10 +6564,12 @@ extern (C++) class TemplateInstance : ScopeDsymbol * tiargs array of template arguments * flags 1: replace const variables with their initializers * 2: don't devolve Parameter to Type + * atd tuple being optimized. If found, it's not expanded here + * but in AliasAssign semantic. * Returns: * false if one or more arguments have errors. */ - extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags) + extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance.semanticTiargs()\n"); @@ -6767,6 +6769,11 @@ extern (C++) class TemplateInstance : ScopeDsymbol TupleDeclaration d = sa.toAlias().isTupleDeclaration(); if (d) { + if (d is atd) + { + (*tiargs)[j] = d; + continue; + } // Expand tuple tiargs.remove(j); tiargs.insert(j, d.objects); diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index fb5e092..4f06bac 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -36,6 +36,21 @@ import dmd.tokens; import dmd.visitor; import dmd.arraytypes; +/// Groups global state for escape checking together +package(dmd) struct EscapeState +{ + // Maps `sequenceNumber` of a `VarDeclaration` to an object that contains the + // reason it failed to infer `scope` + // https://issues.dlang.org/show_bug.cgi?id=23295 + private __gshared RootObject[int] scopeInferFailure; + + /// Called by `initDMD` / `deinitializeDMD` to reset global state + static void reset() + { + scopeInferFailure = null; + } +} + /****************************************************** * Checks memory objects passed to a function. * Checks that if a memory object is passed by ref or by pointer, @@ -271,6 +286,40 @@ bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) return errors; } +/** + * A `scope` variable was assigned to non-scope parameter `v`. + * If applicable, print why the parameter was not inferred `scope`. + * + * Params: + * printFunc = error/deprecation print function to use + * v = parameter that was not inferred + * recursionLimit = recursion limit for printing the reason + */ +void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit) +{ + recursionLimit--; + if (recursionLimit < 0 || !v) + return; + + if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferFailure) + { + switch ((*o).dyncast()) + { + case DYNCAST.expression: + Expression e = cast(Expression) *o; + printFunc(e.loc, "which is not `scope` because of `%s`", e.toChars()); + break; + case DYNCAST.dsymbol: + VarDeclaration v1 = cast(VarDeclaration) *o; + printFunc(v1.loc, "which is assigned to non-scope parameter `%s`", v1.toChars()); + printScopeFailure(printFunc, v1, recursionLimit); + break; + default: + assert(0); + } + } +} + /**************************************** * Function parameter `par` is being initialized to `arg`, * and `par` may escape. @@ -280,6 +329,7 @@ bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) * sc = used to determine current function and module * fdc = function being called, `null` if called indirectly * par = function parameter (`this` if null) + * vPar = `VarDeclaration` corresponding to `par` * parStc = storage classes of function parameter (may have added `scope` from `pure`) * arg = initializer for param * assertmsg = true if the parameter is the msg argument to assert(bool, msg). @@ -287,7 +337,7 @@ bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) * Returns: * `true` if pointers to the stack can escape via assignment */ -bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC parStc, Expression arg, bool assertmsg, bool gag) +bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag) { enum log = false; if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n", @@ -327,13 +377,21 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC } else if (par) { - result |= sc.setUnsafeDIP1000(gag, arg.loc, - desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`", v, par, fdc); + if (sc.setUnsafeDIP1000(gag, arg.loc, + desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`", v, par, fdc)) + { + result = true; + printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), global.params.useDIP1000), vPar, 10); + } } else { - result |= sc.setUnsafeDIP1000(gag, arg.loc, - desc ~ " `%s` assigned to non-scope parameter `this` calling `%s`", v, fdc); + if (sc.setUnsafeDIP1000(gag, arg.loc, + desc ~ " `%s` assigned to non-scope parameter `this` calling `%s`", v, fdc)) + { + result = true; + printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), global.params.useDIP1000), fdc.vthis, 10); + } } } @@ -345,7 +403,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC Dsymbol p = v.toParent2(); - notMaybeScope(v); + notMaybeScope(v, vPar); if (v.isScope()) { @@ -366,7 +424,8 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC */ if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n", v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(), __LINE__); - v.doNotInferScope = true; + + doNotInferScope(v, vPar); } } @@ -378,7 +437,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC Dsymbol p = v.toParent2(); - notMaybeScope(v); + notMaybeScope(v, arg); if (checkScopeVarAddr(v, arg, sc, gag)) { result = true; @@ -405,7 +464,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC Dsymbol p = v.toParent2(); - notMaybeScope(v); + notMaybeScope(v, arg); if ((v.isReference() || v.isScope()) && p == sc.func) { @@ -553,7 +612,16 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) return false; if (e1.isSliceExp()) - return false; + { + if (VarDeclaration va = expToVariable(e1)) + { + if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable + !va.type.hasPointers()) + return false; + } + else + return false; + } /* The struct literal case can arise from the S(e2) constructor call: * return S(e2); @@ -650,7 +718,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (va && !vaIsRef && !va.isScope() && !v.isScope() && !v.isTypesafeVariadicParameter && !va.isTypesafeVariadicParameter && - (va.storage_class & v.storage_class & STC.maybescope) && + (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) && p == fd) { /* Add v to va's list of dependencies @@ -660,7 +728,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } if (vaIsFirstRef && - (v.isScope() || (v.storage_class & STC.maybescope)) && + (v.isScope() || v.maybeScope) && !(v.storage_class & STC.return_) && v.isParameter() && fd.flags & FUNCFLAG.returnInprocess && @@ -672,7 +740,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } if (!(va && va.isScope()) || vaIsRef) - notMaybeScope(v); + notMaybeScope(v, e); if (v.isScope()) { @@ -682,7 +750,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (va.isScope()) continue; - if (!va.doNotInferScope) + if (va.maybeScope) { if (log) printf("inferring scope for lvalue %s\n", va.toChars()); va.storage_class |= STC.scope_ | STC.scopeinferred; @@ -711,7 +779,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } } - if (va && !va.isDataseg() && !va.doNotInferScope) + if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { if (!va.isScope()) { /* v is scope, and va is not scope, so va needs to @@ -742,7 +810,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) Type tb = v.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) { - if (va && !va.isDataseg() && !va.doNotInferScope) + if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { if (!va.isScope()) { //printf("inferring scope for %s\n", va.toChars()); @@ -759,7 +827,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) * It may escape via that assignment, therefore, v can never be 'scope'. */ //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__); - v.doNotInferScope = true; + doNotInferScope(v, e); } } @@ -812,12 +880,12 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } if (!(va && va.isScope())) - notMaybeScope(v); + notMaybeScope(v, e); if (p != sc.func) continue; - if (va && !va.isDataseg() && !va.doNotInferScope) + if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { if (!va.isScope()) { //printf("inferring scope for %s\n", va.toChars()); @@ -855,12 +923,12 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) Dsymbol p = v.toParent2(); if (!(va && va.isScope())) - notMaybeScope(v); + notMaybeScope(v, e); if (!(v.isReference() || v.isScope()) || p != fd) continue; - if (va && !va.isDataseg() && !va.doNotInferScope) + if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { /* Don't infer STC.scope_ for va, because then a closure * won't be generated for fd. @@ -891,7 +959,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() && - (!va || !(va.storage_class & STC.temp))) + (!va || !(va.storage_class & STC.temp) && !va.isScope())) { if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1)) { @@ -910,7 +978,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } } - if (va && !va.isDataseg() && !va.doNotInferScope) + if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { if (!va.isScope()) { //printf("inferring scope for %s\n", va.toChars()); @@ -963,8 +1031,7 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) } else { - //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - v.doNotInferScope = true; + notMaybeScope(v, new ThrowExp(e.loc, e)); } } return result; @@ -1036,7 +1103,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) else { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - v.doNotInferScope = true; + notMaybeScope(v, e); } } @@ -1191,7 +1258,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) Dsymbol p = v.toParent2(); - if ((v.isScope() || (v.storage_class & STC.maybescope)) && + if ((v.isScope() || v.maybeScope) && !(v.storage_class & STC.return_) && v.isParameter() && !v.doNotInferReturn && @@ -1266,7 +1333,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) else { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - v.doNotInferScope = true; + doNotInferScope(v, e); } } @@ -1330,7 +1397,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (!refs) { if (sc.func.vthis == v) - notMaybeScope(v); + notMaybeScope(v, e); if (checkScopeVarAddr(v, e, sc, gag)) { @@ -2226,26 +2293,41 @@ public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* v } /*********************************** - * Turn off `STC.maybescope` for variable `v`. + * Turn off `maybeScope` for variable `v`. * - * This exists in order to find where `STC.maybescope` is getting turned off. + * This exists in order to find where `maybeScope` is getting turned off. * Params: * v = variable + * o = reason for it being turned off: + * - `Expression` such as `throw e` or `&e` + * - `VarDeclaration` of a non-scope parameter it was assigned to + * - `null` for no reason */ -version (none) +private void notMaybeScope(VarDeclaration v, RootObject o) { - private void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v) + if (v.maybeScope) { - printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars()); - v.storage_class &= ~STC.maybescope; + v.maybeScope = false; + if (o && v.isParameter()) + EscapeState.scopeInferFailure[v.sequenceNumber] = o; } } -else + +/*********************************** + * Turn off `maybeScope` for variable `v` if it's not a parameter. + * + * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`, + * which is now just `VarDeclaration.maybeScope`. + * This function should probably be removed in future refactors. + * + * Params: + * v = variable + * o = reason for it being turned off + */ +private void doNotInferScope(VarDeclaration v, RootObject o) { - private void notMaybeScope(VarDeclaration v) - { - v.storage_class &= ~STC.maybescope; - } + if (!v.isParameter) + notMaybeScope(v, o); } /*********************************** @@ -2259,6 +2341,7 @@ else */ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { + if (funcdecl.flags & FUNCFLAG.returnInprocess) { funcdecl.flags &= ~FUNCFLAG.returnInprocess; @@ -2273,6 +2356,8 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) } } + if (!(funcdecl.flags & FUNCFLAG.inferScope)) + return; funcdecl.flags &= ~FUNCFLAG.inferScope; // Eliminate maybescope's @@ -2305,20 +2390,19 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) foreach (u, p; f.parameterList) { auto v = (*funcdecl.parameters)[u]; - if (v.storage_class & STC.maybescope) + if (v.maybeScope) { //printf("Inferring scope for %s\n", v.toChars()); - notMaybeScope(v); + notMaybeScope(v, null); v.storage_class |= STC.scope_ | STC.scopeinferred; p.storageClass |= STC.scope_ | STC.scopeinferred; - assert(!(p.storageClass & STC.maybescope)); } } } - if (funcdecl.vthis && funcdecl.vthis.storage_class & STC.maybescope) + if (funcdecl.vthis && funcdecl.vthis.maybeScope) { - notMaybeScope(funcdecl.vthis); + notMaybeScope(funcdecl.vthis, null); funcdecl.vthis.storage_class |= STC.scope_ | STC.scopeinferred; f.isScopeQual = true; f.isscopeinferred = true; @@ -2358,17 +2442,17 @@ private void eliminateMaybeScopes(VarDeclaration[] array) foreach (va; array) { if (log) printf(" va = %s\n", va.toChars()); - if (!(va.storage_class & (STC.maybescope | STC.scope_))) + if (!(va.maybeScope || va.isScope())) { if (va.maybes) { foreach (v; *va.maybes) { if (log) printf(" v = %s\n", v.toChars()); - if (v.storage_class & STC.maybescope) + if (v.maybeScope) { // v cannot be scope since it is assigned to a non-scope va - notMaybeScope(v); + notMaybeScope(v, va); if (!v.isReference()) v.storage_class &= ~(STC.return_ | STC.returninferred); changes = true; @@ -2485,7 +2569,7 @@ private bool enclosesLifetimeOf(const VarDeclaration va, const VarDeclaration v) * analysis for the function is completed. Thus, we save the data * until then. * Params: - * v = an `STC.maybescope` variable that was assigned to `this` + * v = a variable with `maybeScope == true` that was assigned to `this` */ private void addMaybe(VarDeclaration va, VarDeclaration v) { @@ -2570,8 +2654,7 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool g if (!v.isScope()) { - v.storage_class &= ~STC.maybescope; - v.doNotInferScope = true; + notMaybeScope(v, e); return false; } diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 397a41b..f871fade 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -5153,7 +5153,7 @@ extern (C++) final class CallExp : UnaExp /* Type needs destruction, so declare a tmp * which the back end will recognize and call dtor on */ - auto tmp = copyToTemp(0, "__tmpfordtor", this); + auto tmp = copyToTemp(0, Id.__tmpfordtor.toString(), this); auto de = new DeclarationExp(loc, tmp); auto ve = new VarExp(loc, tmp); Expression e = new CommaExp(loc, de, ve); diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 30baabd..3114100 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -2044,7 +2044,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Argument value can escape from the called function. * Check arg to see if it matters. */ - err |= checkParamArgumentEscape(sc, fd, p, cast(STC) pStc, arg, false, false); + VarDeclaration vPar = fd ? (fd.parameters ? (*fd.parameters)[i] : null) : null; + err |= checkParamArgumentEscape(sc, fd, p, vPar, cast(STC) pStc, arg, false, false); } // Turning heap allocations into stack allocations is dangerous without dip1000, since `scope` inference @@ -4726,7 +4727,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis = ue.e1.type; if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual)) { - if (checkParamArgumentEscape(sc, exp.f, null, STC.undefined_, ethis, false, false)) + if (checkParamArgumentEscape(sc, exp.f, null, null, STC.undefined_, ethis, false, false)) return setError(); } } @@ -6388,7 +6389,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.msg = resolveProperties(sc, exp.msg); exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf()); exp.msg = exp.msg.optimize(WANTvalue); - checkParamArgumentEscape(sc, null, null, STC.undefined_, exp.msg, true, false); + checkParamArgumentEscape(sc, null, null, null, STC.undefined_, exp.msg, true, false); } if (exp.msg && exp.msg.op == EXP.error) @@ -9934,7 +9935,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (isArrayCtor || isArraySetCtor) { const ts = t1b.nextOf().baseElemOf().isTypeStruct(); - if (!ts || (!ts.sym.postblit && !ts.sym.dtor)) + if (!ts || (!ts.sym.postblit && !ts.sym.hasCopyCtor && !ts.sym.dtor)) return setResult(res); auto func = isArrayCtor ? Id._d_arrayctor : Id._d_arraysetctor; @@ -9976,10 +9977,89 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor message("lowered %s =>\n %s", exp.toChars(), res.toChars()); } } + else if (auto ae = res.isAssignExp()) + res = lowerArrayAssign(ae); + else if (auto ce = res.isCommaExp()) + { + if (auto ae1 = ce.e1.isAssignExp()) + ce.e1 = lowerArrayAssign(ae1, true); + if (auto ae2 = ce.e2.isAssignExp()) + ce.e2 = lowerArrayAssign(ae2, true); + } return setResult(res); } + /*************************************** + * Lower AssignExp to `_d_arrayassign_{l,r}` if needed. + * + * Params: + * ae = the AssignExp to be lowered + * fromCommaExp = indicates whether `ae` is part of a CommaExp or not, + * so no unnecessary temporay variable is created. + * Returns: + * a CommaExp contiaining call a to `_d_arrayassign_{l,r}` if needed or + * `ae` otherwise + */ + private Expression lowerArrayAssign(AssignExp ae, bool fromCommaExp = false) + { + Type t1b = ae.e1.type.toBasetype(); + if (t1b.ty != Tsarray && t1b.ty != Tarray) + return ae; + + const isArrayAssign = + (ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) && + (ae.e2.type.ty == Tsarray || ae.e2.type.ty == Tarray) && + (ae.e1.type.nextOf && ae.e2.type.nextOf && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf)); + + if (!isArrayAssign) + return ae; + + const ts = t1b.nextOf().baseElemOf().isTypeStruct(); + if (!ts || (!ts.sym.postblit && !ts.sym.dtor)) + return ae; + + Expression res; + auto func = ae.e2.isLvalue || ae.e2.isSliceExp ? Id._d_arrayassign_l : Id._d_arrayassign_r; + + // Lower to `.object._d_arrayassign_l{r}(e1, e2)`` + Expression id = new IdentifierExp(ae.loc, Id.empty); + id = new DotIdExp(ae.loc, id, Id.object); + id = new DotIdExp(ae.loc, id, func); + + auto arguments = new Expressions(); + arguments.push(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf) + .expressionSemantic(sc)); + + Expression eValue2, value2 = ae.e2; + if (ae.e2.isLvalue) + value2 = new CastExp(ae.loc, ae.e2, ae.e2.type.nextOf.arrayOf) + .expressionSemantic(sc); + else if (!fromCommaExp) + { + // Rvalues from CommaExps were introduced in `visit(AssignExp)` + // and are temporary variables themselves. Rvalues from trivial + // SliceExps are simply passed by reference without any copying. + + // `__assigntmp` will be destroyed together with the array `ae.e1`. + // When `ae.e2` is a variadic arg array, it is also `scope`, so + // `__assigntmp` may also be scope. + auto vd = copyToTemp(STC.rvalue | STC.nodtor | STC.scope_, "__assigntmp", ae.e2); + eValue2 = new DeclarationExp(vd.loc, vd).expressionSemantic(sc); + value2 = new VarExp(vd.loc, vd).expressionSemantic(sc); + } + arguments.push(value2); + + Expression ce = new CallExp(ae.loc, id, arguments); + res = Expression.combine(eValue2, ce).expressionSemantic(sc); + res = Expression.combine(res, ae.e1).expressionSemantic(sc); + + if (global.params.verbose) + message("lowered %s =>\n %s", ae.toChars(), res.toChars()); + + return res; + } + override void visit(PowAssignExp exp) { if (exp.type) @@ -13092,7 +13172,10 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) if (sc.func && sc.func.isSynchronized()) return false; - return sharedError(e); + if (!allowRef && e.type.isShared()) + return sharedError(e); + + return false; } bool visitDotVar(DotVarExp e) @@ -13182,8 +13265,6 @@ bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) } if (sc.func && !sc.intypeof && !v.isDataseg()) { - v.storage_class &= ~STC.maybescope; - v.doNotInferScope = true; if (global.params.useDIP1000 != FeatureState.enabled && !(v.storage_class & STC.temp) && sc.setUnsafe(false, exp.loc, "cannot take address of local `%s` in `@safe` function `%s`", v, sc.func)) diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 7475cb4..4c09474 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -568,8 +568,6 @@ extern (C++) class FuncDeclaration : Declaration if (tf.isreturnscope) vthis.storage_class |= STC.returnScope; } - if (flags & FUNCFLAG.inferScope && !(vthis.storage_class & STC.scope_)) - vthis.storage_class |= STC.maybescope; vthis.dsymbolSemantic(sc); if (!sc.insert(vthis)) diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 05886f9..745d5eb 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -116,10 +116,6 @@ extern (C++) struct Param DiagnosticReporting useDeprecated = DiagnosticReporting.inform; // how use of deprecated features are handled bool useUnitTests; // generate unittest code bool useInline = false; // inline expand functions - FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 - FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params - bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 - bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md bool release; // build release version bool preservePaths; // true means don't strip path from source file DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled @@ -131,31 +127,10 @@ extern (C++) struct Param bool useModuleInfo = true; // generate runtime module information bool useTypeInfo = true; // generate runtime type information bool useExceptions = true; // support exception handling - bool noSharedAccess; // read/write access to shared memory objects - bool previewIn; // `in` means `[ref] scope const`, accepts rvalues - bool shortenedMethods; // allow => in normal function declarations 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 fix16997 = true; // fix integral promotions for unary + - ~ operators - // https://issues.dlang.org/show_bug.cgi?id=16997 - bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes - bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract - /** The --transition=safe switch should only be used to show code with - * silent semantics changes related to @safe improvements. It should not be - * used to hide a feature that will have to go through deprecate-then-error - * before becoming default. - */ - bool ehnogc; // use @nogc exception handling - FeatureState dtorFields; // destruct fields of partially constructed objects - // https://issues.dlang.org/show_bug.cgi?id=14246 - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() bool bitfields; // support C style bit fields - 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 CppStdRevision cplusplus = CppStdRevision.cpp11; // version of C++ standard to support @@ -173,6 +148,28 @@ extern (C++) struct Param bool hcUsage; // print help on -HC switch bool logo; // print compiler 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 + 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 + bool 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 = true; // 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 + CHECKENABLE useInvariants = CHECKENABLE._default; // generate class invariant checks CHECKENABLE useIn = CHECKENABLE._default; // generate precondition checks CHECKENABLE useOut = CHECKENABLE._default; // generate postcondition checks diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 41472b2..a6b1c90 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -116,10 +116,6 @@ struct Param Diagnostic useDeprecated; bool useUnitTests; // generate unittest code bool useInline; // inline expand functions - FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 - FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params - bool fixImmutableConv; - bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md bool release; // build release version bool preservePaths; // true means don't strip path from source file Diagnostic warnings; @@ -131,22 +127,10 @@ struct Param bool useModuleInfo; // generate runtime module information bool useTypeInfo; // generate runtime type information bool useExceptions; // support exception handling - bool noSharedAccess; // read/write access to shared memory objects - bool previewIn; // `in` means `scope const`, perhaps `ref`, accepts rvalues - bool shortenedMethods; // allow => in normal function declarations 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 fix16997; // fix integral promotions for unary + - ~ operators - // https://issues.dlang.org/show_bug.cgi?id=16997 - bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes - bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract - bool ehnogc; // use @nogc exception handling - FeatureState dtorFields; // destruct fields of partially constructed objects - // https://issues.dlang.org/show_bug.cgi?id=14246 - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() bool bitfields; // support C style bit fields - FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters 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) @@ -162,6 +146,27 @@ struct Param bool hcUsage; // print help on -HC switch 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 + 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 + bool 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 + // 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 CHECKENABLE useInvariants; // generate class invariant checks CHECKENABLE useIn; // generate precondition checks CHECKENABLE useOut; // generate postcondition checks diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 680d9c8..48995db 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -1890,7 +1890,17 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg buf.printf("%uu", cast(uint)v); break; case Tint64: - buf.printf("%lldL", v); + if (v == long.min) + { + // https://issues.dlang.org/show_bug.cgi?id=23173 + // This is a special case because - is not part of the + // integer literal and 9223372036854775808L overflows a long + buf.writestring("cast(long)-9223372036854775808"); + } + else + { + buf.printf("%lldL", v); + } break; case Tuns64: buf.printf("%lluLU", v); @@ -2651,7 +2661,9 @@ void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool all assert(strlen(buffer.ptr) < BUFFER_LEN); if (allowHex) { - real_t r = CTFloat.parse(buffer.ptr); + bool isOutOfRange; + real_t r = CTFloat.parse(buffer.ptr, isOutOfRange); + //assert(!isOutOfRange); // test/compilable/test22725.c asserts here if (r != value) // if exact duplication CTFloat.sprint(buffer.ptr, 'a', value); } diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index 5142daa..6695faa 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -160,6 +160,7 @@ immutable Msgtable[] msgtable = { "xopEquals", "__xopEquals" }, { "xopCmp", "__xopCmp" }, { "xtoHash", "__xtoHash" }, + { "__tmpfordtor" }, { "LINE", "__LINE__" }, { "FILE", "__FILE__" }, @@ -318,6 +319,8 @@ immutable Msgtable[] msgtable = { "_aaApply2" }, { "_d_arrayctor" }, { "_d_arraysetctor" }, + { "_d_arrayassign_l" }, + { "_d_arrayassign_r" }, // For pragma's { "Pinline", "inline" }, diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index b42b4a1..7f92298 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -88,7 +88,7 @@ nothrow: return name.ptr; } - extern (D) override const(char)[] toString() const pure + extern (D) override const(char)[] toString() const pure @safe { return name; } diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 11afcdd..21bbde8 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -2531,7 +2531,7 @@ class Lexer auto sbufptr = cast(const(char)*)stringbuffer[].ptr; TOK result; bool isOutOfRange = false; - t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, &isOutOfRange) : CTFloat.zero); + t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, isOutOfRange) : CTFloat.zero); switch (*p) { case 'F': diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 5808c28..6bfb729 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -58,7 +58,6 @@ public: static Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them static Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them static Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them - static unsigned dprogress; // progress resolving the deferred list static void _init(); diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 860cfa9..f2da41b 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -4407,30 +4407,46 @@ extern (C++) final class TypeFunction : TypeNext purityLevel(); + static bool mayHavePointers(Type t) + { + if (auto ts = t.isTypeStruct()) + { + auto sym = ts.sym; + if (sym.members && !sym.determineFields() && sym.type != Type.terror) + // struct is forward referenced, so "may have" pointers + return true; + } + return t.hasPointers(); + } + // See if p can escape via any of the other parameters if (purity == PURE.weak) { // Check escaping through parameters foreach (i, fparam; parameterList) { - if (fparam == p) - continue; Type t = fparam.type; if (!t) continue; - t = t.baseElemOf(); + t = t.baseElemOf(); // punch thru static arrays if (t.isMutable() && t.hasPointers()) { - if (fparam.isReference()) - { - } - else if (t.ty == Tarray || t.ty == Tpointer) + if (fparam.isReference() && fparam != p) + return stc; + + if (t.ty == Tdelegate) + return stc; // could escape thru delegate + + if (t.ty == Tclass) + return stc; + + /* if t is a pointer to mutable pointer + */ + if (auto tn = t.nextOf()) { - Type tn = t.nextOf().toBasetype(); - if (!(tn.isMutable() && tn.hasPointers())) - continue; + if (tn.isMutable() && mayHavePointers(tn)) + return stc; // escape through pointers } - return stc; } } diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index a2c364e..ce2769d 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -1999,7 +1999,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } check(TOK.rightParenthesis); - check(TOK.semicolon); + check(TOK.semicolon, "static assert"); return new AST.StaticAssert(loc, exp, msg); } @@ -2648,7 +2648,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } check(TOK.rightParenthesis); - check(TOK.semicolon); + check(TOK.semicolon, "invariant"); e = new AST.AssertExp(loc, e, msg); auto fbody = new AST.ExpStatement(loc, e); auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody); @@ -4738,7 +4738,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); // advance past = auto t = parseType(); AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null); - check(TOK.semicolon); + check(TOK.semicolon, "alias reassignment"); addComment(s, comment); auto a = new AST.Dsymbols(); a.push(s); @@ -4774,7 +4774,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto s = new AST.AliasThis(loc, token.ident); nextToken(); check(TOK.this_); - check(TOK.semicolon); + check(TOK.semicolon, "`alias Identifier this`"); auto a = new AST.Dsymbols(); a.push(s); addComment(s, comment); @@ -4791,7 +4791,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.assign); auto s = new AliasThis(loc, token.ident); nextToken(); - check(TOK.semicolon); + check(TOK.semicolon, "`alias this = Identifier`"); auto a = new Dsymbols(); a.push(s); addComment(s, comment); @@ -5331,6 +5331,33 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s); } + /***************************** + * Ad-hoc error message for missing or extra parens that close a condition. + * Params: + * start = "if", "while", etc. Must be 0 terminated. + * param = if the condition is a declaration, this will be non-null + * condition = if param is null, then this is the conditional Expression. If condition is null, + * then an error in the condition was already reported. + */ + private void closeCondition(string start, AST.Parameter param, AST.Expression condition) + { + string format; + if (token.value != TOK.rightParenthesis && condition) + { + format = "missing closing `)` after `%s (%s`"; + } + else + check(TOK.rightParenthesis); + if (token.value == TOK.rightParenthesis) + { + if (condition) // if not an error in condition + format = "extra `)` after `%s (%s)`"; + nextToken(); + } + if (format) + error(format.ptr, start.ptr, param ? "declaration".ptr : condition.toChars()); + } + /***************************************** * Parses `foreach` statements, `static foreach` statements and * `static foreach` declarations. @@ -5905,7 +5932,7 @@ LagainStc: { // mixin(string) AST.Expression e = parseAssignExp(); - check(TOK.semicolon); + check(TOK.semicolon, "mixin"); if (e.op == EXP.mixin_) { AST.MixinExp cpe = cast(AST.MixinExp)e; @@ -5961,12 +5988,12 @@ LagainStc: } case TOK.while_: { - AST.Parameter param = null; nextToken(); check(TOK.leftParenthesis); - param = parseAssignCondition(); - AST.Expression condition = parseExpression(); - check(TOK.rightParenthesis); + auto param = parseAssignCondition(); + auto condition = parseExpression(); + closeCondition("while", param, condition); + Loc endloc; AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc); s = new AST.WhileStatement(loc, condition, _body, endloc, param); @@ -5987,7 +6014,6 @@ LagainStc: case TOK.do_: { AST.Statement _body; - AST.Expression condition; nextToken(); const lookingForElseSave = lookingForElse; @@ -5996,8 +6022,8 @@ LagainStc: lookingForElse = lookingForElseSave; check(TOK.while_); check(TOK.leftParenthesis); - condition = parseExpression(); - check(TOK.rightParenthesis); + auto condition = parseExpression(); + closeCondition("do .. while", null, condition); if (token.value == TOK.semicolon) nextToken(); else @@ -6058,25 +6084,11 @@ LagainStc: } case TOK.if_: { - AST.Parameter param = null; - AST.Expression condition; - nextToken(); check(TOK.leftParenthesis); - param = parseAssignCondition(); - condition = parseExpression(); - if (token.value != TOK.rightParenthesis && condition) - { - error("missing closing `)` after `if (%s`", param ? "declaration".ptr : condition.toChars()); - } - else - check(TOK.rightParenthesis); - if (token.value == TOK.rightParenthesis) - { - if (condition) // if not an error in condition - error("extra `)` after `if (%s)`", param ? "declaration".ptr : condition.toChars()); - nextToken(); - } + auto param = parseAssignCondition(); + auto condition = parseExpression(); + closeCondition("if", param, condition); { const lookingForElseSave = lookingForElse; @@ -6223,7 +6235,7 @@ LagainStc: nextToken(); check(TOK.leftParenthesis); AST.Expression condition = parseExpression(); - check(TOK.rightParenthesis); + closeCondition("switch", null, condition); AST.Statement _body = parseStatement(ParseStatementFlags.scope_); s = new AST.SwitchStatement(loc, condition, _body, isfinal); break; @@ -6402,7 +6414,7 @@ LagainStc: { nextToken(); exp = parseExpression(); - check(TOK.rightParenthesis); + closeCondition("synchronized", null, exp); } else exp = null; @@ -6419,7 +6431,7 @@ LagainStc: nextToken(); check(TOK.leftParenthesis); exp = parseExpression(); - check(TOK.rightParenthesis); + closeCondition("with", null, exp); _body = parseStatement(ParseStatementFlags.scope_, null, &endloc); s = new AST.WithStatement(loc, exp, _body, endloc); break; @@ -6511,7 +6523,7 @@ LagainStc: if (peekNext() == TOK.leftParenthesis) { AST.Expression e = parseExpression(); - check(TOK.semicolon); + check(TOK.semicolon, "`import` Expression"); s = new AST.ExpStatement(loc, e); } else diff --git a/gcc/d/dmd/root/README.md b/gcc/d/dmd/root/README.md index 464f338..121d37c 100644 --- a/gcc/d/dmd/root/README.md +++ b/gcc/d/dmd/root/README.md @@ -2,25 +2,25 @@ | File | Purpose | |--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| [aav.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d) | An associative array implementation | -| [array.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d) | A dynamic array implementation | -| [bitarray.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/bitarray.d) | A compact array of bits | -| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/complex.d) | A complex number type | -| [ctfloat.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/ctfloat.d) | A floating point type for compile-time calculations | -| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/env.d) | Modify environment variables | -| [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d) | Read a file from disk and store it in memory | -| [filename.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d) | Encapsulate path and file names | -| [hash.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d) | Calculate a hash for a byte array | -| [longdouble.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/longdouble.d) | 80-bit floating point number implementation in case they are not natively supported | -| [man.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/man.d) | Opens an online manual page | -| [optional.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/optional.d) | Implementation of an 'Optional' type | -| [port.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d) | Portable routines for functions that have different implementations on different platforms | -| [region.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d) | A region allocator | -| [response.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/response.d) | Parse command line arguments from response files | -| [rmem.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d) | Allocate memory using `malloc` or the GC depending on the configuration | -| [rootobject.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/rootobject.d) | A root object that classes in dmd inherit from | -| [speller.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/speller.d) | Try to detect typos in identifiers | -| [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d) | Various string related functions | -| [stringtable.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/stringtable.d) | Specialized associative array with string keys stored in a variable length structure | -| [strtold.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/strtold.d) | D implementation of the standard C function `strtold` (String to long double) | -| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/utf.d) | Encoding/decoding Unicode text | +| [aav.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/aav.d) | An associative array implementation | +| [array.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/array.d) | A dynamic array implementation | +| [bitarray.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/bitarray.d) | A compact array of bits | +| [complex.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/complex.d) | A complex number type | +| [ctfloat.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/ctfloat.d) | A floating point type for compile-time calculations | +| [env.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/env.d) | Modify environment variables | +| [file.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/file.d) | Read a file from disk and store it in memory | +| [filename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/filename.d) | Encapsulate path and file names | +| [hash.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/hash.d) | Calculate a hash for a byte array | +| [longdouble.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/longdouble.d) | 80-bit floating point number implementation in case they are not natively supported | +| [man.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/man.d) | Opens an online manual page | +| [optional.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/optional.d) | Implementation of an 'Optional' type | +| [port.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/port.d) | Portable routines for functions that have different implementations on different platforms | +| [region.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/region.d) | A region allocator | +| [response.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/response.d) | Parse command line arguments from response files | +| [rmem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/rmem.d) | Allocate memory using `malloc` or the GC depending on the configuration | +| [rootobject.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/rootobject.d) | A root object that classes in dmd inherit from | +| [speller.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/speller.d) | Try to detect typos in identifiers | +| [string.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/string.d) | Various string related functions | +| [stringtable.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/stringtable.d) | Specialized associative array with string keys stored in a variable length structure | +| [strtold.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/strtold.d) | D implementation of the standard C function `strtold` (String to long double) | +| [utf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/utf.d) | Encoding/decoding Unicode text | diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index c2eb3e1..212fe96 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -222,6 +222,16 @@ public: } } + extern (D) void insert(size_t index, T[] a) pure nothrow + { + size_t d = a.length; + reserve(d); + if (length != index) + memmove(data.ptr + index + d, data.ptr + index, (length - index) * T.sizeof); + memcpy(data.ptr + index, a.ptr, d * T.sizeof); + length += d; + } + void insert(size_t index, T ptr) pure nothrow { reserve(1); @@ -414,6 +424,14 @@ unittest arrayA.zero(); foreach(e; arrayA) assert(e == 0); + + arrayA.setDim(0); + arrayA.pushSlice([5, 6]); + arrayA.insert(1, [1, 2]); + assert(arrayA[] == [5, 1, 2, 6]); + arrayA.insert(0, [7, 8]); + arrayA.insert(arrayA.length, [0, 9]); + assert(arrayA[] == [7, 8, 5, 1, 2, 6, 0, 9]); } /** diff --git a/gcc/d/dmd/root/ctfloat.d b/gcc/d/dmd/root/ctfloat.d index 5ccc7bf..8c2fe46 100644 --- a/gcc/d/dmd/root/ctfloat.d +++ b/gcc/d/dmd/root/ctfloat.d @@ -47,7 +47,7 @@ extern (C++) struct CTFloat static bool isInfinity(real_t r) pure; @system - static real_t parse(const(char)* literal, bool* isOutOfRange = null); + static real_t parse(const(char)* literal, out bool isOutOfRange); @system static int sprint(char* str, char fmt, real_t x); diff --git a/gcc/d/dmd/root/ctfloat.h b/gcc/d/dmd/root/ctfloat.h index 6b83dbf..5a6cf25 100644 --- a/gcc/d/dmd/root/ctfloat.h +++ b/gcc/d/dmd/root/ctfloat.h @@ -50,7 +50,7 @@ struct CTFloat static bool isSNaN(real_t r); static bool isInfinity(real_t r); - static real_t parse(const char *literal, bool *isOutOfRange = NULL); + static real_t parse(const char *literal, bool& isOutOfRange); static int sprint(char *str, char fmt, real_t x); static size_t hash(real_t a); diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 6f37770..ad4487f 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -472,9 +472,6 @@ private extern(C++) final class Semantic3Visitor : Visitor stc |= STC.variadic; } - if ((funcdecl.flags & FUNCFLAG.inferScope) && !(fparam.storageClass & STC.scope_)) - stc |= STC.maybescope; - stc |= fparam.storageClass & (STC.IOR | STC.return_ | STC.scope_ | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor | STC.returnScope | STC.register); v.storage_class = stc; v.dsymbolSemantic(sc2); diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index be95432..367c56b 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -1268,6 +1268,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) auto o = (*e.args)[0]; auto po = isParameter(o); auto s = getDsymbolWithoutExpCtx(o); + auto typeOfArg = isType(o); UserAttributeDeclaration udad = null; if (po) { @@ -1282,6 +1283,10 @@ Expression semanticTraits(TraitsExp e, Scope* sc) //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope); udad = s.userAttribDecl; } + else if (typeOfArg) + { + // If there is a type but no symbol, do nothing rather than erroring. + } else { version (none) diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index b1f1b1f..b21ff79 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1447,6 +1447,11 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) eparam.storageClass &= ~STC.auto_; eparam.storageClass |= STC.autoref; } + else if (eparam.storageClass & STC.ref_) + { + .error(loc, "cannot explicitly instantiate template function with `auto ref` parameter"); + errors = true; + } else { .error(loc, "`auto` can only be used as part of `auto ref` for template function parameters"); diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 140df7e..b0ce870 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -986,11 +986,9 @@ public: else if ((postblit || destructor) && e->op != EXP::blit && e->op != EXP::construct) { - /* Generate: _d_arrayassign(ti, from, to); */ - this->result_ = build_libcall (LIBCALL_ARRAYASSIGN, e->type, 3, - build_typeinfo (e, etype), - d_array_convert (e->e2), - d_array_convert (e->e1)); + /* Assigning to a non-trivially copyable array has already been + handled by the front-end. */ + gcc_unreachable (); } else { @@ -1124,27 +1122,7 @@ public: /* All other kinds of lvalue or rvalue static array assignment. Array construction has already been handled by the front-end. */ gcc_assert (e->op != EXP::construct); - - /* Generate: _d_arrayassign_l() - or: _d_arrayassign_r() */ - libcall_fn libcall = (lvalue) - ? LIBCALL_ARRAYASSIGN_L : LIBCALL_ARRAYASSIGN_R; - tree elembuf = build_local_temp (build_ctype (etype)); - Type *arrtype = (e->type->ty == TY::Tsarray) - ? etype->arrayOf () : e->type; - tree result = build_libcall (libcall, arrtype, 4, - build_typeinfo (e, etype), - d_array_convert (e->e2), - d_array_convert (e->e1), - build_address (elembuf)); - - /* Cast the libcall result back to a static array. */ - if (e->type->ty == TY::Tsarray) - result = indirect_ref (build_ctype (e->type), - d_array_ptr (result)); - - this->result_ = result; - return; + gcc_unreachable (); } /* Simple assignment. */ diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def index 459a283..282f22c 100644 --- a/gcc/d/runtime.def +++ b/gcc/d/runtime.def @@ -115,14 +115,7 @@ DEF_D_RUNTIME (ALLOCMEMORY, "_d_allocmemory", RT(VOIDPTR), P1(SIZE_T), DEF_D_RUNTIME (ARRAYCOPY, "_d_arraycopy", RT(ARRAY_VOID), P3(SIZE_T, ARRAY_VOID, ARRAY_VOID), 0) -/* Used for array assignments from an existing array. The `set' variant is for - when the assignment value is a single element. */ -DEF_D_RUNTIME (ARRAYASSIGN, "_d_arrayassign", RT(ARRAY_VOID), - P3(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID), 0) -DEF_D_RUNTIME (ARRAYASSIGN_L, "_d_arrayassign_l", RT(ARRAY_VOID), - P4(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID, VOIDPTR), 0) -DEF_D_RUNTIME (ARRAYASSIGN_R, "_d_arrayassign_r", RT(ARRAY_VOID), - P4(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID, VOIDPTR), 0) +/* Used for array assignments from a single element. */ DEF_D_RUNTIME (ARRAYSETASSIGN, "_d_arraysetassign", RT(VOIDPTR), P4(VOIDPTR, VOIDPTR, SIZE_T, CONST_TYPEINFO), 0) diff --git a/gcc/testsuite/gdc.test/compilable/aliasassign.d b/gcc/testsuite/gdc.test/compilable/aliasassign.d index a29836e..8643f5d 100644 --- a/gcc/testsuite/gdc.test/compilable/aliasassign.d +++ b/gcc/testsuite/gdc.test/compilable/aliasassign.d @@ -3,18 +3,18 @@ template AliasSeq(T...) { alias AliasSeq = T; } template Unqual(T) { static if (is(T U == const U)) - alias Unqual = U; + alias Unqual = U; else static if (is(T U == immutable U)) - alias Unqual = U; + alias Unqual = U; else - alias Unqual = T; + alias Unqual = T; } template staticMap(alias F, T...) { alias A = AliasSeq!(); static foreach (t; T) - A = AliasSeq!(A, F!t); // what's tested + A = AliasSeq!(A, F!t); // what's tested alias staticMap = A; } @@ -28,7 +28,7 @@ template reverse(T...) { alias A = AliasSeq!(); static foreach (t; T) - A = AliasSeq!(t, A); // what's tested + A = AliasSeq!(t, A); // what's tested alias reverse = A; } @@ -38,3 +38,98 @@ alias TK2 = reverse!(int, const uint, X2); static assert(TK2[0] == 3); static assert(is(TK2[1] == const(uint))); static assert(is(TK2[2] == int)); + +/**************************************************/ + +template Tp(Args...) +{ + alias Tp = AliasSeq!(int, 1, "asd", Args); + static foreach (arg; Args) + { + Tp = AliasSeq!(4, Tp, "zxc", arg, Tp, 5, 4, int, Tp[0..2]); + } +} + +void fun(){} + +alias a1 = Tp!(char[], fun, x => x); +static assert( + __traits(isSame, a1, AliasSeq!(4, 4, 4, int, 1, "asd", char[], fun, + x => x, "zxc", char[], int, 1, "asd", char[], fun, x => x, + 5, 4, int, int, 1, "zxc", fun, 4, int, 1, "asd", char[], + fun, x => x, "zxc", char[], int, 1, "asd", char[], fun, + x => x, 5, 4, int, int, 1, 5, 4, int, 4, int, "zxc", x => x, + 4, 4, int, 1, "asd", char[], fun, x => x, "zxc", char[], + int, 1, "asd", char[], fun, x => x, 5, 4, int, int, 1, + "zxc", fun, 4, int, 1, "asd", char[], fun, x => x, "zxc", + char[], int, 1, "asd", char[], fun, x => x, 5, 4, int, int, + 1, 5, 4, int, 4, int, 5, 4, int, 4, 4))); + +template Tp2(Args...) +{ + alias Tp2 = () => 1; + static foreach (i; 0..Args.length) + Tp2 = AliasSeq!(Tp2, Args[i]); +} + +const x = 8; +static assert( + __traits(isSame, Tp2!(2, float, x), AliasSeq!(() => 1, 2, float, x))); + + +enum F(int i) = i * i; + +template staticMap2(alias fun, args...) +{ + alias staticMap2 = AliasSeq!(); + static foreach (i; 0 .. args.length) + staticMap2 = AliasSeq!(fun!(args[i]), staticMap2, fun!(args[i])); +} + +enum a2 = staticMap2!(F, 0, 1, 2, 3, 4); + +struct Cmp(T...){} +// isSame sucks +static assert(is(Cmp!a2 == Cmp!(16, 9, 4, 1, 0, 0, 1, 4, 9, 16))); + +template Tp3() +{ + alias aa1 = int; + static foreach (t; AliasSeq!(float, char[])) + aa1 = AliasSeq!(aa1, t); + static assert(is(aa1 == AliasSeq!(int, float, char[]))); + + alias aa2 = AliasSeq!int; + static foreach (t; AliasSeq!(float, char[])) + aa2 = AliasSeq!(aa2, t); + static assert(is(aa2 == AliasSeq!(int, float, char[]))); + + alias aa3 = AliasSeq!int; + aa3 = AliasSeq!(float, char); + static assert(is(aa3 == AliasSeq!(float, char))); +} +alias a3 = Tp3!(); + +template Tp4() // Uses slow path because overload +{ + alias AliasSeq(T...) = T; + alias AliasSeq(alias f, T...) = T; + + alias aa4 = int; + aa4 = AliasSeq!(aa4, float); + static assert(is(aa4 == AliasSeq!(int, float))); + +} +alias a4 = Tp4!(); + +template Tp5() // same tp overloaded, still uses fast path +{ + alias AliasSeq2(T...) = T; + alias AliasSeq = AliasSeq2; + alias AliasSeq = AliasSeq2; + + alias aa5 = int; + aa5 = AliasSeq!(aa5, float); + static assert(is(aa5 == AliasSeq!(int, float))); +} +alias a5 = Tp5!(); diff --git a/gcc/testsuite/gdc.test/compilable/scope_infer_array_assign.d b/gcc/testsuite/gdc.test/compilable/scope_infer_array_assign.d new file mode 100644 index 0000000..8ef54a1 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/scope_infer_array_assign.d @@ -0,0 +1,28 @@ +// REQUIRED_ARGS: -preview=dip1000 + +// Test that scope inference works even with non POD array assignment +// This is tricky because it gets lowered to something like: +// (S[] __assigntmp0 = e[]) , _d_arrayassign_l(this.e[], __assigntmp0) , this.e[]; + +@safe: + +struct File +{ + void* f; + ~this() scope { } +} + +struct Vector +{ + File[] e; + + auto assign(File[] e) + { + this.e[] = e[]; // slice copy + } +} + +void test(scope File[] arr, Vector v) +{ + v.assign(arr); +} diff --git a/gcc/testsuite/gdc.test/compilable/test21197.d b/gcc/testsuite/gdc.test/compilable/test21197.d new file mode 100644 index 0000000..01ee66e --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21197.d @@ -0,0 +1,25 @@ +/* REQUIRED_ARGS: -preview=dip1000 + */ +// https://issues.dlang.org/show_bug.cgi?id=21197 + +@safe void check2() +{ + int random; + + S create1() return scope { + return S(); + } + + scope S gen1 = create1; + + S create2() { + return S(&random); + } + + scope S gen2 = create2; +} + +struct S +{ + int* r; +} diff --git a/gcc/testsuite/gdc.test/compilable/uda.d b/gcc/testsuite/gdc.test/compilable/uda.d index ac66c2f..aa6277b 100644 --- a/gcc/testsuite/gdc.test/compilable/uda.d +++ b/gcc/testsuite/gdc.test/compilable/uda.d @@ -6,3 +6,9 @@ struct foo { } @foo bar () { } /************************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23241 + +alias feynman = int; +enum get = __traits(getAttributes, feynman); +static assert(get.length == 0); diff --git a/gcc/testsuite/gdc.test/fail_compilation/aliasassign2.d b/gcc/testsuite/gdc.test/fail_compilation/aliasassign2.d new file mode 100644 index 0000000..dd421c9 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/aliasassign2.d @@ -0,0 +1,33 @@ +/* TEST_OUTPUT: +--- +fail_compilation/aliasassign2.d(16): Error: `alias aa1 = aa1;` cannot alias itself, use a qualified name to create an overload set +fail_compilation/aliasassign2.d(19): Error: template instance `aliasassign2.Tp1!()` error instantiating +fail_compilation/aliasassign2.d(24): Error: undefined identifier `unknown` +fail_compilation/aliasassign2.d(26): Error: template instance `aliasassign2.Tp2!()` error instantiating +fail_compilation/aliasassign2.d(31): Error: template instance `AliasSeqX!(aa3, 1)` template `AliasSeqX` is not defined, did you mean AliasSeq(T...)? +fail_compilation/aliasassign2.d(33): Error: template instance `aliasassign2.Tp3!()` error instantiating +--- +*/ + +alias AliasSeq(T...) = T; + +template Tp1() +{ + alias aa1 = aa1; + aa1 = AliasSeq!(aa1, float); +} +alias a1 = Tp1!(); + +template Tp2() +{ + alias aa2 = AliasSeq!(); + aa2 = AliasSeq!(aa2, unknown); +} +alias a2 = Tp2!(); + +template Tp3() +{ + alias aa3 = AliasSeq!(); + aa3 = AliasSeqX!(aa3, 1); +} +alias a3 = Tp3!(); diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag23295.d b/gcc/testsuite/gdc.test/fail_compilation/diag23295.d new file mode 100644 index 0000000..a0bfe88 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag23295.d @@ -0,0 +1,40 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/diag23295.d(21): Error: scope variable `x` assigned to non-scope parameter `y` calling `foo` +fail_compilation/diag23295.d(32): which is assigned to non-scope parameter `z` +fail_compilation/diag23295.d(34): which is not `scope` because of `f = & z` +fail_compilation/diag23295.d(24): Error: scope variable `ex` assigned to non-scope parameter `e` calling `thro` +fail_compilation/diag23295.d(39): which is not `scope` because of `throw e` +--- +*/ + +// explain why scope inference failed +// https://issues.dlang.org/show_bug.cgi?id=23295 + +@safe: + +void main() +{ + scope int* x; + foo(x, null); + + scope Exception ex; + thro(ex); +} + +auto foo(int* y, int** w) +{ + fooImpl(y, null); +} + +auto fooImpl(int* z, int** w) +{ + auto f = &z; +} + +auto thro(Exception e) +{ + throw e; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d index d9f554a..e969b24 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d @@ -1,26 +1,27 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10968.d(41): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(41): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(42): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(42): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(43): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(43): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(46): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(46): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(30): `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_arrayassign_l!(SA[], SA)._d_arrayassign_l` fail_compilation/fail10968.d(47): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(47): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(47): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` +fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here 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(29): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor` +fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` +fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(30): `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_arrayctor!(SA[], SA)._d_arrayctor` --- */ @@ -51,12 +52,12 @@ void bar() pure @safe /* TEST_OUTPUT: --- -fail_compilation/fail10968.d(74): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit fail_compilation/fail10968.d(75): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit fail_compilation/fail10968.d(76): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit -fail_compilation/fail10968.d(79): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit +fail_compilation/fail10968.d(77): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit fail_compilation/fail10968.d(80): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit fail_compilation/fail10968.d(81): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit +fail_compilation/fail10968.d(82): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14669.d b/gcc/testsuite/gdc.test/fail_compilation/fail14669.d index be72663..5621ecc 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail14669.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail14669.d @@ -20,9 +20,9 @@ void test1() /* TEST_OUTPUT: --- -fail_compilation/fail14669.d(29): Error: `auto` can only be used as part of `auto ref` for template function parameters +fail_compilation/fail14669.d(29): Error: cannot explicitly instantiate template function with `auto ref` parameter fail_compilation/fail14669.d(38): Error: template instance `fail14669.bar1!int` error instantiating -fail_compilation/fail14669.d(30): Error: `auto` can only be used as part of `auto ref` for template function parameters +fail_compilation/fail14669.d(30): Error: cannot explicitly instantiate template function with `auto ref` parameter fail_compilation/fail14669.d(40): Error: template instance `fail14669.bar2!int` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d index 5d7d6dd..a30a65b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(` fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File` -fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `)` +fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (0` fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/import15525.d b/gcc/testsuite/gdc.test/fail_compilation/imports/import15525.d new file mode 100644 index 0000000..9c05e7c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/import15525.d @@ -0,0 +1,3 @@ +module imports.import15525; + +template Tuple{ static if } diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue12652.d b/gcc/testsuite/gdc.test/fail_compilation/issue12652.d new file mode 100644 index 0000000..0ddd6b4 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/issue12652.d @@ -0,0 +1,24 @@ +/* +TEST_OUTPUT: +---- +fail_compilation/issue12652.d(18): Error: static initializations of associative arrays is not allowed. +fail_compilation/issue12652.d(18): associative arrays must be initialized at runtime: https://dlang.org/spec/hash-map.html#runtime_initialization +--- +*/ + +enum A +{ + x, + y, + z +} + +struct S +{ + string[A] t = [A.x : "aaa", A.y : "bbb"]; +} + +void main () +{ + S s; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d index a8e5de5..95d5dbe 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d @@ -78,6 +78,7 @@ void foo() @safe fail_compilation/retscope6.d(8016): Error: address of variable `i` assigned to `p` with longer lifetime fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `betty` fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `betty` +fail_compilation/retscope6.d(8021): which is assigned to non-scope parameter `p` fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `archie` --- */ @@ -255,6 +256,7 @@ void escape_throw_20150() @safe /* TEST_OUTPUT: --- fail_compilation/retscope6.d(14019): Error: scope variable `scopePtr` assigned to non-scope parameter `x` calling `noInfer23021` +fail_compilation/retscope6.d(14009): which is not `scope` because of `*escapeHole = cast(const(int)*)x` fail_compilation/retscope6.d(14022): Error: scope variable `scopePtr` may not be returned --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/shared.d b/gcc/testsuite/gdc.test/fail_compilation/shared.d index ab6f540..7d15b16 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/shared.d +++ b/gcc/testsuite/gdc.test/fail_compilation/shared.d @@ -225,3 +225,14 @@ auto ref Object test_inference_4(const return shared ref Object a) { return a; } + +// https://issues.dlang.org/show_bug.cgi?id=23226 +// Allow accessing non-shared `this` +struct BitRange +{ + int bits; + void f() + { + this.bits++; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15525.d b/gcc/testsuite/gdc.test/fail_compilation/test15525.d new file mode 100644 index 0000000..cd35f30 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test15525.d @@ -0,0 +1,17 @@ +// https://issues.dlang.org/show_bug.cgi?id=15525 + +/* +TEST_OUTPUT: +--- +fail_compilation/imports/import15525.d(3): Error: parenthesized template parameter list expected following template identifier +fail_compilation/imports/import15525.d(3): Error: (expression) expected following `static if` +fail_compilation/imports/import15525.d(3): Error: declaration expected, not `}` +fail_compilation/test15525.d(16): Error: template instance `Tuple!()` template `Tuple` is not defined +--- +*/ + +struct CrashMe +{ + import imports.import15525; + Tuple!() crash; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17423.d b/gcc/testsuite/gdc.test/fail_compilation/test17423.d index 3afb63b..faa9806 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17423.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17423.d @@ -1,7 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17423.d(26): Error: reference to local `this` assigned to non-scope parameter `dlg` calling `opApply` +fail_compilation/test17423.d(27): Error: reference to local `this` assigned to non-scope parameter `dlg` calling `opApply` +fail_compilation/test17423.d(16): which is not `scope` because of `this.myDlg = dlg` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17764.d b/gcc/testsuite/gdc.test/fail_compilation/test17764.d new file mode 100644 index 0000000..6ee988a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test17764.d @@ -0,0 +1,21 @@ +/* REQUIRED_ARGS: -preview=dip1000 + * TEST_OUTPUT: +--- +fail_compilation/test17764.d(109): Error: scope variable `c` assigned to non-scope `global` +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=17764 + +#line 100 + +int** global; + +struct S { int** str; } + +void f() @safe +{ + int* buf; + S[1] c = S(&buf); + global = c[0].str; /* This should be rejected. */ +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20245.d b/gcc/testsuite/gdc.test/fail_compilation/test20245.d index 1713c9d..98caa03 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test20245.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test20245.d @@ -2,15 +2,16 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test20245.d(20): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` -fail_compilation/test20245.d(21): Error: copying `&x` into allocated memory escapes a reference to parameter `x` -fail_compilation/test20245.d(22): Error: scope variable `a` may not be returned -fail_compilation/test20245.d(26): Error: cannot take address of `scope` variable `x` since `scope` applies to first indirection only -fail_compilation/test20245.d(32): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` -fail_compilation/test20245.d(33): Error: copying `&x` into allocated memory escapes a reference to parameter `x` -fail_compilation/test20245.d(49): Error: reference to local variable `price` assigned to non-scope `this.minPrice` -fail_compilation/test20245.d(68): Error: reference to local variable `this` assigned to non-scope parameter `msg` calling `this` -fail_compilation/test20245.d(88): Error: reference to local variable `this` assigned to non-scope parameter `content` calling `listUp` +fail_compilation/test20245.d(21): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` +fail_compilation/test20245.d(22): Error: copying `&x` into allocated memory escapes a reference to parameter `x` +fail_compilation/test20245.d(23): Error: scope variable `a` may not be returned +fail_compilation/test20245.d(27): Error: cannot take address of `scope` variable `x` since `scope` applies to first indirection only +fail_compilation/test20245.d(33): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` +fail_compilation/test20245.d(34): Error: copying `&x` into allocated memory escapes a reference to parameter `x` +fail_compilation/test20245.d(50): Error: reference to local variable `price` assigned to non-scope `this.minPrice` +fail_compilation/test20245.d(69): Error: reference to local variable `this` assigned to non-scope parameter `msg` calling `this` +fail_compilation/test20245.d(89): Error: reference to local variable `this` assigned to non-scope parameter `content` calling `listUp` +fail_compilation/test20245.d(82): which is not `scope` because of `charPtr = content` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20809.d b/gcc/testsuite/gdc.test/fail_compilation/test20809.d new file mode 100644 index 0000000..44728c5 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test20809.d @@ -0,0 +1,44 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/test20809.d(114): Deprecation: returning `this.a` escapes a reference to parameter `this` +fail_compilation/test20809.d(112): perhaps annotate the function with `return` +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=20809 + +#line 100 + +@safe: + +struct S +{ + @safe: + int a; + ~this() + { + a = 0; + } + + ref int val() + { + return a; + } +} + +S bar() +{ + return S(2); +} + +int foo() +{ + return bar.val; +} + +void test() +{ + assert(foo() == 2); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23073.d b/gcc/testsuite/gdc.test/fail_compilation/test23073.d new file mode 100644 index 0000000..39106ba --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test23073.d @@ -0,0 +1,35 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test23073.d(28): Error: scope variable `c` assigned to non-scope parameter `c` calling `assignNext` +fail_compilation/test23073.d(22): which is not `scope` because of `c.next = c` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23073 +// scope inference from pure doesn't consider self-assignment + +@safe: + +class C +{ + C next; +} + +void assignNext(C c) pure nothrow @nogc +{ + c.next = c; +} + +C escape() @nogc +{ + scope C c = new C(); + assignNext(c); + return c.next; +} + +void main() +{ + C dangling = escape(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/testsemi.d b/gcc/testsuite/gdc.test/fail_compilation/testsemi.d new file mode 100644 index 0000000..fc9bc65 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/testsemi.d @@ -0,0 +1,46 @@ +/* TEST_OUTPUT: +--- +fail_compilation/testsemi.d(102): Error: found `int` when expecting `;` following static assert +fail_compilation/testsemi.d(102): Error: no identifier for declarator `x` +fail_compilation/testsemi.d(109): Error: found `alias` when expecting `;` following alias reassignment +fail_compilation/testsemi.d(112): Error: found `}` when expecting `;` following invariant +fail_compilation/testsemi.d(117): Error: found `int` when expecting `;` following `alias Identifier this` +fail_compilation/testsemi.d(117): Error: no identifier for declarator `x` +fail_compilation/testsemi.d(123): Error: found `int` when expecting `;` following mixin +fail_compilation/testsemi.d(129): Error: found `int` when expecting `;` following `import` Expression +fail_compilation/testsemi.d(131): Error: `}` expected following members in `class` declaration at fail_compilation/testsemi.d(112) +--- + */ + +#line 100 + +static assert(1) +int x; + +template map(alias F, Args...) +{ + alias A = AliasSeq!(); + static foreach (Arg; Args) + A = AliasSeq!(A, F!Arg) + alias staticMap = A; +} + +class C { invariant(3) } + +class D +{ + alias x this + int x; +} + +void test1() +{ + mixin("int x;") + int y; +} + +void test2() +{ + import(1) + int z; +} diff --git a/gcc/testsuite/gdc.test/runnable/test20365.d b/gcc/testsuite/gdc.test/runnable/test20365.d new file mode 100644 index 0000000..71865a5 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test20365.d @@ -0,0 +1,21 @@ +// https://issues.dlang.org/show_bug.cgi?id=20365 + +string result = ""; + +struct S +{ + long[3] a; + this(ref typeof(this)) { result ~= "C"; } +} + +void fun() +{ + S[4] a; + auto b = a; +} + +void main() +{ + fun(); + assert(result == "CCCC"); +} diff --git a/gcc/testsuite/gdc.test/runnable/test20809.d b/gcc/testsuite/gdc.test/runnable/test20809.d new file mode 100644 index 0000000..6d6191e --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test20809.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=20809 + + +@safe: +struct S{ + @safe: + int[8] a; + ~this(){ a[] = 0; } + ref val(){ return a; } +} +S bar(){ return S([2,2,2,2,2,2,2,2]); } +int[8] foo(){ return bar.val; } + +void main(){ assert(foo() == [2,2,2,2,2,2,2,2]); } // error diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index c358b69..85fc49d 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -d7772a236983ec37b92d21b28bad3cd2de57b945 +817610b16d0f0f469b9fbb28c000956fb910c43f The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 2e1e91d..d828749 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -171,17 +171,18 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \ core/exception.d core/gc/config.d core/gc/gcinterface.d \ core/gc/registry.d core/int128.d core/internal/abort.d \ - core/internal/array/appending.d core/internal/array/capacity.d \ - core/internal/array/casting.d core/internal/array/comparison.d \ - core/internal/array/concatenation.d core/internal/array/construction.d \ - core/internal/array/duplication.d core/internal/array/equality.d \ - core/internal/array/operations.d core/internal/array/utils.d \ - core/internal/atomic.d core/internal/attributes.d \ - core/internal/container/array.d core/internal/container/common.d \ - core/internal/container/hashtab.d core/internal/container/treap.d \ - core/internal/convert.d core/internal/dassert.d \ - core/internal/destruction.d core/internal/entrypoint.d \ - core/internal/gc/bits.d core/internal/gc/impl/conservative/gc.d \ + core/internal/array/appending.d core/internal/array/arrayassign.d \ + core/internal/array/capacity.d core/internal/array/casting.d \ + core/internal/array/comparison.d core/internal/array/concatenation.d \ + core/internal/array/construction.d core/internal/array/duplication.d \ + core/internal/array/equality.d core/internal/array/operations.d \ + core/internal/array/utils.d core/internal/atomic.d \ + core/internal/attributes.d core/internal/container/array.d \ + core/internal/container/common.d core/internal/container/hashtab.d \ + core/internal/container/treap.d core/internal/convert.d \ + core/internal/dassert.d core/internal/destruction.d \ + core/internal/entrypoint.d core/internal/gc/bits.d \ + core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index de6656c..57660ee 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -192,6 +192,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/demangle.lo core/exception.lo core/gc/config.lo \ core/gc/gcinterface.lo core/gc/registry.lo core/int128.lo \ core/internal/abort.lo core/internal/array/appending.lo \ + core/internal/array/arrayassign.lo \ core/internal/array/capacity.lo core/internal/array/casting.lo \ core/internal/array/comparison.lo \ core/internal/array/concatenation.lo \ @@ -839,17 +840,18 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \ core/exception.d core/gc/config.d core/gc/gcinterface.d \ core/gc/registry.d core/int128.d core/internal/abort.d \ - core/internal/array/appending.d core/internal/array/capacity.d \ - core/internal/array/casting.d core/internal/array/comparison.d \ - core/internal/array/concatenation.d core/internal/array/construction.d \ - core/internal/array/duplication.d core/internal/array/equality.d \ - core/internal/array/operations.d core/internal/array/utils.d \ - core/internal/atomic.d core/internal/attributes.d \ - core/internal/container/array.d core/internal/container/common.d \ - core/internal/container/hashtab.d core/internal/container/treap.d \ - core/internal/convert.d core/internal/dassert.d \ - core/internal/destruction.d core/internal/entrypoint.d \ - core/internal/gc/bits.d core/internal/gc/impl/conservative/gc.d \ + core/internal/array/appending.d core/internal/array/arrayassign.d \ + core/internal/array/capacity.d core/internal/array/casting.d \ + core/internal/array/comparison.d core/internal/array/concatenation.d \ + core/internal/array/construction.d core/internal/array/duplication.d \ + core/internal/array/equality.d core/internal/array/operations.d \ + core/internal/array/utils.d core/internal/atomic.d \ + core/internal/attributes.d core/internal/container/array.d \ + core/internal/container/common.d core/internal/container/hashtab.d \ + core/internal/container/treap.d core/internal/convert.d \ + core/internal/dassert.d core/internal/destruction.d \ + core/internal/entrypoint.d core/internal/gc/bits.d \ + core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ @@ -1201,6 +1203,8 @@ core/internal/array/$(am__dirstamp): @$(MKDIR_P) core/internal/array @: > core/internal/array/$(am__dirstamp) core/internal/array/appending.lo: core/internal/array/$(am__dirstamp) +core/internal/array/arrayassign.lo: \ + core/internal/array/$(am__dirstamp) core/internal/array/capacity.lo: core/internal/array/$(am__dirstamp) core/internal/array/casting.lo: core/internal/array/$(am__dirstamp) core/internal/array/comparison.lo: \ diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d index cb8d433..ca87f90 100644 --- a/libphobos/libdruntime/core/demangle.d +++ b/libphobos/libdruntime/core/demangle.d @@ -2328,7 +2328,7 @@ char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @property bool empty() const { return !s.length; } - @property const(char)[] front() const return + @property const(char)[] front() const return scope { immutable i = indexOfDot(); return i == -1 ? s[0 .. $] : s[0 .. i]; diff --git a/libphobos/libdruntime/core/exception.d b/libphobos/libdruntime/core/exception.d index 81aa43b..a05a24c 100644 --- a/libphobos/libdruntime/core/exception.d +++ b/libphobos/libdruntime/core/exception.d @@ -14,7 +14,7 @@ void __switch_errorT()(string file = __FILE__, size_t line = __LINE__) @trusted { // Consider making this a compile time check. version (D_Exceptions) - throw staticError!SwitchError(file, line, null); + throw staticError!SwitchError("No appropriate switch clause found", file, line, null); else assert(0, "No appropriate switch clause found"); } @@ -446,16 +446,16 @@ class ForkError : Error */ class SwitchError : Error { - @safe pure nothrow @nogc this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) + @safe pure nothrow @nogc this( string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) { - super( "No appropriate switch clause found", file, line, next ); + super( msg, file, line, next ); } } unittest { { - auto se = new SwitchError(); + auto se = new SwitchError("No appropriate switch clause found"); assert(se.file == __FILE__); assert(se.line == __LINE__ - 2); assert(se.next is null); @@ -463,7 +463,7 @@ unittest } { - auto se = new SwitchError("hello", 42, new Exception("It's an Exception!")); + auto se = new SwitchError("No appropriate switch clause found", "hello", 42, new Exception("It's an Exception!")); assert(se.file == "hello"); assert(se.line == 42); assert(se.next !is null); diff --git a/libphobos/libdruntime/core/internal/array/arrayassign.d b/libphobos/libdruntime/core/internal/array/arrayassign.d new file mode 100644 index 0000000..6132e68 --- /dev/null +++ b/libphobos/libdruntime/core/internal/array/arrayassign.d @@ -0,0 +1,304 @@ +module core.internal.array.arrayassign; + +// Force `enforceRawArraysConformable` to remain `pure` `@nogc` +private void enforceRawArraysConformable(const char[] action, const size_t elementSize, + const void[] a1, const void[] a2, const bool allowOverlap) @trusted @nogc pure nothrow +{ + import core.internal.util.array : enforceRawArraysConformable; + + alias Type = void function(const char[] action, const size_t elementSize, + const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow; + (cast(Type)&enforceRawArraysConformable)(action, elementSize, a1, a2, allowOverlap); +} + +private template CopyElem(string CopyAction) +{ + const char[] CopyElem = "{\n" ~ q{ + memcpy(&tmp, cast(void*) &dst, elemSize); + } ~ CopyAction ~ q{ + auto elem = cast(Unqual!T*) &tmp; + destroy(*elem); + } ~ "}\n"; +} + +private template CopyArray(bool CanOverlap, string CopyAction) +{ + const char[] CopyArray = CanOverlap ? q{ + if (vFrom.ptr < vTo.ptr && vTo.ptr < vFrom.ptr + elemSize * vFrom.length) + foreach_reverse (i, ref dst; to) + } ~ CopyElem!(CopyAction) ~ q{ + else + foreach (i, ref dst; to) + } ~ CopyElem!(CopyAction) + : q{ + foreach (i, ref dst; to) + } ~ CopyElem!(CopyAction); +} + +private template ArrayAssign(string CopyLogic, string AllowOverLap) +{ + const char[] ArrayAssign = q{ + import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.lifetime : copyEmplace; + import core.stdc.string : memcpy; + + void[] vFrom = (cast(void*) from.ptr)[0 .. from.length]; + void[] vTo = (cast(void*) to.ptr)[0 .. to.length]; + enum elemSize = T.sizeof; + + enforceRawArraysConformable("copy", elemSize, vFrom, vTo, } ~ AllowOverLap ~ q{); + + void[elemSize] tmp = void; + + } ~ CopyLogic ~ q{ + + return to; + }; +} + +/** + * Does array assignment (not construction) from another array of the same + * element type. Handles overlapping copies. Assumes the right hand side is an + * lvalue, + * + * Used for static array assignment with non-POD element types: + * --- + * struct S + * { + * ~this() {} // destructor, so not Plain Old Data + * } + * + * void main() + * { + * S[3] arr; + * S[3] lvalue; + * + * arr = lvalue; + * // Generates: + * // _d_arrayassign_l(arr[], lvalue[]), arr; + * } + * --- + * + * Params: + * to = destination array + * from = source array + * Returns: + * `to` + */ +Tarr _d_arrayassign_l(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +{ + mixin(ArrayAssign!(q{ + static if (hasElaborateCopyConstructor!T) + } ~ CopyArray!(true, "copyEmplace(from[i], dst);") ~ q{ + else + } ~ CopyArray!(true, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"), + "true")); +} + +@safe unittest +{ + int counter; + struct S + { + int val; + this(int val) { this.val = val; } + this(const scope ref S rhs) + { + val = rhs.val; + counter++; + } + } + + S[4] arr1; + S[4] arr2 = [S(0), S(1), S(2), S(3)]; + _d_arrayassign_l(arr1[], arr2[]); + + assert(counter == 4); + assert(arr1 == arr2); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S + { + int val; + this(int val) { this.val = val; } + this(const scope ref S rhs) + { + val = rhs.val; + counter++; + } + } + + S[4] arr1; + S[4] arr2 = [S(0), S(1), S(2), S(3)]; + _d_arrayassign_l(arr1[], arr2[]); + + assert(counter == 4); + assert(arr1 == arr2); +} + +@safe nothrow unittest +{ + // Test that throwing works + int counter; + bool didThrow; + + struct Throw + { + int val; + this(this) + { + counter++; + if (counter == 2) + throw new Exception(""); + } + } + try + { + Throw[4] a; + Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; + _d_arrayassign_l(a[], b[]); + } + catch (Exception) + { + didThrow = true; + } + assert(didThrow); + assert(counter == 2); + + + // Test that `nothrow` works + didThrow = false; + counter = 0; + struct NoThrow + { + int val; + this(this) + { + counter++; + } + } + try + { + NoThrow[4] a; + NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)]; + _d_arrayassign_l(a[], b[]); + } + catch (Exception) + { + didThrow = false; + } + assert(!didThrow); + assert(counter == 4); +} + +/** + * Does array assignment (not construction) from another array of the same + * element type. Does not support overlapping copies. Assumes the right hand + * side is an rvalue, + * + * Used for static array assignment with non-POD element types: + * --- + * struct S + * { + * ~this() {} // destructor, so not Plain Old Data + * } + * + * void main() + * { + * S[3] arr; + * S[3] getRvalue() {return lvalue;} + * + * arr = getRvalue(); + * // Generates: + * // (__appendtmp = getRvalue), _d_arrayassign_l(arr[], __appendtmp), arr; + * } + * --- + * + * Params: + * to = destination array + * from = source array + * Returns: + * `to` + */ +Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +{ + mixin(ArrayAssign!( + CopyArray!(false, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"), + "false")); +} + +@safe unittest +{ + int counter; + struct S + { + int val; + this(int val) { this.val = val; } + this(const scope ref S rhs) + { + val = rhs.val; + counter++; + } + } + + S[4] arr1; + S[4] arr2 = [S(0), S(1), S(2), S(3)]; + _d_arrayassign_r(arr1[], arr2[]); + + assert(counter == 0); + assert(arr1 == arr2); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S + { + int val; + this(int val) { this.val = val; } + this(const scope ref S rhs) + { + val = rhs.val; + counter++; + } + } + + S[4] arr1; + S[4] arr2 = [S(0), S(1), S(2), S(3)]; + _d_arrayassign_r(arr1[], arr2[]); + + assert(counter == 0); + assert(arr1 == arr2); +} + +@safe nothrow unittest +{ + // Test that `nothrow` works + bool didThrow = false; + int counter = 0; + struct NoThrow + { + int val; + this(this) + { + counter++; + } + } + try + { + NoThrow[4] a; + NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)]; + _d_arrayassign_r(a[], b[]); + } + catch (Exception) + { + didThrow = false; + } + assert(!didThrow); + assert(counter == 0); +} diff --git a/libphobos/libdruntime/core/internal/array/equality.d b/libphobos/libdruntime/core/internal/array/equality.d index d3fdd65..c110d64 100644 --- a/libphobos/libdruntime/core/internal/array/equality.d +++ b/libphobos/libdruntime/core/internal/array/equality.d @@ -236,6 +236,33 @@ unittest static assert(!useMemcmp!(int[], int[])); } +// https://issues.dlang.org/show_bug.cgi?id=21094 +unittest +{ + static class C + { + int a; + } + static struct S + { + bool isValid; + C fib; + + inout(C) get() pure @safe @nogc nothrow inout + { + return isValid ? fib : C.init; + } + T opCast(T : C)() const { return null; } + + alias get this; + } + + auto foo(S[] lhs, S[] rhs) + { + return lhs == rhs; + } +} + // Returns a reference to an array element, eliding bounds check and // casting void to ubyte. pragma(inline, true) diff --git a/libphobos/libdruntime/core/sys/posix/sys/socket.d b/libphobos/libdruntime/core/sys/posix/sys/socket.d index 3a7b753..fc5dc5d 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/socket.d +++ b/libphobos/libdruntime/core/sys/posix/sys/socket.d @@ -188,10 +188,40 @@ version (linux) extern (D) inout(ubyte)* CMSG_DATA( return scope inout(cmsghdr)* cmsg ) pure nothrow @nogc { return cast(ubyte*)( cmsg + 1 ); } - private inout(cmsghdr)* __cmsg_nxthdr(inout(msghdr)*, inout(cmsghdr)*) pure nothrow @nogc; - extern (D) inout(cmsghdr)* CMSG_NXTHDR(inout(msghdr)* msg, inout(cmsghdr)* cmsg) pure nothrow @nogc + version (CRuntime_Musl) { - return __cmsg_nxthdr(msg, cmsg); + extern (D) + { + private size_t __CMSG_LEN(inout(cmsghdr)* cmsg) pure nothrow @nogc + { + return (cmsg.cmsg_len + size_t.sizeof -1) & cast(size_t)(~(size_t.sizeof - 1)); + } + + private inout(cmsghdr)* __CMSG_NEXT(inout(cmsghdr)* cmsg) pure nothrow @nogc + { + return cmsg + __CMSG_LEN(cmsg); + } + + private inout(msghdr)* __MHDR_END(inout(msghdr)* mhdr) pure nothrow @nogc + { + return cast(inout(msghdr)*)(mhdr.msg_control + mhdr.msg_controllen); + } + + inout(cmsghdr)* CMSG_NXTHDR(inout(msghdr)* msg, inout(cmsghdr)* cmsg) pure nothrow @nogc + { + return cmsg.cmsg_len < cmsghdr.sizeof || + __CMSG_LEN(cmsg) + cmsghdr.sizeof >= __MHDR_END(msg) - cast(inout(msghdr)*)(cmsg) + ? cast(inout(cmsghdr)*) null : cast(inout(cmsghdr)*) __CMSG_NEXT(cmsg); + } + } + } + else + { + private inout(cmsghdr)* __cmsg_nxthdr(inout(msghdr)*, inout(cmsghdr)*) pure nothrow @nogc; + extern (D) inout(cmsghdr)* CMSG_NXTHDR(inout(msghdr)* msg, inout(cmsghdr)* cmsg) pure nothrow @nogc + { + return __cmsg_nxthdr(msg, cmsg); + } } extern (D) inout(cmsghdr)* CMSG_FIRSTHDR( inout(msghdr)* mhdr ) pure nothrow @nogc diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 8ef65489..d842499 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -6,6 +6,7 @@ * $(TR $(TD Arrays) $(TD * $(MYREF assumeSafeAppend) * $(MYREF capacity) + * $(A #.dup.2, $(TT dup)) * $(MYREF idup) * $(MYREF reserve) * )) @@ -14,6 +15,7 @@ * $(MYREF byKeyValue) * $(MYREF byValue) * $(MYREF clear) + * $(MYREF dup) * $(MYREF get) * $(MYREF keys) * $(MYREF rehash) @@ -23,15 +25,15 @@ * )) * $(TR $(TD General) $(TD * $(MYREF destroy) - * $(MYREF dup) * $(MYREF hashOf) - * $(MYREF opEquals) + * $(MYREF imported) + * $(MYREF noreturn) * )) - * $(TR $(TD Types) $(TD + * $(TR $(TD Classes) $(TD * $(MYREF Error) * $(MYREF Exception) - * $(MYREF noreturn) * $(MYREF Object) + * $(MYREF opEquals) * $(MYREF Throwable) * )) * $(TR $(TD Type info) $(TD @@ -61,7 +63,11 @@ alias size_t = typeof(int.sizeof); alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0); alias sizediff_t = ptrdiff_t; // For backwards compatibility only. -alias noreturn = typeof(*null); /// bottom type +/** + * Bottom type. + * See $(DDSUBLINK spec/type, noreturn). + */ +alias noreturn = typeof(*null); alias hash_t = size_t; // For backwards compatibility only. alias equals_t = bool; // For backwards compatibility only. @@ -266,7 +272,9 @@ class Object the typeinfo name string compare. This is because of dmd's dll implementation. However, it can infer to @safe if your class' opEquals is. +/ -bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) if (is(LHS : const Object) && is(RHS : const Object)) +bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) +if ((is(LHS : const Object) || is(LHS : const shared Object)) && + (is(RHS : const Object) || is(RHS : const shared Object))) { static if (__traits(compiles, lhs.opEquals(rhs)) && __traits(compiles, rhs.opEquals(lhs))) { @@ -505,6 +513,16 @@ unittest assert(obj1 != obj2); } +// https://issues.dlang.org/show_bug.cgi?id=23291 +@system unittest +{ + static shared class C { bool opEquals(const(shared(C)) rhs) const shared { return true;}} + const(C) c = new C(); + const(C)[] a = [c]; + const(C)[] b = [c]; + assert(a[0] == b[0]); +} + private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; void setSameMutex(shared Object ownee, shared Object owner) @@ -3473,13 +3491,18 @@ ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); })); /*********************************** - * Looks up key; if it exists applies the update callable else evaluates the - * create callable and adds it to the associative array + * Calls `create` if `key` doesn't exist in the associative array, + * otherwise calls `update`. + * `create` returns a corresponding value for `key`. + * `update` accepts a key parameter. If it returns a value, the value is + * set for `key`. * Params: * aa = The associative array. * key = The key. - * create = The callable to apply on create. - * update = The callable to apply on update. + * create = The callable to create a value for `key`. + * Must return V. + * update = The callable to call if `key` exists. + * Takes a K argument, returns a V or void. */ void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void))) @@ -3509,23 +3532,39 @@ if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof } /// -@system unittest +@safe unittest { - auto aa = ["k1": 1]; + int[string] aa; - aa.update("k1", { - return -1; // create (won't be executed) - }, (ref int v) { - v += 1; // update - }); - assert(aa["k1"] == 2); - - aa.update("k2", { - return 0; // create - }, (ref int v) { - v = -1; // update (won't be executed) - }); - assert(aa["k2"] == 0); + // create + aa.update("key", + () => 1, + (int) {} // not executed + ); + assert(aa["key"] == 1); + + // update value by ref + aa.update("key", + () => 0, // not executed + (ref int v) { + v += 1; + }); + assert(aa["key"] == 2); + + // update from return value + aa.update("key", + () => 0, // not executed + (int v) => v * 2 + ); + assert(aa["key"] == 4); + + // 'update' without changing value + aa.update("key", + () => 0, // not executed + (int) { + // do something else + }); + assert(aa["key"] == 4); } @safe unittest @@ -4576,6 +4615,8 @@ public import core.internal.array.casting: __ArrayCast; public import core.internal.array.concatenation : _d_arraycatnTXImpl; 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; +public import core.internal.array.arrayassign : _d_arrayassign_r; public import core.internal.array.capacity: _d_arraysetlengthTImpl; public import core.internal.dassert: _d_assert_fail; diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d index c9db2fc..c9e2b15 100644 --- a/libphobos/libdruntime/rt/arrayassign.d +++ b/libphobos/libdruntime/rt/arrayassign.d @@ -19,171 +19,6 @@ private debug(PRINTF) import core.stdc.stdio; } -/* - * Superseded array assignment hook. Does not take into account destructors: - * https://issues.dlang.org/show_bug.cgi?id=13661 - * Kept for backward binary compatibility. This function can be removed in the future. - */ -extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) -{ - debug(PRINTF) printf("_d_arrayassign(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); - - immutable elementSize = ti.tsize; - - // Need a temporary buffer tmp[] big enough to hold one element - void[16] buf = void; - void* ptmp = (elementSize > buf.sizeof) ? malloc(elementSize) : buf.ptr; - scope (exit) - { - if (ptmp != buf.ptr) - free(ptmp); - } - return _d_arrayassign_l(ti, from, to, ptmp); -} - -/** -Does array assignment (not construction) from another array of the same -element type. - -Handles overlapping copies. - -The `_d_arrayassign_l` variant assumes the right hand side is an lvalue, -while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors. - -Used for static array assignment with non-POD element types: ---- -struct S -{ - ~this() {} // destructor, so not Plain Old Data -} - -void main() -{ - S[3] arr; - S[3] lvalue; - - arr = lvalue; - // Generates: - // S _tmp; - // _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp); - - S[3] getRvalue() {return lvalue;} - arr = getRvalue(); - // Similar, but `_d_arrayassign_r` -} ---- - -Params: - ti = `TypeInfo` of the array element type. - dst = target memory. Its `.length` is equal to the element count, not byte length. - src = source memory. Its `.length` is equal to the element count, not byte length. - ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes. -Returns: `dst` -*/ -extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) -{ - debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); - - immutable elementSize = ti.tsize; - - enforceRawArraysConformable("copy", elementSize, src, dst, true); - - if (src.ptr < dst.ptr && dst.ptr < src.ptr + elementSize * src.length) - { - // If dst is in the middle of src memory, use reverse order. - for (auto i = dst.length; i--; ) - { - void* pdst = dst.ptr + i * elementSize; - void* psrc = src.ptr + i * elementSize; - memcpy(ptmp, pdst, elementSize); - memcpy(pdst, psrc, elementSize); - ti.postblit(pdst); - ti.destroy(ptmp); - } - } - else - { - // Otherwise, use normal order. - foreach (i; 0 .. dst.length) - { - void* pdst = dst.ptr + i * elementSize; - void* psrc = src.ptr + i * elementSize; - memcpy(ptmp, pdst, elementSize); - memcpy(pdst, psrc, elementSize); - ti.postblit(pdst); - ti.destroy(ptmp); - } - } - return dst; -} - -unittest // Bugzilla 14024 -{ - string op; - - struct S - { - char x = 'x'; - this(this) { op ~= x-0x20; } // upper case - ~this() { op ~= x; } // lower case - } - - S[4] mem; - ref S[2] slice(int a, int b) { return mem[a .. b][0 .. 2]; } - - op = null; - mem[0].x = 'a'; - mem[1].x = 'b'; - mem[2].x = 'x'; - mem[3].x = 'y'; - slice(0, 2) = slice(2, 4); // [ab] = [xy] - assert(op == "XaYb", op); - - op = null; - mem[0].x = 'x'; - mem[1].x = 'y'; - mem[2].x = 'a'; - mem[3].x = 'b'; - slice(2, 4) = slice(0, 2); // [ab] = [xy] - assert(op == "XaYb", op); - - op = null; - mem[0].x = 'a'; - mem[1].x = 'b'; - mem[2].x = 'c'; - slice(0, 2) = slice(1, 3); // [ab] = [bc] - assert(op == "BaCb", op); - - op = null; - mem[0].x = 'x'; - mem[1].x = 'y'; - mem[2].x = 'z'; - slice(1, 3) = slice(0, 2); // [yz] = [xy] - assert(op == "YzXy", op); -} - -/// ditto -extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) -{ - debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); - - immutable elementSize = ti.tsize; - - enforceRawArraysConformable("copy", elementSize, src, dst, false); - - // Always use normal order, because we can assume that - // the rvalue src has no overlapping with dst. - foreach (i; 0 .. dst.length) - { - void* pdst = dst.ptr + i * elementSize; - void* psrc = src.ptr + i * elementSize; - memcpy(ptmp, pdst, elementSize); - memcpy(pdst, psrc, elementSize); - ti.destroy(ptmp); - } - return dst; -} - /** Set all elements of an array to a single value. diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 1f0cfbf..c8a3771 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -5748ca43fd5c3e31ce7a8511f542b67e5d5a3dc6 +b578dfad94770574d7e522557a77276c35943daa The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am index da7a200..2413024 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -127,11 +127,10 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \ std/experimental/logger/core.d std/experimental/logger/filelogger.d \ std/experimental/logger/multilogger.d \ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \ - std/experimental/typecons.d std/file.d std/format/internal/floats.d \ - std/format/internal/read.d std/format/internal/write.d \ - std/format/package.d std/format/read.d std/format/spec.d \ - std/format/write.d std/functional.d std/getopt.d std/int128.d \ - std/internal/attributes.d std/internal/cstring.d \ + std/file.d std/format/internal/floats.d std/format/internal/read.d \ + std/format/internal/write.d std/format/package.d std/format/read.d \ + std/format/spec.d std/format/write.d std/functional.d std/getopt.d \ + std/int128.d std/internal/attributes.d std/internal/cstring.d \ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ std/internal/memory.d std/internal/scopebuffer.d \ @@ -139,7 +138,9 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \ std/internal/test/uda.d std/internal/unicode_comp.d \ std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \ std/internal/unicode_norm.d std/internal/unicode_tables.d \ - std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \ + std/internal/windows/advapi32.d std/json.d std/logger/core.d \ + std/logger/filelogger.d std/logger/multilogger.d \ + std/logger/nulllogger.d std/logger/package.d std/math/algebraic.d \ std/math/constants.d std/math/exponential.d std/math/hardware.d \ std/math/operations.d std/math/package.d std/math/remainder.d \ std/math/rounding.d std/math/traits.d std/math/trigonometry.d \ diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index 6f58fee..562a428 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -218,7 +218,6 @@ am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/multilogger.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/package.lo \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/floats.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.lo \ @@ -246,7 +245,11 @@ am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_tables.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.lo \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo std/logger/core.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/filelogger.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/multilogger.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/nulllogger.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/package.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/algebraic.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/exponential.lo \ @@ -589,11 +592,10 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/core.d std/experimental/logger/filelogger.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/multilogger.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.d std/file.d std/format/internal/floats.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.d std/format/internal/write.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/package.d std/format/read.d std/format/spec.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/write.d std/functional.d std/getopt.d std/int128.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.d std/internal/cstring.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.d std/format/internal/floats.d std/format/internal/read.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/write.d std/format/package.d std/format/read.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/spec.d std/format/write.d std/functional.d std/getopt.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/int128.d std/internal/attributes.d std/internal/cstring.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/memory.d std/internal/scopebuffer.d \ @@ -601,7 +603,9 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/uda.d std/internal/unicode_comp.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.d std/internal/unicode_tables.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/logger/core.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/filelogger.d std/logger/multilogger.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/nulllogger.d std/logger/package.d std/math/algebraic.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.d std/math/exponential.d std/math/hardware.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/operations.d std/math/package.d std/math/remainder.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/rounding.d std/math/traits.d std/math/trigonometry.d \ @@ -830,7 +834,6 @@ std/experimental/logger/nulllogger.lo: \ std/experimental/logger/$(am__dirstamp) std/experimental/logger/package.lo: \ std/experimental/logger/$(am__dirstamp) -std/experimental/typecons.lo: std/experimental/$(am__dirstamp) std/file.lo: std/$(am__dirstamp) std/format/internal/$(am__dirstamp): @$(MKDIR_P) std/format/internal @@ -879,6 +882,14 @@ std/internal/windows/$(am__dirstamp): std/internal/windows/advapi32.lo: \ std/internal/windows/$(am__dirstamp) std/json.lo: std/$(am__dirstamp) +std/logger/$(am__dirstamp): + @$(MKDIR_P) std/logger + @: > std/logger/$(am__dirstamp) +std/logger/core.lo: std/logger/$(am__dirstamp) +std/logger/filelogger.lo: std/logger/$(am__dirstamp) +std/logger/multilogger.lo: std/logger/$(am__dirstamp) +std/logger/nulllogger.lo: std/logger/$(am__dirstamp) +std/logger/package.lo: std/logger/$(am__dirstamp) std/math/$(am__dirstamp): @$(MKDIR_P) std/math @: > std/math/$(am__dirstamp) @@ -994,6 +1005,8 @@ mostlyclean-compile: -rm -f std/internal/test/*.lo -rm -f std/internal/windows/*.$(OBJEXT) -rm -f std/internal/windows/*.lo + -rm -f std/logger/*.$(OBJEXT) + -rm -f std/logger/*.lo -rm -f std/math/*.$(OBJEXT) -rm -f std/math/*.lo -rm -f std/net/*.$(OBJEXT) @@ -1033,6 +1046,7 @@ clean-libtool: -rm -rf std/internal/math/.libs std/internal/math/_libs -rm -rf std/internal/test/.libs std/internal/test/_libs -rm -rf std/internal/windows/.libs std/internal/windows/_libs + -rm -rf std/logger/.libs std/logger/_libs -rm -rf std/math/.libs std/math/_libs -rm -rf std/net/.libs std/net/_libs -rm -rf std/range/.libs std/range/_libs @@ -1162,6 +1176,7 @@ distclean-generic: -rm -f std/internal/math/$(am__dirstamp) -rm -f std/internal/test/$(am__dirstamp) -rm -f std/internal/windows/$(am__dirstamp) + -rm -f std/logger/$(am__dirstamp) -rm -f std/math/$(am__dirstamp) -rm -f std/net/$(am__dirstamp) -rm -f std/range/$(am__dirstamp) diff --git a/libphobos/src/index.dd b/libphobos/src/index.dd index cbc173d..45c248e 100644 --- a/libphobos/src/index.dd +++ b/libphobos/src/index.dd @@ -459,6 +459,19 @@ $(BOOKTABLE , $(TDNW $(MREF core,simd)) $(TD SIMD intrinsics) ) + $(LEADINGROW Logging) + $(TR + $(TDNW + $(MREF std,logger)$(BR) + $(MREF std,logger,core)$(BR) + $(MREF std,logger,filelogger)$(BR) + $(MREF std,logger,multilogger)$(BR) + $(MREF std,logger,nulllogger)$(BR) + ) + $(TD + Logging. + ) + ) $(COMMENT $(LEADINGROW Undocumented modules (intentionally omitted).) @@ -509,18 +522,6 @@ $(COMMENT Deprecated modules. ) ) - $(TR - $(TDNW - $(MREF std,experimental,logger)$(BR) - $(MREF std,experimental,logger,core)$(BR) - $(MREF std,experimental,logger,filelogger)$(BR) - $(MREF std,experimental,logger,multilogger)$(BR) - $(MREF std,experimental,logger,nulllogger)$(BR) - ) - $(TD - Experimental modules. - ) - ) ) ) diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 300a897..3e828ce 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -1798,7 +1798,7 @@ if (isInputRange!R) assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ])); interface I {} - class C : I { override size_t toHash() const nothrow @safe { return 0; } } + static class C : I { override size_t toHash() const nothrow @safe { return 0; } } const C[] a4 = [new const C()]; auto g4 = a4.group!"a is b"; assert(g4.front[1] == 1); @@ -2255,25 +2255,26 @@ if (isForwardRange!Range) import std.algorithm.comparison : equal; size_t popCount = 0; - class RefFwdRange + static class RefFwdRange { int[] impl; + size_t* pcount; @safe nothrow: - this(int[] data) { impl = data; } + this(int[] data, size_t* pcount) { impl = data; this.pcount = pcount; } @property bool empty() { return impl.empty; } @property auto ref front() { return impl.front; } void popFront() { impl.popFront(); - popCount++; + (*pcount)++; } - @property auto save() { return new RefFwdRange(impl); } + @property auto save() { return new RefFwdRange(impl, pcount); } } static assert(isForwardRange!RefFwdRange); - auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]); + auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9], &popCount); auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2)); auto outerSave1 = groups.save; @@ -6058,7 +6059,7 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) import std.algorithm.comparison : equal; // Test by-reference separator - class RefSep { + static class RefSep { @safe: string _impl; this(string s) { _impl = s; } diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index daa4b99..870b1b4 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -13,7 +13,7 @@ $(T2 any, `any!"a > 0"([1, 2, -3, -4])` returns `true` because at least one element is positive) $(T2 balancedParens, - `balancedParens("((1 + 1) / 2)")` returns `true` because the + `balancedParens("((1 + 1) / 2)", '(', ')')` returns `true` because the string has balanced parentheses.) $(T2 boyerMooreFinder, `find("hello world", boyerMooreFinder("or"))` returns `"orld"` diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index b86e0f9..f48602e 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -2297,7 +2297,7 @@ if (isInputRange!RoR && // https://issues.dlang.org/show_bug.cgi?id=10895 @safe unittest { - class A + static class A { string name; alias name this; @@ -4376,8 +4376,8 @@ unittest return app[]; } - class C {} - struct S { const(C) c; } + static class C {} + static struct S { const(C) c; } S[] s = [ S(new C) ]; auto t = fastCopy(s); // Does not compile diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d index b2fcc071..33d0eae 100644 --- a/libphobos/src/std/bigint.d +++ b/libphobos/src/std/bigint.d @@ -1541,7 +1541,7 @@ Returns: number in upper case. */ -string toHex(const(BigInt) x) @safe +string toHex(const(BigInt) x) pure @safe { import std.array : appender; auto outbuff = appender!string(); diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index 5a15538..3b860fe 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -478,6 +478,20 @@ if (isFloatingPoint!T) } return this; } + + /** Returns a complex number instance that correponds in size and in ABI + to the associated C compiler's `_Complex` type. + */ + auto toNative() + { + import core.stdc.config : c_complex_float, c_complex_double, c_complex_real; + static if (is(T == float)) + return c_complex_float(re, im); + else static if (is(T == double)) + return c_complex_double(re, im); + else + return c_complex_real(re, im); + } } @safe pure nothrow unittest @@ -1910,3 +1924,14 @@ Complex!T pow(T)(const T x, Complex!T n) @trusted pure nothrow @nogc } }} } + +@safe pure nothrow @nogc unittest +{ + import std.meta : AliasSeq; + static foreach (T; AliasSeq!(float, double, real)) + {{ + auto c = Complex!T(123, 456); + auto n = c.toNative(); + assert(c.re == n.re && c.im == n.im); + }} +} diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d index 622dee4..9bd8d27 100644 --- a/libphobos/src/std/container/rbtree.d +++ b/libphobos/src/std/container/rbtree.d @@ -2055,7 +2055,7 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init } //Combinations not in examples. -@safe pure unittest +@system pure unittest { auto rbt1 = redBlackTree!(true, string)("hello", "hello"); auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3); diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d index f3c6932..a30ae58 100644 --- a/libphobos/src/std/experimental/logger/core.d +++ b/libphobos/src/std/experimental/logger/core.d @@ -1,3049 +1,13 @@ -// Written in the D programming language. /** -Source: $(PHOBOSSRC std/experimental/logger/core.d) -*/ -module std.experimental.logger.core; - -import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder; -import core.sync.mutex : Mutex; -import std.datetime.date : DateTime; -import std.datetime.systime : Clock, SysTime; -import std.range.primitives; -import std.traits; - -import std.experimental.logger.filelogger; - -/** This functions is used at runtime to determine if a `LogLevel` is -active. The same previously defined version statements are used to disable -certain levels. Again the version statements are associated with a compile -unit and can therefore not disable logging in other compile units. -pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc -*/ -bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL, - LogLevel globalLL, lazy bool condition = true) @safe -{ - return ll >= globalLL - && ll >= loggerLL - && ll != LogLevel.off - && globalLL != LogLevel.off - && loggerLL != LogLevel.off - && condition; -} - -/* This function formates a `SysTime` into an `OutputRange`. - -The `SysTime` is formatted similar to -$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part. -The fractional second part is in milliseconds and is always 3 digits. -*/ -void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time) -if (isOutputRange!(OutputRange,string)) -{ - import std.format.write : formattedWrite; - - const auto dt = cast(DateTime) time; - const auto fsec = time.fracSecs.total!"msecs"; - - formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, - fsec); -} - -/** This function logs data. - -In order for the data to be processed, the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel`; additionally the condition passed must be `true`. - -Params: - ll = The `LogLevel` used by this log call. - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -log(LogLevel.warning, true, "Hello World", 3.1415); --------------------- -*/ -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) -if (args.length != 1) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (ll, condition, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(const LogLevel ll, - lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName, - prettyFuncName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog`. - -Params: - ll = The `LogLevel` used by this log call. - args = The data that should be logged. - -Example: --------------------- -log(LogLevel.warning, "Hello World", 3.1415); --------------------- -*/ -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) -if (args.length > 1 && !is(Unqual!(A[0]) : bool)) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (ll, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg, - int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName, - moduleName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the -`sharedLog` must be greater or equal to the `defaultLogLevel` -add the condition passed must be `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -log(true, "Hello World", 3.1415); --------------------- -*/ -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) -if (args.length != 1) -{ - stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, condition, args); -} - -/// Ditto -void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg, - int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) -{ - stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel, - condition, arg, line, file, funcName, prettyFuncName); -} - -/** This function logs data. - -In order for the data to be processed the `LogLevel` of the -`sharedLog` must be greater or equal to the `defaultLogLevel`. - -Params: - args = The data that should be logged. - -Example: --------------------- -log("Hello World", 3.1415); --------------------- -*/ -void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) -if ((args.length > 1 && !is(Unqual!(A[0]) : bool) - && !is(Unqual!(A[0]) == LogLevel)) - || args.length == 0) -{ - stdThreadLocalLog.log!(line, file, funcName, - prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args); -} - -void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) -{ - stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file, - funcName, prettyFuncName, moduleName); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel` additionally the condition passed must be `true`. - -Params: - ll = The `LogLevel` used by this log call. - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(LogLevel.warning, true, "Hello World %f", 3.1415); --------------------- -*/ -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (ll, condition, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `LogLevel` of the `sharedLog` and the -`defaultLogLevel`. - -Params: - ll = The `LogLevel` used by this log call. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(LogLevel.warning, "Hello World %f", 3.1415); --------------------- -*/ -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg, - lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (ll, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `defaultLogLevel` additionally the condition -passed must be `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf(true, "Hello World %f", 3.1415); --------------------- -*/ -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, condition, msg, args); -} - -/** This function logs data in a `printf`-style manner. - -In order for the data to be processed the `LogLevel` of the log call must -be greater or equal to the `defaultLogLevel`. - -Params: - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -logf("Hello World %f", 3.1415); --------------------- -*/ -void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) -{ - stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName) - (stdThreadLocalLog.logLevel, msg, args); -} - -/** This template provides the global log functions with the `LogLevel` -is encoded in the function name. - -The aliases following this template create the public names of these log -functions. -*/ -template defaultLogFunction(LogLevel ll) -{ - void defaultLogFunction(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) - { - stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, - prettyFuncName, moduleName)(args); - } - - void defaultLogFunction(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, - prettyFuncName, moduleName)(condition, args); - } -} - -/** This function logs data to the `stdThreadLocalLog`, optionally depending -on a condition. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. -If a condition is given, it must evaluate to `true`. - -Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - -Example: --------------------- -trace(1337, "is number"); -info(1337, "is number"); -error(1337, "is number"); -critical(1337, "is number"); -fatal(1337, "is number"); -trace(true, 1337, "is number"); -info(false, 1337, "is number"); -error(true, 1337, "is number"); -critical(false, 1337, "is number"); -fatal(true, 1337, "is number"); --------------------- -*/ -alias trace = defaultLogFunction!(LogLevel.trace); -/// Ditto -alias info = defaultLogFunction!(LogLevel.info); -/// Ditto -alias warning = defaultLogFunction!(LogLevel.warning); -/// Ditto -alias error = defaultLogFunction!(LogLevel.error); -/// Ditto -alias critical = defaultLogFunction!(LogLevel.critical); -/// Ditto -alias fatal = defaultLogFunction!(LogLevel.fatal); - -/** This template provides the global `printf`-style log functions with -the `LogLevel` is encoded in the function name. - -The aliases following this template create the public names of the log -functions. -*/ -template defaultLogFunctionf(LogLevel ll) -{ - void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, - prettyFuncName, moduleName)(msg, args); - } - - void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, - prettyFuncName, moduleName)(condition, msg, args); - } -} - -/** This function logs data to the `sharedLog` in a `printf`-style -manner. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `sharedLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. - -Params: - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -tracef("is number %d", 1); -infof("is number %d", 2); -errorf("is number %d", 3); -criticalf("is number %d", 4); -fatalf("is number %d", 5); --------------------- - -The second version of the function logs data to the `sharedLog` in a $(D -printf)-style manner. - -In order for the resulting log message to be logged the `LogLevel` must -be greater or equal than the `LogLevel` of the `sharedLog` and -must be greater or equal than the global `LogLevel`. -Additionally the `LogLevel` must be greater or equal than the `LogLevel` -of the `stdSharedLogger`. - -Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - -Example: --------------------- -tracef(false, "is number %d", 1); -infof(false, "is number %d", 2); -errorf(true, "is number %d", 3); -criticalf(true, "is number %d", 4); -fatalf(someFunct(), "is number %d", 5); --------------------- -*/ -alias tracef = defaultLogFunctionf!(LogLevel.trace); -/// Ditto -alias infof = defaultLogFunctionf!(LogLevel.info); -/// Ditto -alias warningf = defaultLogFunctionf!(LogLevel.warning); -/// Ditto -alias errorf = defaultLogFunctionf!(LogLevel.error); -/// Ditto -alias criticalf = defaultLogFunctionf!(LogLevel.critical); -/// Ditto -alias fatalf = defaultLogFunctionf!(LogLevel.fatal); - -private struct MsgRange -{ - import std.traits : isSomeString, isSomeChar; - - private Logger log; - - this(Logger log) @safe - { - this.log = log; - } - - void put(T)(T msg) @safe - if (isSomeString!T) - { - log.logMsgPart(msg); - } - - void put(dchar elem) @safe - { - import std.utf : encode; - char[4] buffer; - size_t len = encode(buffer, elem); - log.logMsgPart(buffer[0 .. len]); - } -} - -private void formatString(A...)(MsgRange oRange, A args) -{ - import std.format.write : formattedWrite; - - foreach (arg; args) - { - formattedWrite(oRange, "%s", arg); - } -} - -@system unittest -{ - void dummy() @safe - { - auto tl = new TestLogger(); - auto dst = MsgRange(tl); - formatString(dst, "aaa", "bbb"); - } - - dummy(); -} - -/** -There are eight usable logging level. These level are $(I all), $(I trace), -$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off). -If a log function with `LogLevel.fatal` is called the shutdown handler of -that logger is called. -*/ -enum LogLevel : ubyte -{ - all = 1, /** Lowest possible assignable `LogLevel`. */ - trace = 32, /** `LogLevel` for tracing the execution of the program. */ - info = 64, /** This level is used to display information about the - program. */ - warning = 96, /** warnings about the program should be displayed with this - level. */ - error = 128, /** Information about errors should be logged with this - level.*/ - critical = 160, /** Messages that inform about critical errors should be - logged with this level. */ - fatal = 192, /** Log messages that describe fatal errors should use this - level. */ - off = ubyte.max /** Highest possible `LogLevel`. */ -} - -/** This class is the base of every logger. In order to create a new kind of -logger a deriving class needs to implement the `writeLogMsg` method. By -default this is not thread-safe. - -It is also possible to `override` the three methods `beginLogMsg`, -`logMsgPart` and `finishLogMsg` together, this option gives more -flexibility. -*/ -abstract class Logger -{ - import std.array : appender, Appender; - import std.concurrency : thisTid, Tid; - - /** LogEntry is a aggregation combining all information associated - with a log message. This aggregation will be passed to the method - writeLogMsg. - */ - protected struct LogEntry - { - /// the filename the log function was called from - string file; - /// the line number the log function was called from - int line; - /// the name of the function the log function was called from - string funcName; - /// the pretty formatted name of the function the log function was - /// called from - string prettyFuncName; - /// the name of the module the log message is coming from - string moduleName; - /// the `LogLevel` associated with the log message - LogLevel logLevel; - /// thread id of the log message - Tid threadId; - /// the time the message was logged - SysTime timestamp; - /// the message of the log message - string msg; - /// A refernce to the `Logger` used to create this `LogEntry` - Logger logger; - } - - /** - Every subclass of `Logger` has to call this constructor from their - constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal - handler will throw an `Error` if a log call is made with level - `LogLevel.fatal`. - - Params: - lv = `LogLevel` to use for this `Logger` instance. - */ - this(this This)(LogLevel lv) - { - this.logLevel_ = lv; - this.fatalHandler_ = delegate() { - throw new Error("A fatal log message was logged"); - }; - - this.mutex = new typeof(mutex)(); - } - - /** A custom logger must implement this method in order to work in a - `MultiLogger` and `ArrayLogger`. - - Params: - payload = All information associated with call to log function. - - See_Also: beginLogMsg, logMsgPart, finishLogMsg - */ - abstract protected void writeLogMsg(ref LogEntry payload) @safe; - - /* The default implementation will use an `std.array.appender` - internally to construct the message string. This means dynamic, - GC memory allocation. A logger can avoid this allocation by - reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`. - `beginLogMsg` is always called first, followed by any number of calls - to `logMsgPart` and one call to `finishLogMsg`. - - As an example for such a custom `Logger` compare this: - ---------------- - class CLogger : Logger - { - override void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp) - { - ... logic here - } - - override void logMsgPart(const(char)[] msg) - { - ... logic here - } - - override void finishLogMsg() - { - ... logic here - } - - void writeLogMsg(ref LogEntry payload) - { - this.beginLogMsg(payload.file, payload.line, payload.funcName, - payload.prettyFuncName, payload.moduleName, payload.logLevel, - payload.threadId, payload.timestamp, payload.logger); - - this.logMsgPart(payload.msg); - this.finishLogMsg(); - } - } - ---------------- - */ - protected void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp, Logger logger) - @safe - { - msgAppender = appender!string(); - header = LogEntry(file, line, funcName, prettyFuncName, - moduleName, logLevel, threadId, timestamp, null, logger); - } - - /** Logs a part of the log message. */ - protected void logMsgPart(scope const(char)[] msg) @safe - { - msgAppender.put(msg); - } - - /** Signals that the message has been written and no more calls to - `logMsgPart` follow. */ - protected void finishLogMsg() @safe - { - header.msg = msgAppender.data; - this.writeLogMsg(header); - } - - /** The `LogLevel` determines if the log call are processed or dropped - by the `Logger`. In order for the log call to be processed the - `LogLevel` of the log call must be greater or equal to the `LogLevel` - of the `logger`. - - These two methods set and get the `LogLevel` of the used `Logger`. - - Example: - ----------- - auto f = new FileLogger(stdout); - f.logLevel = LogLevel.info; - assert(f.logLevel == LogLevel.info); - ----------- - */ - @property final LogLevel logLevel() const pure @safe @nogc - { - return trustedLoad(this.logLevel_); - } - - /// Ditto - @property final void logLevel(const LogLevel lv) @safe @nogc - { - atomicStore(this.logLevel_, lv); - } - - /** This `delegate` is called in case a log message with - `LogLevel.fatal` gets logged. - - By default an `Error` will be thrown. - */ - @property final void delegate() fatalHandler() @safe @nogc - { - synchronized (mutex) return this.fatalHandler_; - } - - /// Ditto - @property final void fatalHandler(void delegate() @safe fh) @safe @nogc - { - synchronized (mutex) this.fatalHandler_ = fh; - } - - /** This method allows forwarding log entries from one logger to another. - - `forwardMsg` will ensure proper synchronization and then call - `writeLogMsg`. This is an API for implementing your own loggers and - should not be called by normal user code. A notable difference from other - logging functions is that the `globalLogLevel` wont be evaluated again - since it is assumed that the caller already checked that. - */ - void forwardMsg(ref LogEntry payload) @trusted - { - if (isLoggingEnabled(payload.logLevel, this.logLevel_, - globalLogLevel)) - { - this.writeLogMsg(payload); - - if (payload.logLevel == LogLevel.fatal) - this.fatalHandler_(); - } - } - - /** This template provides the log functions for the `Logger` `class` - with the `LogLevel` encoded in the function name. - - For further information see the the two functions defined inside of this - template. - - The aliases following this template create the public names of these log - functions. - */ - template memLogFunctions(LogLevel ll) - { - /** This function logs data to the used `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.trace(1337, "is number"); - s.info(1337, "is number"); - s.error(1337, "is number"); - s.critical(1337, "is number"); - s.fatal(1337, "is number"); - -------------------- - */ - void logImpl(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - condition. - - In order for the resulting log message to be logged the `LogLevel` must - be greater or equal than the `LogLevel` of the used `Logger` and - must be greater or equal than the global `LogLevel` additionally the - condition passed must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.trace(true, 1337, "is number"); - s.info(false, 1337, "is number"); - s.error(true, 1337, "is number"); - s.critical(false, 1337, "is number"); - s.fatal(true, 1337, "is number"); - -------------------- - */ - void logImpl(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy A args) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel` additionally - the passed condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - msg = The `printf`-style string. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stderr); - s.tracef(true, "is number %d", 1); - s.infof(true, "is number %d", 2); - s.errorf(false, "is number %d", 3); - s.criticalf(someFunc(), "is number %d", 4); - s.fatalf(true, "is number %d", 5); - -------------------- - */ - void logImplf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` must - be greater or equal than the `LogLevel` of the used `Logger` and - must be greater or equal than the global `LogLevel`. - - Params: - msg = The `printf`-style string. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stderr); - s.tracef("is number %d", 1); - s.infof("is number %d", 2); - s.errorf("is number %d", 3); - s.criticalf("is number %d", 4); - s.fatalf("is number %d", 5); - -------------------- - */ - void logImplf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - static if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - } - - /// Ditto - alias trace = memLogFunctions!(LogLevel.trace).logImpl; - /// Ditto - alias tracef = memLogFunctions!(LogLevel.trace).logImplf; - /// Ditto - alias info = memLogFunctions!(LogLevel.info).logImpl; - /// Ditto - alias infof = memLogFunctions!(LogLevel.info).logImplf; - /// Ditto - alias warning = memLogFunctions!(LogLevel.warning).logImpl; - /// Ditto - alias warningf = memLogFunctions!(LogLevel.warning).logImplf; - /// Ditto - alias error = memLogFunctions!(LogLevel.error).logImpl; - /// Ditto - alias errorf = memLogFunctions!(LogLevel.error).logImplf; - /// Ditto - alias critical = memLogFunctions!(LogLevel.critical).logImpl; - /// Ditto - alias criticalf = memLogFunctions!(LogLevel.critical).logImplf; - /// Ditto - alias fatal = memLogFunctions!(LogLevel.fatal).logImpl; - /// Ditto - alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf; - - /** This method logs data with the `LogLevel` of the used `Logger`. - - This method takes a `bool` as first argument. In order for the - data to be processed the `bool` must be `true` and the `LogLevel` - of the Logger must be greater or equal to the global `LogLevel`. - - Params: - args = The data that should be logged. - condition = The condition must be `true` for the data to be logged. - args = The data that is to be logged. - - Returns: The logger used by the logging function as reference. - - Example: - -------------------- - auto l = new StdioLogger(); - l.log(1337); - -------------------- - */ - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) - if (args.length != 1) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T, string moduleName = __MODULE__)(const LogLevel ll, - lazy bool condition, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel`. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(LogLevel.trace, 1337, "is number"); - s.log(LogLevel.info, 1337, "is number"); - s.log(LogLevel.warning, 1337, "is number"); - s.log(LogLevel.error, 1337, "is number"); - s.log(LogLevel.fatal, 1337, "is number"); - -------------------- - */ - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - explicitly passed condition with the `LogLevel` of the used - `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel` and the condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(true, 1337, "is number"); - s.log(true, 1337, "is number"); - s.log(true, 1337, "is number"); - s.log(false, 1337, "is number"); - s.log(false, 1337, "is number"); - -------------------- - */ - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - if (args.length != 1) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(lazy bool condition, lazy T args, int line = __LINE__, - string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with the `LogLevel` - of the used `Logger`. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel`. - - Params: - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.log(1337, "is number"); - s.log(info, 1337, "is number"); - s.log(1337, "is number"); - s.log(1337, "is number"); - s.log(1337, "is number"); - -------------------- - */ - void log(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 1 - && !is(Unqual!(A[0]) : bool) - && !is(immutable A[0] == immutable LogLevel)) - || args.length == 0) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /// Ditto - void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) - { - synchronized (mutex) - { - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - auto writer = MsgRange(this); - formatString(writer, arg); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel` and depending on a condition in a `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel` and the - condition must be `true`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - condition = The condition must be `true` for the data to be logged. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.info, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number"); - s.logf(LogLevel.error, false ,"%d %s", 1337, "is number"); - s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number"); - -------------------- - */ - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` with a specific - `LogLevel` in a `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - must be greater or equal than the `LogLevel` of the used `Logger` - and must be greater or equal than the global `LogLevel`. - - Params: - ll = The specific `LogLevel` used for logging the log message. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(LogLevel.trace, "%d %s", 1337, "is number"); - s.logf(LogLevel.info, "%d %s", 1337, "is number"); - s.logf(LogLevel.warning, "%d %s", 1337, "is number"); - s.logf(LogLevel.error, "%d %s", 1337, "is number"); - s.logf(LogLevel.fatal, "%d %s", 1337, "is number"); - -------------------- - */ - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, ll, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (ll == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This function logs data to the used `Logger` depending on a - condition with the `LogLevel` of the used `Logger` in a - `printf`-style manner. - - In order for the resulting log message to be logged the `LogLevel` - of the used `Logger` must be greater or equal than the global - `LogLevel` and the condition must be `true`. - - Params: - condition = The condition must be `true` for the data to be logged. - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - s.logf(false ,"%d %s", 1337, "is number"); - s.logf(true ,"%d %s", 1337, "is number"); - -------------------- - */ - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, - condition)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - /** This method logs data to the used `Logger` with the `LogLevel` - of the this `Logger` in a `printf`-style manner. - - In order for the data to be processed the `LogLevel` of the `Logger` - must be greater or equal to the global `LogLevel`. - - Params: - msg = The format string used for this log call. - args = The data that should be logged. - - Example: - -------------------- - auto s = new FileLogger(stdout); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - s.logf("%d %s", 1337, "is number"); - -------------------- - */ - void logf(int line = __LINE__, string file = __FILE__, - string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - { - synchronized (mutex) - { - import std.format.write : formattedWrite; - - if (isLoggingEnabled(this.logLevel_, this.logLevel_, - globalLogLevel)) - { - this.beginLogMsg(file, line, funcName, prettyFuncName, - moduleName, this.logLevel_, thisTid, Clock.currTime, this); - - auto writer = MsgRange(this); - formattedWrite(writer, msg, args); - - this.finishLogMsg(); - - if (this.logLevel_ == LogLevel.fatal) - this.fatalHandler_(); - } - } - } - - private void delegate() @safe fatalHandler_; - private shared LogLevel logLevel_ = LogLevel.info; - private Mutex mutex; - - protected Appender!string msgAppender; - protected LogEntry header; -} - -// Thread Global - -private shared Logger stdSharedDefaultLogger; -private shared Logger stdSharedLogger; -private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; - -/* This method returns the global default Logger. - * Marked @trusted because of excessive reliance on __gshared data + * This module is now deprecated, use $(MREF std, logger, core) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/logger/core.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) */ -private @property shared(Logger) defaultSharedLoggerImpl() @trusted -{ - import core.lifetime : emplace; - import std.stdio : stderr; - - __gshared align(__traits(classInstanceAlignment, FileLogger)) - void[__traits(classInstanceSize, FileLogger)] _buffer = void; - - import std.concurrency : initOnce; - initOnce!stdSharedDefaultLogger({ - auto buffer = cast(ubyte[]) _buffer; - return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info); - }()); - - return atomicLoad(stdSharedDefaultLogger); -} - -/** This property sets and gets the default `Logger`. Unless set to another -logger by the user, the default logger's log level is LogLevel.info. - -Example: -------------- -sharedLog = new FileLogger(yourFile); -------------- -The example sets a new `FileLogger` as new `sharedLog`. - -If at some point you want to use the original default logger again, you can -use $(D sharedLog = null;). This will put back the original. - -Note: -While getting and setting `sharedLog` is thread-safe, it has to be considered -that the returned reference is only a current snapshot and in the following -code, you must make sure no other thread reassigns to it between reading and -writing `sharedLog`. - -`sharedLog` is only thread-safe if the the used `Logger` is thread-safe. -The default `Logger` is thread-safe. -------------- -if (sharedLog !is myLogger) - sharedLog = new myLogger; -------------- -*/ -@property shared(Logger) sharedLog() @safe -{ - // If we have set up our own logger use that - if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger)) - { - return atomicLoad(logger); - } - else - { - // Otherwise resort to the default logger - return defaultSharedLoggerImpl; - } -} - -/// Ditto -@property void sharedLog(shared(Logger) logger) @safe -{ - atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger)); -} - -/** This methods get and set the global `LogLevel`. - -Every log message with a `LogLevel` lower as the global `LogLevel` -will be discarded before it reaches `writeLogMessage` method of any -`Logger`. -*/ -/* Implementation note: -For any public logging call, the global log level shall only be queried once on -entry. Otherwise when another threads changes the level, we would work with -different levels at different spots in the code. -*/ -@property LogLevel globalLogLevel() @safe @nogc -{ - return trustedLoad(stdLoggerGlobalLogLevel); -} - -/// Ditto -@property void globalLogLevel(LogLevel ll) @safe -{ - trustedStore(stdLoggerGlobalLogLevel, ll); -} - -// Thread Local - -/** The `StdForwardLogger` will always forward anything to the sharedLog. - -The `StdForwardLogger` will not throw if data is logged with $(D -LogLevel.fatal). -*/ -class StdForwardLogger : Logger -{ - /** The default constructor for the `StdForwardLogger`. - - Params: - lv = The `LogLevel` for the `MultiLogger`. By default the $(D - LogLevel) is `all`. - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.fatalHandler = delegate() {}; - } - - override protected void writeLogMsg(ref LogEntry payload) @trusted - { - synchronized (sharedLog.mutex) - { - (cast() sharedLog).forwardMsg(payload); - } - } -} - -/// -@safe unittest -{ - auto nl1 = new StdForwardLogger(LogLevel.all); -} - -@safe unittest -{ - import core.thread : Thread, msecs; - - static class RaceLogger : Logger - { - int value; - this() @safe shared - { - super(LogLevel.init); - } - override void writeLogMsg(ref LogEntry payload) @safe - { - import core.thread : Thread, msecs; - if (payload.msg == "foo") - { - value = 42; - () @trusted { Thread.sleep(100.msecs); }(); - assert(value == 42, "Another thread changed the value"); - } - else - { - () @trusted { Thread.sleep(50.msecs); } (); - value = 13; - } - } - } - - sharedLog = new shared RaceLogger; - scope(exit) { sharedLog = null; } - () @trusted { new Thread(() { log("foo"); }).start(); }(); - log("bar"); -} - -/** This `LogLevel` is unqiue to every thread. - -The thread local `Logger` will use this `LogLevel` to filter log calls -every same way as presented earlier. -*/ -//public LogLevel threadLogLevel = LogLevel.all; -private Logger stdLoggerThreadLogger; -private Logger stdLoggerDefaultThreadLogger; - -/* This method returns the thread local default Logger. -*/ -private @property Logger stdThreadLocalLogImpl() @trusted -{ - import core.lifetime : emplace; - - static align(__traits(classInstanceAlignment, StdForwardLogger)) - void[__traits(classInstanceSize, StdForwardLogger)] buffer; - - if (stdLoggerDefaultThreadLogger is null) - { - stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all); - } - return stdLoggerDefaultThreadLogger; -} - -/** This function returns a thread unique `Logger`, that by default -propagates all data logged to it to the `sharedLog`. - -These properties can be used to set and get this `Logger`. Every -modification to this `Logger` will only be visible in the thread the -modification has been done from. - -This `Logger` is called by the free standing log functions. This allows to -create thread local redirections and still use the free standing log -functions. -*/ -@property Logger stdThreadLocalLog() @safe -{ - // If we have set up our own logger use that - if (auto logger = stdLoggerThreadLogger) - return logger; - else - // Otherwise resort to the default logger - return stdThreadLocalLogImpl; -} - -/// Ditto -@property void stdThreadLocalLog(Logger logger) @safe -{ - stdLoggerThreadLogger = logger; -} - -/// Ditto -@system unittest -{ - import std.experimental.logger.filelogger : FileLogger; - import std.file : deleteme, remove; - Logger l = stdThreadLocalLog; - stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log"); - scope(exit) remove(deleteme ~ "-someFile.log"); - - auto tempLog = stdThreadLocalLog; - stdThreadLocalLog = l; - destroy(tempLog); -} - -@safe unittest -{ - LogLevel ll = globalLogLevel; - globalLogLevel = LogLevel.fatal; - assert(globalLogLevel == LogLevel.fatal); - globalLogLevel = ll; -} - -package class TestLogger : Logger -{ - int line = -1; - string file = null; - string func = null; - string prettyFunc = null; - string msg = null; - LogLevel lvl; - - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - } - - override protected void writeLogMsg(ref LogEntry payload) @safe - { - this.line = payload.line; - this.file = payload.file; - this.func = payload.funcName; - this.prettyFunc = payload.prettyFuncName; - this.lvl = payload.logLevel; - this.msg = payload.msg; - } -} - -version (StdUnittest) private void testFuncNames(Logger logger) @safe -{ - string s = "I'm here"; - logger.log(s); -} - -@safe unittest -{ - auto tl1 = new TestLogger(); - testFuncNames(tl1); - assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func); - assert(tl1.prettyFunc == - "void std.experimental.logger.core.testFuncNames(Logger logger) @safe", - tl1.prettyFunc); - assert(tl1.msg == "I'm here", tl1.msg); -} - -@safe unittest -{ - auto tl1 = new TestLogger(LogLevel.all); - tl1.log(); - assert(tl1.line == __LINE__ - 1); - tl1.log(true); - assert(tl1.line == __LINE__ - 1); - tl1.log(false); - assert(tl1.line == __LINE__ - 3); - tl1.log(LogLevel.info); - assert(tl1.line == __LINE__ - 1); - tl1.log(LogLevel.off); - assert(tl1.line == __LINE__ - 3); - tl1.log(LogLevel.info, true); - assert(tl1.line == __LINE__ - 1); - tl1.log(LogLevel.info, false); - assert(tl1.line == __LINE__ - 3); - - auto oldunspecificLogger = sharedLog; - scope(exit) { - sharedLog = atomicLoad(oldunspecificLogger); - } - - () @trusted { - sharedLog = cast(shared) tl1; - }(); - - log(); - assert(tl1.line == __LINE__ - 1); - - log(LogLevel.info); - assert(tl1.line == __LINE__ - 1); - - log(true); - assert(tl1.line == __LINE__ - 1); - - log(LogLevel.warning, true); - assert(tl1.line == __LINE__ - 1); - - trace(); - assert(tl1.line == __LINE__ - 1); -} - -@safe unittest -{ - import std.experimental.logger.multilogger : MultiLogger; - - auto tl1 = new TestLogger; - auto tl2 = new TestLogger; - - auto ml = new MultiLogger(); - ml.insertLogger("one", tl1); - ml.insertLogger("two", tl2); - - string msg = "Hello Logger World"; - ml.log(msg); - int lineNumber = __LINE__ - 1; - assert(tl1.msg == msg); - assert(tl1.line == lineNumber); - assert(tl2.msg == msg); - assert(tl2.line == lineNumber); - - ml.removeLogger("one"); - ml.removeLogger("two"); - auto n = ml.removeLogger("one"); - assert(n is null); -} - -@safe unittest -{ - bool errorThrown = false; - auto tl = new TestLogger; - auto dele = delegate() { - errorThrown = true; - }; - tl.fatalHandler = dele; - tl.fatal(); - assert(errorThrown); -} - -@safe unittest -{ - import std.conv : to; - import std.exception : assertThrown, assertNotThrown; - import std.format : format; - - auto l = new TestLogger(LogLevel.all); - string msg = "Hello Logger World"; - l.log(msg); - int lineNumber = __LINE__ - 1; - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.log(true, msg); - lineNumber = __LINE__ - 1; - assert(l.msg == msg, l.msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.log(false, msg); - assert(l.msg == msg); - assert(l.line == lineNumber, to!string(l.line)); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - l.logf(msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.logf(true, msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - l.logf(false, msg, "Yet"); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet")); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - Logger oldunspecificLogger; - () @trusted { - oldunspecificLogger = cast() sharedLog; - }(); - - assert(oldunspecificLogger.logLevel == LogLevel.info, - to!string(oldunspecificLogger.logLevel)); - - assert(l.logLevel == LogLevel.all); - - () @trusted { - sharedLog = cast(shared) l; - }(); - - assert(globalLogLevel == LogLevel.all, - to!string(globalLogLevel)); - - scope(exit) - { - () @trusted { - sharedLog = atomicLoad(cast(shared) oldunspecificLogger); - }(); - } - - () @trusted { - assert((cast() sharedLog).logLevel == LogLevel.all); - }(); - - assert(stdThreadLocalLog.logLevel == LogLevel.all); - assert(globalLogLevel == LogLevel.all); - - msg = "Another message"; - log(msg); - lineNumber = __LINE__ - 1; - assert(l.logLevel == LogLevel.all); - assert(l.line == lineNumber, to!string(l.line)); - assert(l.msg == msg, l.msg); - - log(true, msg); - lineNumber = __LINE__ - 1; - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - log(false, msg); - assert(l.msg == msg); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - logf(msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - logf(true, msg, "Yet"); - lineNumber = __LINE__ - 1; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - logf(false, msg, "Yet"); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - msg = "%s Another message"; - () @trusted { - assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - () @trusted { - assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet")); - } (); - lineNumber = __LINE__ - 2; - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); - - assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet")); - assert(l.msg == msg.format("Yet")); - assert(l.line == lineNumber); - assert(l.logLevel == LogLevel.all); -} - -@system unittest // default logger -{ - import std.file : deleteme, exists, remove; - import std.stdio : File; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - FileLogger l = new FileLogger(filename); - auto oldunspecificLogger = sharedLog; - - sharedLog = cast(shared) l; - - scope(exit) - { - remove(filename); - assert(!exists(filename)); - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - globalLogLevel = LogLevel.critical; - assert(globalLogLevel == LogLevel.critical); - - log(LogLevel.warning, notWritten); - log(LogLevel.critical, written); - - l.file.flush(); - l.file.close(); - - auto file = File(filename, "r"); - assert(!file.eof); - - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - assert(readLine.indexOf(notWritten) == -1, readLine); - file.close(); -} - -@system unittest -{ - import std.file : deleteme, remove; - import std.stdio : File; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - remove(filename); - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - auto l = new FileLogger(filename); - sharedLog = cast(shared) l; - - () @trusted { - (cast() sharedLog).logLevel = LogLevel.critical; - }(); - - log(LogLevel.error, false, notWritten); - log(LogLevel.critical, true, written); - destroy(l); - - auto file = File(filename, "r"); - auto readLine = file.readln(); - assert(!readLine.empty, readLine); - assert(readLine.indexOf(written) != -1); - assert(readLine.indexOf(notWritten) == -1); - file.close(); -} - -@safe unittest -{ - import std.conv : to; - - auto tl = new TestLogger(LogLevel.all); - int l = __LINE__; - tl.info("a"); - assert(tl.line == l+1); - assert(tl.msg == "a"); - assert(tl.logLevel == LogLevel.all); - assert(globalLogLevel == LogLevel.all); - l = __LINE__; - tl.trace("b"); - assert(tl.msg == "b", tl.msg); - assert(tl.line == l+1, to!string(tl.line)); -} - -// testing possible log conditions -@safe unittest -{ - import std.conv : to; - import std.format : format; - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - auto mem = new TestLogger; - mem.fatalHandler = delegate() {}; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - int value = 0; - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - mem.logLevel = ll; - - foreach (cond; [true, false]) - { - foreach (condValue; [true, false]) - { - foreach (memOrG; [true, false]) - { - foreach (prntf; [true, false]) - { - foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, - LogLevel.error, LogLevel.critical, - LogLevel.fatal, LogLevel.off]) - { - foreach (singleMulti; 0 .. 2) - { - int lineCall; - mem.msg = "-1"; - if (memOrG) - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - mem.logf(ll2, condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - mem.logf(ll2, condValue, - "%d %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.logf(ll2, "%s", value); - lineCall = __LINE__; - } - else - { - mem.logf(ll2, "%d %d", - value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - mem.log(ll2, condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(ll2, condValue, - to!string(value), value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.log(ll2, to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(ll2, - to!string(value), - value); - lineCall = __LINE__; - } - } - } - } - else - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - logf(ll2, condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - logf(ll2, condValue, - "%s %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - logf(ll2, "%s", value); - lineCall = __LINE__; - } - else - { - logf(ll2, "%s %s", value, - value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - log(ll2, condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - log(ll2, condValue, value, - to!string(value)); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - log(ll2, to!string(value)); - lineCall = __LINE__; - } - else - { - log(ll2, value, - to!string(value)); - lineCall = __LINE__; - } - } - } - } - - string valueStr = to!string(value); - ++value; - - bool ll2Off = (ll2 != LogLevel.off); - bool gllOff = (gll != LogLevel.off); - bool llOff = (ll != LogLevel.off); - bool condFalse = (cond ? condValue : true); - bool ll2VSgll = (ll2 >= gll); - bool ll2VSll = (ll2 >= ll); - - bool shouldLog = ll2Off && gllOff && llOff - && condFalse && ll2VSgll && ll2VSll; - - /* - writefln( - "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)" - , gll != LogLevel.off, ll2 != LogLevel.off, - cond ? condValue : true, - ll2 >= gll, ll2 >= ll, shouldLog); - */ - - - if (shouldLog) - { - assert(mem.msg.indexOf(valueStr) != -1, - format( - "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ - "cond(%b) condValue(%b)" ~ - " memOrG(%b) shouldLog(%b) %s == %s" ~ - " %b %b %b %b %b", - lineCall, ll2Off, gll, ll, ll2, cond, - condValue, memOrG, shouldLog, mem.msg, - valueStr, gllOff, llOff, condFalse, - ll2VSgll, ll2VSll - )); - } - else - { - assert(mem.msg.indexOf(valueStr), - format( - "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ - "cond(%b) condValue(%b)" ~ - " memOrG(%b) shouldLog(%b) %s == %s" ~ - " %b %b %b %b %b", - lineCall, ll2Off, gll, ll, ll2, cond, - condValue, memOrG, shouldLog, mem.msg, - valueStr, gllOff, llOff, condFalse, - ll2VSgll, ll2VSll - )); - } - } - } - } - } - } - } - } - } -} - -// more testing -@safe unittest -{ - import std.conv : to; - import std.format : format; - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - auto mem = new TestLogger; - mem.fatalHandler = delegate() {}; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - int value = 0; - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - mem.logLevel = ll; - - foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - stdThreadLocalLog.logLevel = tll; - - foreach (cond; [true, false]) - { - foreach (condValue; [true, false]) - { - foreach (memOrG; [true, false]) - { - foreach (prntf; [true, false]) - { - foreach (singleMulti; 0 .. 2) - { - int lineCall; - mem.msg = "-1"; - if (memOrG) - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - mem.logf(condValue, "%s", - value); - lineCall = __LINE__; - } - else - { - mem.logf(condValue, - "%d %d", value, value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.logf("%s", value); - lineCall = __LINE__; - } - else - { - mem.logf("%d %d", - value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - mem.log(condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(condValue, - to!string(value), value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - mem.log(to!string(value)); - lineCall = __LINE__; - } - else - { - mem.log(to!string(value), - value); - lineCall = __LINE__; - } - } - } - } - else - { - if (prntf) - { - if (cond) - { - if (singleMulti == 0) - { - logf(condValue, "%s", value); - lineCall = __LINE__; - } - else - { - logf(condValue, "%s %d", value, - value); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - logf("%s", value); - lineCall = __LINE__; - } - else - { - logf("%s %s", value, value); - lineCall = __LINE__; - } - } - } - else - { - if (cond) - { - if (singleMulti == 0) - { - log(condValue, - to!string(value)); - lineCall = __LINE__; - } - else - { - log(condValue, value, - to!string(value)); - lineCall = __LINE__; - } - } - else - { - if (singleMulti == 0) - { - log(to!string(value)); - lineCall = __LINE__; - } - else - { - log(value, to!string(value)); - lineCall = __LINE__; - } - } - } - } - - string valueStr = to!string(value); - ++value; - - bool gllOff = (gll != LogLevel.off); - bool llOff = (ll != LogLevel.off); - bool tllOff = (tll != LogLevel.off); - bool llVSgll = (ll >= gll); - bool tllVSll = - (stdThreadLocalLog.logLevel >= ll); - bool condFalse = (cond ? condValue : true); - - bool shouldLog = gllOff && llOff - && (memOrG ? true : tllOff) - && (memOrG ? - (ll >= gll) : - (tll >= gll && tll >= ll)) - && condFalse; - - if (shouldLog) - { - assert(mem.msg.indexOf(valueStr) != -1, - format("\ngll(%s) ll(%s) tll(%s) " ~ - "cond(%s) condValue(%s) " ~ - "memOrG(%s) prntf(%s) " ~ - "singleMulti(%s)", - gll, ll, tll, cond, condValue, - memOrG, prntf, singleMulti) - ~ format(" gllOff(%s) llOff(%s) " ~ - "llVSgll(%s) tllVSll(%s) " ~ - "tllOff(%s) condFalse(%s) " - ~ "shoudlLog(%s)", - gll != LogLevel.off, - ll != LogLevel.off, llVSgll, - tllVSll, tllOff, condFalse, - shouldLog) - ~ format("msg(%s) line(%s) " ~ - "lineCall(%s) valueStr(%s)", - mem.msg, mem.line, lineCall, - valueStr) - ); - } - else - { - assert(mem.msg.indexOf(valueStr) == -1, - format("\ngll(%s) ll(%s) tll(%s) " ~ - "cond(%s) condValue(%s) " ~ - "memOrG(%s) prntf(%s) " ~ - "singleMulti(%s)", - gll, ll, tll, cond, condValue, - memOrG, prntf, singleMulti) - ~ format(" gllOff(%s) llOff(%s) " ~ - "llVSgll(%s) tllVSll(%s) " ~ - "tllOff(%s) condFalse(%s) " - ~ "shoudlLog(%s)", - gll != LogLevel.off, - ll != LogLevel.off, llVSgll, - tllVSll, tllOff, condFalse, - shouldLog) - ~ format("msg(%s) line(%s) " ~ - "lineCall(%s) valueStr(%s)", - mem.msg, mem.line, lineCall, - valueStr) - ); - } - } - } - } - } - } - } - } - } -} - -// testing more possible log conditions -@safe unittest -{ - bool fatalLog; - auto mem = new TestLogger; - mem.fatalHandler = delegate() { fatalLog = true; }; - auto oldunspecificLogger = sharedLog; - - stdThreadLocalLog.logLevel = LogLevel.all; - - () @trusted { - sharedLog = cast(shared) mem; - }(); - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - - globalLogLevel = gll; - - foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - mem.logLevel = ll; - - foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, - LogLevel.info, LogLevel.warning, LogLevel.error, - LogLevel.critical, LogLevel.fatal, LogLevel.off]) - { - stdThreadLocalLog.logLevel = tll; - - foreach (cond; [true, false]) - { - assert(globalLogLevel == gll); - assert(mem.logLevel == ll); - - bool gllVSll = LogLevel.trace >= globalLogLevel; - bool llVSgll = ll >= globalLogLevel; - bool lVSll = LogLevel.trace >= ll; - bool gllOff = globalLogLevel != LogLevel.off; - bool llOff = mem.logLevel != LogLevel.off; - bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off; - bool tllVSll = tll >= ll; - bool tllVSgll = tll >= gll; - bool lVSgll = LogLevel.trace >= tll; - - bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond; - - mem.line = -1; - /* - writefln("gll(%3u) ll(%3u) cond(%b) test(%b)", - gll, ll, cond, test); - writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll, - gllOff, llOff, cond, test2); - */ - - mem.trace(__LINE__); int line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - trace(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.trace(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - trace(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.tracef("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - tracef("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.tracef(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - tracef(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.info >= ll; - lVSgll = LogLevel.info >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.info(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - info(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.info(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - info(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.infof("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - infof("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.infof(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - infof(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.warning >= ll; - lVSgll = LogLevel.warning >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.warning(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warning(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warning(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warning(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warningf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warningf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.warningf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - warningf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.critical >= ll; - lVSgll = LogLevel.critical >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.critical(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - critical(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.critical(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - critical(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.criticalf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - criticalf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - mem.criticalf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - - criticalf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - - llVSgll = ll >= globalLogLevel; - lVSll = LogLevel.fatal >= ll; - lVSgll = LogLevel.fatal >= tll; - test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; - testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && - lVSgll && cond; - - mem.fatal(__LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatal(__LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatal(cond, __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatal(cond, __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatalf("%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatalf("%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - - mem.fatalf(cond, "%d", __LINE__); line = __LINE__; - assert(test ? mem.line == line : true); line = -1; - assert(test ? fatalLog : true); - fatalLog = false; - - fatalf(cond, "%d", __LINE__); line = __LINE__; - assert(testG ? mem.line == line : true); line = -1; - assert(testG ? fatalLog : true); - fatalLog = false; - } - } - } - } -} - -// Issue #5 -@safe unittest -{ - import std.string : indexOf; - - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - auto tl = new TestLogger(LogLevel.info); - - () @trusted { - sharedLog = cast(shared) tl; - }(); - - trace("trace"); - assert(tl.msg.indexOf("trace") == -1); -} - -// Issue #5 -@safe unittest -{ - import std.experimental.logger.multilogger : MultiLogger; - import std.string : indexOf; - - stdThreadLocalLog.logLevel = LogLevel.all; - - auto oldunspecificLogger = sharedLog; - - scope(exit) - { - sharedLog = atomicLoad(oldunspecificLogger); - globalLogLevel = LogLevel.all; - } - - auto logger = new MultiLogger(LogLevel.error); - - auto tl = new TestLogger(LogLevel.info); - logger.insertLogger("required", tl); - - () @trusted { - sharedLog = cast(shared) logger; - }(); - - trace("trace"); - assert(tl.msg.indexOf("trace") == -1); - info("info"); - assert(tl.msg.indexOf("info") == -1); - error("error"); - assert(tl.msg.indexOf("error") == 0); -} - -@system unittest -{ - import std.exception : assertThrown; - auto tl = new TestLogger(); - assertThrown!Throwable(tl.fatal("fatal")); -} - -// log objects with non-safe toString -@system unittest -{ - struct Test - { - string toString() const @system - { - return "test"; - } - } - - auto tl = new TestLogger(); - tl.info(Test.init); - assert(tl.msg == "test"); -} - -// Workaround for atomics not allowed in @safe code -private auto trustedLoad(T)(ref shared T value) @trusted -{ - return atomicLoad!(MemoryOrder.acq)(value); -} - -// ditto -private void trustedStore(T)(ref shared T dst, ref T src) @trusted -{ - atomicStore!(MemoryOrder.rel)(dst, src); -} - -// check that thread-local logging does not propagate -// to shared logger -@system unittest -{ - import core.thread, std.concurrency; - - static shared logged_count = 0; - - class TestLog : Logger - { - Tid tid; - - this() - { - super (LogLevel.trace); - this.tid = thisTid; - } - - override void writeLogMsg(ref LogEntry payload) @trusted - { - assert(thisTid == this.tid); - atomicOp!"+="(logged_count, 1); - } - } - - class IgnoredLog : Logger - { - this() - { - super (LogLevel.trace); - } - - override void writeLogMsg(ref LogEntry payload) @trusted - { - assert(false); - } - } - - auto oldSharedLog = sharedLog; - scope(exit) - { - sharedLog = atomicLoad(oldSharedLog); - } - - () @trusted { - sharedLog = cast(shared) new IgnoredLog; - }(); - - Thread[] spawned; - - foreach (i; 0 .. 4) - { - spawned ~= new Thread({ - stdThreadLocalLog = new TestLog; - trace("zzzzzzzzzz"); - }); - spawned[$-1].start(); - } - - foreach (t; spawned) - t.join(); - - assert(atomicOp!"=="(logged_count, 4)); -} - -@safe unittest -{ - auto dl = () @trusted { - return cast(FileLogger) cast() sharedLog; - }(); - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} - -// https://issues.dlang.org/show_bug.cgi?id=14940 -@safe unittest -{ - import std.typecons : Nullable; - - Nullable!int a = 1; - auto l = new TestLogger(); - l.infof("log: %s", a); - assert(l.msg == "log: 1"); -} - -// Ensure @system toString methods work -@system unittest -{ - enum SystemToStringMsg = "SystemToString"; - static struct SystemToString - { - string toString() @system - { - return SystemToStringMsg; - } - } - - auto tl = new TestLogger(); - - SystemToString sts; - tl.logf("%s", sts); - assert(tl.msg == SystemToStringMsg); -} - -// https://issues.dlang.org/show_bug.cgi?id=17328 -@safe unittest -{ - import std.format : format; - - ubyte[] data = [0]; - string s = format("%(%02x%)", data); // format 00 - assert(s == "00"); - - auto tl = new TestLogger(); - - tl.infof("%(%02x%)", data); // infof 000 - - size_t i; - string fs = tl.msg; - for (; i < s.length; ++i) - { - assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs); - } - assert(fs.length == 2); -} - -// https://issues.dlang.org/show_bug.cgi?id=15954 -@safe unittest -{ - import std.conv : to; - auto tl = new TestLogger(); - tl.log("123456789".to!wstring); - assert(tl.msg == "123456789"); -} - -// https://issues.dlang.org/show_bug.cgi?id=16256 -@safe unittest -{ - import std.conv : to; - auto tl = new TestLogger(); - tl.log("123456789"d); - assert(tl.msg == "123456789"); -} - -// https://issues.dlang.org/show_bug.cgi?id=15517 -@system unittest -{ - import std.file : exists, remove, tempDir; - import std.path : buildPath; - import std.stdio : File; - import std.string : indexOf; - - string fn = tempDir.buildPath("logfile.log"); - if (exists(fn)) - { - remove(fn); - } - - auto oldShared = sharedLog; - scope(exit) - { - sharedLog = atomicLoad(oldShared); - if (exists(fn)) - { - remove(fn); - } - } - - auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; - - auto fl = new FileLogger(fn); - - () @trusted { - sharedLog = cast(shared) fl; - }(); - - assert(exists(fn)); - - foreach (t; ts) - { - log(t); - } - - auto f = File(fn); - auto l = f.byLine(); - assert(!l.empty); - size_t idx; - foreach (it; l) - { - assert(it.indexOf(ts[idx]) != -1, it); - ++idx; - } - - assert(exists(fn)); - fl.file.close(); -} +module std.experimental.logger.core; +public import std.logger.core; diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d index 457012e..3205a25 100644 --- a/libphobos/src/std/experimental/logger/filelogger.d +++ b/libphobos/src/std/experimental/logger/filelogger.d @@ -1,272 +1,13 @@ -// Written in the D programming language. /** -Source: $(PHOBOSSRC std/experimental/logger/filelogger.d) -*/ + * This module is now deprecated, use $(MREF std, logger, filelogger) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/logger/filelogger.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.experimental.logger.filelogger; - -import std.experimental.logger.core; -import std.stdio; - -import std.typecons : Flag; - -/** An option to create $(LREF FileLogger) directory if it is non-existent. -*/ -alias CreateFolder = Flag!"CreateFolder"; - -/** This `Logger` implementation writes log messages to the associated -file. The name of the file has to be passed on construction time. If the file -is already present new log messages will be append at its end. -*/ -class FileLogger : Logger -{ - import std.concurrency : Tid; - import std.datetime.systime : SysTime; - import std.format.write : formattedWrite; - - /** A constructor for the `FileLogger` Logger. - - Params: - fn = The filename of the output file of the `FileLogger`. If that - file can not be opened for writting an exception will be thrown. - lv = The `LogLevel` for the `FileLogger`. By default the - - Example: - ------------- - auto l1 = new FileLogger("logFile"); - auto l2 = new FileLogger("logFile", LogLevel.fatal); - auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes); - ------------- - */ - this(const string fn, const LogLevel lv = LogLevel.all) @safe - { - this(fn, lv, CreateFolder.yes); - } - - /** A constructor for the `FileLogger` Logger that takes a reference to - a `File`. - - The `File` passed must be open for all the log call to the - `FileLogger`. If the `File` gets closed, using the `FileLogger` - for logging will result in undefined behaviour. - - Params: - fn = The file used for logging. - lv = The `LogLevel` for the `FileLogger`. By default the - `LogLevel` for `FileLogger` is `LogLevel.all`. - createFileNameFolder = if yes and fn contains a folder name, this - folder will be created. - - Example: - ------------- - auto file = File("logFile.log", "w"); - auto l1 = new FileLogger(file); - auto l2 = new FileLogger(file, LogLevel.fatal); - ------------- - */ - this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe - { - import std.file : exists, mkdirRecurse; - import std.path : dirName; - import std.conv : text; - - super(lv); - this.filename = fn; - - if (createFileNameFolder) - { - auto d = dirName(this.filename); - mkdirRecurse(d); - assert(exists(d), text("The folder the FileLogger should have", - " created in '", d,"' could not be created.")); - } - - this.file_.open(this.filename, "a"); - } - - /** A constructor for the `FileLogger` Logger that takes a reference to - a `File`. - - The `File` passed must be open for all the log call to the - `FileLogger`. If the `File` gets closed, using the `FileLogger` - for logging will result in undefined behaviour. - - Params: - file = The file used for logging. - lv = The `LogLevel` for the `FileLogger`. By default the - `LogLevel` for `FileLogger` is `LogLevel.all`. - - Example: - ------------- - auto file = File("logFile.log", "w"); - auto l1 = new FileLogger(file); - auto l2 = new FileLogger(file, LogLevel.fatal); - ------------- - */ - this(File file, const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.file_ = file; - } - - /** If the `FileLogger` is managing the `File` it logs to, this - method will return a reference to this File. - */ - @property File file() @safe - { - return this.file_; - } - - /* This method overrides the base class method in order to log to a file - without requiring heap allocated memory. Additionally, the `FileLogger` - local mutex is logged to serialize the log calls. - */ - override protected void beginLogMsg(string file, int line, string funcName, - string prettyFuncName, string moduleName, LogLevel logLevel, - Tid threadId, SysTime timestamp, Logger logger) - @safe - { - import std.string : lastIndexOf; - ptrdiff_t fnIdx = file.lastIndexOf('/') + 1; - ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1; - - auto lt = this.file_.lockingTextWriter(); - systimeToISOString(lt, timestamp); - import std.conv : to; - formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string, - file[fnIdx .. $], line, funcName[funIdx .. $]); - } - - /* This methods overrides the base class method and writes the parts of - the log call directly to the file. - */ - override protected void logMsgPart(scope const(char)[] msg) - { - formattedWrite(this.file_.lockingTextWriter(), "%s", msg); - } - - /* This methods overrides the base class method and finalizes the active - log call. This requires flushing the `File` and releasing the - `FileLogger` local mutex. - */ - override protected void finishLogMsg() - { - this.file_.lockingTextWriter().put("\n"); - this.file_.flush(); - } - - /* This methods overrides the base class method and delegates the - `LogEntry` data to the actual implementation. - */ - override protected void writeLogMsg(ref LogEntry payload) - { - this.beginLogMsg(payload.file, payload.line, payload.funcName, - payload.prettyFuncName, payload.moduleName, payload.logLevel, - payload.threadId, payload.timestamp, payload.logger); - this.logMsgPart(payload.msg); - this.finishLogMsg(); - } - - /** If the `FileLogger` was constructed with a filename, this method - returns this filename. Otherwise an empty `string` is returned. - */ - string getFilename() - { - return this.filename; - } - - /** The `File` log messages are written to. */ - protected File file_; - - /** The filename of the `File` log messages are written to. */ - protected string filename; -} - -@system unittest -{ - import std.array : empty; - import std.file : deleteme, remove; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto l = new FileLogger(filename); - - scope(exit) - { - remove(filename); - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - l.logLevel = LogLevel.critical; - l.log(LogLevel.warning, notWritten); - l.log(LogLevel.critical, written); - destroy(l); - - auto file = File(filename, "r"); - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - readLine = file.readln(); - assert(readLine.indexOf(notWritten) == -1, readLine); -} - -@safe unittest -{ - import std.file : rmdirRecurse, exists, deleteme; - import std.path : dirName; - - const string tmpFolder = dirName(deleteme); - const string filepath = tmpFolder ~ "/bug15771/minas/oops/"; - const string filename = filepath ~ "output.txt"; - assert(!exists(filepath)); - - auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes); - scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }(); - - f.log("Hello World!"); - assert(exists(filepath)); - f.file.close(); -} - -@system unittest -{ - import std.array : empty; - import std.file : deleteme, remove; - import std.string : indexOf; - - string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; - auto file = File(filename, "w"); - auto l = new FileLogger(file); - - scope(exit) - { - remove(filename); - } - - string notWritten = "this should not be written to file"; - string written = "this should be written to file"; - - l.logLevel = LogLevel.critical; - l.log(LogLevel.warning, notWritten); - l.log(LogLevel.critical, written); - file.close(); - - file = File(filename, "r"); - string readLine = file.readln(); - assert(readLine.indexOf(written) != -1, readLine); - readLine = file.readln(); - assert(readLine.indexOf(notWritten) == -1, readLine); - file.close(); -} - -@system unittest -{ - auto dl = cast(FileLogger) sharedLog; - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} +public import std.logger.filelogger; diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d index 3593690..ae00b25 100644 --- a/libphobos/src/std/experimental/logger/multilogger.d +++ b/libphobos/src/std/experimental/logger/multilogger.d @@ -1,200 +1,13 @@ -// Written in the D programming language. /** -Source: $(PHOBOSSRC std/experimental/logger/multilogger.d) -*/ + * This module is now deprecated, use $(MREF std, logger, multilogger) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/logger/multilogger.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.experimental.logger.multilogger; - -import std.experimental.logger.core; -import std.experimental.logger.filelogger; - -/** This Element is stored inside the `MultiLogger` and associates a -`Logger` to a `string`. -*/ -struct MultiLoggerEntry -{ - string name; /// The name if the `Logger` - Logger logger; /// The stored `Logger` -} - -/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an -`Logger[]` in their order of insertion. - -Every data logged to this `MultiLogger` will be distributed to all the $(D -Logger)s inserted into it. This `MultiLogger` implementation can -hold multiple `Logger`s with the same name. If the method `removeLogger` -is used to remove a `Logger` only the first occurrence with that name will -be removed. -*/ -class MultiLogger : Logger -{ - /** A constructor for the `MultiLogger` Logger. - - Params: - lv = The `LogLevel` for the `MultiLogger`. By default the - `LogLevel` for `MultiLogger` is `LogLevel.all`. - - Example: - ------------- - auto l1 = new MultiLogger(LogLevel.trace); - ------------- - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - } - - /** This member holds all `Logger`s stored in the `MultiLogger`. - - When inheriting from `MultiLogger` this member can be used to gain - access to the stored `Logger`. - */ - protected MultiLoggerEntry[] logger; - - /** This method inserts a new Logger into the `MultiLogger`. - - Params: - name = The name of the `Logger` to insert. - newLogger = The `Logger` to insert. - */ - void insertLogger(string name, Logger newLogger) @safe - { - this.logger ~= MultiLoggerEntry(name, newLogger); - } - - /** This method removes a Logger from the `MultiLogger`. - - Params: - toRemove = The name of the `Logger` to remove. If the `Logger` - is not found `null` will be returned. Only the first occurrence of - a `Logger` with the given name will be removed. - - Returns: The removed `Logger`. - */ - Logger removeLogger(in char[] toRemove) @safe - { - import std.algorithm.mutation : copy; - import std.range.primitives : back, popBack; - for (size_t i = 0; i < this.logger.length; ++i) - { - if (this.logger[i].name == toRemove) - { - Logger ret = this.logger[i].logger; - this.logger[i] = this.logger.back; - this.logger.popBack(); - - return ret; - } - } - - return null; - } - - /* The override to pass the payload to all children of the - `MultiLoggerBase`. - */ - override protected void writeLogMsg(ref LogEntry payload) @safe - { - foreach (it; this.logger) - { - /* We don't perform any checks here to avoid race conditions. - Instead the child will check on its own if its log level matches - and assume LogLevel.all for the globalLogLevel (since we already - know the message passes this test). - */ - it.logger.forwardMsg(payload); - } - } -} - -@safe unittest -{ - import std.exception : assertThrown; - import std.experimental.logger.nulllogger; - auto a = new MultiLogger; - auto n0 = new NullLogger(); - auto n1 = new NullLogger(); - a.insertLogger("zero", n0); - a.insertLogger("one", n1); - - auto n0_1 = a.removeLogger("zero"); - assert(n0_1 is n0); - auto n = a.removeLogger("zero"); - assert(n is null); - - auto n1_1 = a.removeLogger("one"); - assert(n1_1 is n1); - n = a.removeLogger("one"); - assert(n is null); -} - -@safe unittest -{ - auto a = new MultiLogger; - auto n0 = new TestLogger; - auto n1 = new TestLogger; - a.insertLogger("zero", n0); - a.insertLogger("one", n1); - - a.log("Hello TestLogger"); int line = __LINE__; - assert(n0.msg == "Hello TestLogger"); - assert(n0.line == line); - assert(n1.msg == "Hello TestLogger"); - assert(n1.line == line); -} - -// Issue #16 -@system unittest -{ - import std.file : deleteme; - import std.stdio : File; - import std.string : indexOf; - string logName = deleteme ~ __FUNCTION__ ~ ".log"; - auto logFileOutput = File(logName, "w"); - scope(exit) - { - import std.file : remove; - logFileOutput.close(); - remove(logName); - } - auto traceLog = new FileLogger(logFileOutput, LogLevel.all); - auto infoLog = new TestLogger(LogLevel.info); - - auto root = new MultiLogger(LogLevel.all); - root.insertLogger("fileLogger", traceLog); - root.insertLogger("stdoutLogger", infoLog); - - string tMsg = "A trace message"; - root.trace(tMsg); int line1 = __LINE__; - - assert(infoLog.line != line1); - assert(infoLog.msg != tMsg); - - string iMsg = "A info message"; - root.info(iMsg); int line2 = __LINE__; - - assert(infoLog.line == line2); - assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg); - - logFileOutput.close(); - logFileOutput = File(logName, "r"); - assert(logFileOutput.isOpen); - assert(!logFileOutput.eof); - - auto line = logFileOutput.readln(); - assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg); - assert(!logFileOutput.eof); - line = logFileOutput.readln(); - assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg); -} - -@system unittest -{ - auto dl = cast(FileLogger) sharedLog; - assert(dl !is null); - assert(dl.logLevel == LogLevel.info); - assert(globalLogLevel == LogLevel.all); - - auto tl = cast(StdForwardLogger) stdThreadLocalLog; - assert(tl !is null); - stdThreadLocalLog.logLevel = LogLevel.all; -} +public import std.logger.multilogger; diff --git a/libphobos/src/std/experimental/logger/nulllogger.d b/libphobos/src/std/experimental/logger/nulllogger.d index 0ff7663..2c1f0ba 100644 --- a/libphobos/src/std/experimental/logger/nulllogger.d +++ b/libphobos/src/std/experimental/logger/nulllogger.d @@ -1,41 +1,13 @@ -// Written in the D programming language. /** -Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d) -*/ + * This module is now deprecated, use $(MREF std, logger, nulllogger) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.experimental.logger.nulllogger; - -import std.experimental.logger.core; - -/** The `NullLogger` will not process any log messages. - -In case of a log message with `LogLevel.fatal` nothing will happen. -*/ -class NullLogger : Logger -{ - /** The default constructor for the `NullLogger`. - - Independent of the parameter this Logger will never log a message. - - Params: - lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel` - for `NullLogger` is `LogLevel.all`. - */ - this(const LogLevel lv = LogLevel.all) @safe - { - super(lv); - this.fatalHandler = delegate() {}; - } - - override protected void writeLogMsg(ref LogEntry payload) @safe @nogc - { - } -} - -/// -@safe unittest -{ - import std.experimental.logger.core : LogLevel; - auto nl1 = new NullLogger(LogLevel.all); - nl1.info("You will never read this."); - nl1.fatal("You will never read this, either and it will not throw"); -} +public import std.logger.nulllogger; diff --git a/libphobos/src/std/experimental/logger/package.d b/libphobos/src/std/experimental/logger/package.d index 89dc713..4d19ea9 100644 --- a/libphobos/src/std/experimental/logger/package.d +++ b/libphobos/src/std/experimental/logger/package.d @@ -1,168 +1,17 @@ -// Written in the D programming language. /** -Implements logging facilities. - -Copyright: Copyright Robert "burner" Schadek 2013 -- -License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. -Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek) - -$(H3 Basic Logging) - -Message logging is a common approach to expose runtime information of a -program. Logging should be easy, but also flexible and powerful, therefore -`D` provides a standard interface for logging. - -The easiest way to create a log message is to write: -------------- -import std.experimental.logger; - -void main() { - log("Hello World"); -} -------------- -This will print a message to the `stderr` device. The message will contain -the filename, the line number, the name of the surrounding function, the time -and the message. - -More complex log call can go along the lines like: -------------- -log("Logging to the sharedLog with its default LogLevel"); -logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging"); -info("Logging to the sharedLog with its info LogLevel"); -warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6"); -error("Logging to the sharedLog with its error LogLevel"); -errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with"); -critical("Logging to the"," sharedLog with its error LogLevel"); -fatal("Logging to the sharedLog with its fatal LogLevel"); - -auto fLogger = new FileLogger("NameOfTheLogFile"); -fLogger.log("Logging to the fileLogger with its default LogLevel"); -fLogger.info("Logging to the fileLogger with its default LogLevel"); -fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6"); -fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less"); -fLogger.critical("Logging to the fileLogger with its info LogLevel"); -fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6"); -fLogger.fatal("Logging to the fileLogger with its warning LogLevel"); -------------- -Additionally, this example shows how a new `FileLogger` is created. -Individual `Logger` and the global log functions share commonly named -functions to log data. - -The names of the functions are as follows: -$(UL - $(LI `log`) - $(LI `trace`) - $(LI `info`) - $(LI `warning`) - $(LI `critical`) - $(LI `fatal`) -) -The default `Logger` will by default log to `stderr` and has a default -`LogLevel` of `LogLevel.all`. The default Logger can be accessed by -using the property called `sharedLog`. This property is a reference to the -current default `Logger`. This reference can be used to assign a new -default `Logger`. -------------- -sharedLog = new FileLogger("New_Default_Log_File.log"); -------------- - -Additional `Logger` can be created by creating a new instance of the -required `Logger`. - -$(H3 Logging Fundamentals) -$(H4 LogLevel) -The `LogLevel` of a log call can be defined in two ways. The first is by -calling `log` and passing the `LogLevel` explicitly as the first argument. -The second way of setting the `LogLevel` of a -log call, is by calling either `trace`, `info`, `warning`, -`critical`, or `fatal`. The log call will then have the respective -`LogLevel`. If no `LogLevel` is defined the log call will use the -current `LogLevel` of the used `Logger`. If data is logged with -`LogLevel` `fatal` by default an `Error` will be thrown. -This behaviour can be modified by using the member `fatalHandler` to -assign a custom delegate to handle log call with `LogLevel` `fatal`. - -$(H4 Conditional Logging) -Conditional logging can be achieved be passing a `bool` as first -argument to a log function. If conditional logging is used the condition must -be `true` in order to have the log message logged. - -In order to combine an explicit `LogLevel` passing with conditional -logging, the `LogLevel` has to be passed as first argument followed by the -`bool`. - -$(H4 Filtering Log Messages) -Messages are logged if the `LogLevel` of the log message is greater than or -equal to the `LogLevel` of the used `Logger` and additionally if the -`LogLevel` of the log message is greater than or equal to the global `LogLevel`. -If a condition is passed into the log call, this condition must be true. - -The global `LogLevel` is accessible by using `globalLogLevel`. -To assign a `LogLevel` of a `Logger` use the `logLevel` property of -the logger. - -$(H4 Printf Style Logging) -If `printf`-style logging is needed add a $(B f) to the logging call, such as -$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)). -The additional $(B f) appended to the function name enables `printf`-style -logging for all combinations of explicit `LogLevel` and conditional -logging functions and methods. - -$(H4 Thread Local Redirection) -Calls to the free standing log functions are not directly forwarded to the -global `Logger` `sharedLog`. Actually, a thread local `Logger` of -type `StdForwardLogger` processes the log call and then, by default, forwards -the created `Logger.LogEntry` to the `sharedLog` `Logger`. -The thread local `Logger` is accessible by the `stdThreadLocalLog` -property. This property allows to assign user defined `Logger`. The default -`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all` -and it will therefore forward all messages to the `sharedLog` `Logger`. -The `LogLevel` of the `stdThreadLocalLog` can be used to filter log -calls before they reach the `sharedLog` `Logger`. - -$(H3 User Defined Logger) -To customize the `Logger` behavior, create a new `class` that inherits from -the abstract `Logger` `class`, and implements the `writeLogMsg` -method. -------------- -class MyCustomLogger : Logger -{ - this(LogLevel lv) @safe - { - super(lv); - } - - override void writeLogMsg(ref LogEntry payload) - { - // log message in my custom way - } -} - -auto logger = new MyCustomLogger(LogLevel.info); -logger.log("Awesome log message with LogLevel.info"); -------------- - -To gain more precise control over the logging process, additionally to -overriding the `writeLogMsg` method the methods `beginLogMsg`, -`logMsgPart` and `finishLogMsg` can be overridden. - -$(H3 Provided Logger) -By default four `Logger` implementations are given. The `FileLogger` -logs data to files. It can also be used to log to `stdout` and `stderr` -as these devices are files as well. A `Logger` that logs to `stdout` can -therefore be created by $(D new FileLogger(stdout)). -The `MultiLogger` is basically an associative array of `string`s to -`Logger`. It propagates log calls to its stored `Logger`. The -`ArrayLogger` contains an array of `Logger` and also propagates log -calls to its stored `Logger`. The `NullLogger` does not do anything. It -will never log a message and will never throw on a log call with `LogLevel` -`error`. - -Source: $(PHOBOSSRC std/experimental/logger/package.d) -*/ + * This module is now deprecated, use $(MREF std, logger) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/logger/package.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.experimental.logger; -public import std.experimental.logger.core; -public import std.experimental.logger.filelogger; -public import std.experimental.logger.multilogger; -public import std.experimental.logger.nulllogger; +public import std.logger.core; +public import std.logger.filelogger; +public import std.logger.multilogger; +public import std.logger.nulllogger; diff --git a/libphobos/src/std/experimental/typecons.d b/libphobos/src/std/experimental/typecons.d deleted file mode 100644 index 46e21e7..0000000 --- a/libphobos/src/std/experimental/typecons.d +++ /dev/null @@ -1,1083 +0,0 @@ -// Written in the D programming language. - -/** -This module implements experimental additions/modifications to $(MREF std, typecons). - -Use this module to test out new functionality for $(REF wrap, std, typecons) -which allows for a struct to be wrapped against an interface; the -implementation in $(MREF std, typecons) only allows for classes to use the wrap -functionality. - -Source: $(PHOBOSSRC std/experimental/typecons.d) - -Copyright: Copyright the respective authors, 2008- -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(HTTP erdani.org, Andrei Alexandrescu), - $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski), - Don Clugston, - Shin Fujishiro, - Kenji Hara - */ -module std.experimental.typecons; - -import std.meta; // : AliasSeq, allSatisfy; -import std.traits; - -import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, GetOverloadedMethods; - -private -{ - pragma(mangle, "_d_toObject") - extern(C) pure nothrow Object typecons_d_toObject(void* p); -} - -/* - * Avoids opCast operator overloading. - */ -private template dynamicCast(T) -if (is(T == class) || is(T == interface)) -{ - @trusted - T dynamicCast(S)(inout S source) - if (is(S == class) || is(S == interface)) - { - static if (is(Unqual!S : Unqual!T)) - { - import std.traits : QualifierOf; - alias Qual = QualifierOf!S; // SharedOf or MutableOf - alias TmpT = Qual!(Unqual!T); - inout(TmpT) tmp = source; // bypass opCast by implicit conversion - return *cast(T*)(&tmp); // + variable pointer cast + dereference - } - else - { - return cast(T) typecons_d_toObject(*cast(void**)(&source)); - } - } -} - -@system unittest -{ - class C { @disable void opCast(T)(); } - auto c = new C; - static assert(!__traits(compiles, cast(Object) c)); - auto o = dynamicCast!Object(c); - assert(c is o); - - interface I { @disable void opCast(T)(); Object instance(); } - interface J { @disable void opCast(T)(); Object instance(); } - class D : I, J { Object instance() { return this; } } - I i = new D(); - static assert(!__traits(compiles, cast(J) i)); - J j = dynamicCast!J(i); - assert(i.instance() is j.instance()); -} - -/* - * Determines if the `Source` type satisfies all interface requirements of - * `Targets`. - */ -private template implementsInterface(Source, Targets...) -if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) -{ - import std.meta : staticMap; - - // strict upcast - bool implementsInterface()() - if (Targets.length == 1 && is(Source : Targets[0])) - { - return true; - } - // structural upcast - template implementsInterface() - if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets)) - { - auto implementsInterface() - { - return hasRequiredMethods!(); - } - - // list of FuncInfo - alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!Targets); - // list of function symbols - alias SourceMembers = GetOverloadedMethods!Source; - - // Check whether all of SourceMembers satisfy covariance target in - // TargetMembers - template hasRequiredMethods(size_t i = 0) - { - static if (i >= TargetMembers.length) - enum hasRequiredMethods = true; - else - { - enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers); - - version (StdUnittest) {} - else debug - { - static if (foundFunc == -1) - pragma(msg, "Could not locate matching function for: ", - TargetMembers[i].stringof); - } - - enum hasRequiredMethods = - foundFunc != -1 && - hasRequiredMethods!(i + 1); - } - } - } -} -// ditto -private template implementsInterface(Source, Targets...) -if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) -{ - import std.meta : staticMap; - - alias implementsInterface = .implementsInterface!(Source, staticMap!(Unqual, Targets)); -} - -@safe unittest -{ - interface Foo { - void foo(); - } - interface Bar { - void bar(); - } - interface FooBar : Foo, Bar { - void foobar(); - } - - struct A { - void foo() {} - } - struct B { - void bar() {} - void foobar() {} - } - class C { - void foo() {} - void bar() {} - } - struct D { - void foo() {} - void bar() {} - void foobar() {} - } - // Implements interface - static assert(implementsInterface!(A, Foo)); - static assert(implementsInterface!(A, const(Foo))); - static assert(implementsInterface!(A, immutable(Foo))); - // Doesn't implement interface - static assert(!implementsInterface!(B, Foo)); - static assert(implementsInterface!(B, Bar)); - // Implements both interfaces - static assert(implementsInterface!(C, Foo)); - static assert(implementsInterface!(C, Bar)); - static assert(implementsInterface!(C, Foo, Bar)); - static assert(implementsInterface!(C, Foo, const(Bar))); - static assert(!implementsInterface!(A, Foo, Bar)); - static assert(!implementsInterface!(A, Foo, immutable(Bar))); - // Implements inherited - static assert(implementsInterface!(D, FooBar)); - static assert(!implementsInterface!(B, FooBar)); -} - -private enum isInterface(ConceptType) = is(ConceptType == interface); - -/// -template wrap(Targets...) -if (Targets.length >= 1 && allSatisfy!(isInterface, Targets)) -{ - import std.meta : ApplyLeft, staticMap; - - version (StdDdoc) - { - /** - * Wrap src in an anonymous class implementing $(D_PARAM Targets). - * - * wrap creates an internal wrapper class which implements the - * interfaces in `Targets` using the methods of `src`, then returns a - * GC-allocated instance of it. - * - * $(D_PARAM Source) can be either a `class` or a `struct`, but it must - * $(I structurally conform) with all the $(D_PARAM Targets) - * interfaces; i.e. it must provide concrete methods with compatible - * signatures of those in $(D_PARAM Targets). - * - * If $(D_PARAM Source) is a `struct` then wrapping/unwrapping will - * create a copy; it is not possible to affect the original `struct` - * through the wrapper. - * - * The returned object additionally supports $(LREF unwrap). - * - * Note: - * If $(D_PARAM Targets) has only one entry and $(D_PARAM Source) is a - * class which explicitly implements it, wrap simply returns src - * upcasted to `Targets[0]`. - * - * Bugs: - * wrap does not support interfaces which take their own type as either - * a parameter type or return type in any of its methods. - * - * See_Also: $(LREF unwrap) for examples - */ - auto wrap(Source)(inout Source src) - if (implementsInterface!(Source, Targets)); - } - - static if (!allSatisfy!(isMutable, Targets)) - alias wrap = .wrap!(staticMap!(Unqual, Targets)); - else - { - // strict upcast - auto wrap(Source)(inout Source src) - if (Targets.length == 1 && is(Source : Targets[0])) - { - alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]); - return dynamicCast!(inout T)(src); - } - - // structural upcast - template wrap(Source) - if (!allSatisfy!(ApplyLeft!(isImplicitlyConvertible, Source), Targets)) - { - auto wrap(inout Source src) - { - static assert(implementsInterface!(Source, Targets), - "Source "~Source.stringof~ - " does not have structural conformance to "~ - Targets.stringof); - - alias T = Select!(is(Source == shared), shared Impl, Impl); - return new inout T(src); - } - - // list of FuncInfo - alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!(Targets)); - // list of function symbols - alias SourceMembers = GetOverloadedMethods!Source; - - static if (is(Source == class) || is(Source == interface)) - alias StructuralType = Object; - else static if (is(Source == struct)) - alias StructuralType = Source; - - // Check whether all of SourceMembers satisfy covariance target in TargetMembers - // Internal wrapper class - final class Impl : Structural!StructuralType, Targets - { - private: - Source _wrap_source; - - this( inout Source s) inout @safe pure nothrow { _wrap_source = s; } - this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; } - - static if (is(Source == class) || is(Source == interface)) - { - // BUG: making private should work with NVI. - protected inout(Object) _wrap_getSource() inout @safe - { - return dynamicCast!(inout Object)(_wrap_source); - } - } - else - { - // BUG: making private should work with NVI. - protected inout(Source) _wrap_getSource() inout @safe - { - return _wrap_source; - } - } - - import std.conv : to; - import core.lifetime : forward; - template generateFun(size_t i) - { - enum name = TargetMembers[i].name; - enum fa = functionAttributes!(TargetMembers[i].type); - static args(int num)() - { - string r; - bool first = true; - foreach (i; 0 .. num) - { - import std.conv : to; - r ~= (first ? "" : ", ") ~ " a" ~ (i+1).to!string; - first = false; - } - return r; - } - static if (fa & FunctionAttribute.property) - { - static if (Parameters!(TargetMembers[i].type).length == 0) - enum fbody = "_wrap_source."~name; - else - enum fbody = "_wrap_source."~name~" = a1"; - } - else - { - enum fbody = "_wrap_source."~name~"("~args!(Parameters!(TargetMembers[i].type).length)~")"; - } - enum generateFun = - "override "~wrapperSignature!(TargetMembers[i]) ~ - "{ return "~fbody~"; }"; - } - - public: - static foreach (i; 0 .. TargetMembers.length) - mixin(generateFun!i); - } - } - } -} - -// Build a signature that matches the provided function -// Each argument will be provided a name in the form a# -private template wrapperSignature(alias fun) -{ - enum name = fun.name; - enum fa = functionAttributes!(fun.type); - static @property stc() - { - string r; - if (fa & FunctionAttribute.property) r ~= "@property "; - if (fa & FunctionAttribute.ref_) r ~= "ref "; - if (fa & FunctionAttribute.pure_) r ~= "pure "; - if (fa & FunctionAttribute.nothrow_) r ~= "nothrow "; - if (fa & FunctionAttribute.trusted) r ~= "@trusted "; - if (fa & FunctionAttribute.safe) r ~= "@safe "; - return r; - } - static @property mod() - { - alias type = AliasSeq!(fun.type)[0]; - string r; - static if (is(type == immutable)) r ~= " immutable"; - else - { - static if (is(type == shared)) r ~= " shared"; - static if (is(type == const)) r ~= " const"; - else static if (is(type == inout)) r ~= " inout"; - //else --> mutable - } - return r; - } - alias param = Parameters!(fun.type); - static @property wrapperParameters() - { - string r; - bool first = true; - foreach (i, p; param) - { - import std.conv : to; - r ~= (first ? "" : ", ") ~ p.stringof ~ " a" ~ (i+1).to!string; - first = false; - } - return r; - } - - enum wrapperSignature = - stc~ReturnType!(fun.type).stringof ~ " " - ~ name~"("~wrapperParameters~")"~mod; -} - -@safe unittest -{ - interface M - { - void f1(); - void f2(string[] args, int count); - void f3(string[] args, int count) pure const; - } - - alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!M); - static assert(wrapperSignature!(TargetMembers[0]) == "void f1()" - , wrapperSignature!(TargetMembers[0])); - - static assert(wrapperSignature!(TargetMembers[1]) == "void f2(string[] a1, int a2)" - , wrapperSignature!(TargetMembers[1])); - - static assert(wrapperSignature!(TargetMembers[2]) == "pure void f3(string[] a1, int a2) const" - , wrapperSignature!(TargetMembers[2])); -} - -// Internal class to support dynamic cross-casting -private interface Structural(T) -{ - inout(T) _wrap_getSource() inout @safe pure nothrow; -} - -private string unwrapExceptionText(Source, Target)() -{ - return Target.stringof~ " not wrapped into "~ Source.stringof; -} - -version (StdDdoc) -{ - /** - * Extract object previously wrapped by $(LREF wrap). - * - * Params: - * Target = type of wrapped object - * src = wrapper object returned by $(LREF wrap) - * - * Returns: the wrapped object, or null if src is not a wrapper created - * by $(LREF wrap) and $(D_PARAM Target) is a class - * - * Throws: $(REF ConvException, std, conv) when attempting to extract a - * struct which is not the wrapped type - * - * See_also: $(LREF wrap) - */ - public inout(Target) unwrap(Target, Source)(inout Source src); -} - -/// -@system unittest -{ - interface Quack - { - int quack(); - @property int height(); - } - interface Flyer - { - @property int height(); - } - class Duck : Quack - { - int quack() { return 1; } - @property int height() { return 10; } - } - class Human - { - int quack() { return 2; } - @property int height() { return 20; } - } - struct HumanStructure - { - int quack() { return 3; } - @property int height() { return 30; } - } - - Duck d1 = new Duck(); - Human h1 = new Human(); - HumanStructure hs1; - - interface Refreshable - { - int refresh(); - } - // does not have structural conformance - static assert(!__traits(compiles, d1.wrap!Refreshable)); - static assert(!__traits(compiles, h1.wrap!Refreshable)); - static assert(!__traits(compiles, hs1.wrap!Refreshable)); - - // strict upcast - Quack qd = d1.wrap!Quack; - assert(qd is d1); - assert(qd.quack() == 1); // calls Duck.quack - // strict downcast - Duck d2 = qd.unwrap!Duck; - assert(d2 is d1); - - // structural upcast - Quack qh = h1.wrap!Quack; - Quack qhs = hs1.wrap!Quack; - assert(qh.quack() == 2); // calls Human.quack - assert(qhs.quack() == 3); // calls HumanStructure.quack - // structural downcast - Human h2 = qh.unwrap!Human; - HumanStructure hs2 = qhs.unwrap!HumanStructure; - assert(h2 is h1); - assert(hs2 is hs1); - - // structural upcast (two steps) - Quack qx = h1.wrap!Quack; // Human -> Quack - Quack qxs = hs1.wrap!Quack; // HumanStructure -> Quack - Flyer fx = qx.wrap!Flyer; // Quack -> Flyer - Flyer fxs = qxs.wrap!Flyer; // Quack -> Flyer - assert(fx.height == 20); // calls Human.height - assert(fxs.height == 30); // calls HumanStructure.height - // strucural downcast (two steps) - Quack qy = fx.unwrap!Quack; // Flyer -> Quack - Quack qys = fxs.unwrap!Quack; // Flyer -> Quack - Human hy = qy.unwrap!Human; // Quack -> Human - HumanStructure hys = qys.unwrap!HumanStructure; // Quack -> HumanStructure - assert(hy is h1); - assert(hys is hs1); - // strucural downcast (one step) - Human hz = fx.unwrap!Human; // Flyer -> Human - HumanStructure hzs = fxs.unwrap!HumanStructure; // Flyer -> HumanStructure - assert(hz is h1); - assert(hzs is hs1); -} - -/// -@system unittest -{ - import std.traits : functionAttributes, FunctionAttribute; - interface A { int run(); } - interface B { int stop(); @property int status(); } - class X - { - int run() { return 1; } - int stop() { return 2; } - @property int status() { return 3; } - } - - auto x = new X(); - auto ab = x.wrap!(A, B); - A a = ab; - B b = ab; - assert(a.run() == 1); - assert(b.stop() == 2); - assert(b.status == 3); - static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property); -} - -template unwrap(Target) -{ - static if (!isMutable!Target) - alias unwrap = .unwrap!(Unqual!Target); - else - { - // strict downcast - auto unwrap(Source)(inout Source src) - if (is(Target : Source)) - { - alias T = Select!(is(Source == shared), shared Target, Target); - return dynamicCast!(inout T)(src); - } - - // structural downcast for struct target - auto unwrap(Source)(inout Source src) - if (is(Target == struct)) - { - alias T = Select!(is(Source == shared), shared Target, Target); - auto upCastSource = dynamicCast!Object(src); // remove qualifier - do - { - if (auto a = dynamicCast!(Structural!Object)(upCastSource)) - { - upCastSource = a._wrap_getSource(); - } - else if (auto a = dynamicCast!(Structural!T)(upCastSource)) - { - return a._wrap_getSource(); - } - else - { - static if (hasMember!(Source, "_wrap_getSource")) - return unwrap!Target(src._wrap_getSource()); - else - break; - } - } while (upCastSource); - import std.conv : ConvException; - throw new ConvException(unwrapExceptionText!(Source,Target)); - } - // structural downcast for class target - auto unwrap(Source)(inout Source src) - if (!is(Target : Source) && !is(Target == struct)) - { - alias T = Select!(is(Source == shared), shared Target, Target); - Object upCastSource = dynamicCast!(Object)(src); // remove qualifier - do - { - // Unwrap classes - if (auto a = dynamicCast!(Structural!Object)(upCastSource)) - { - if (auto d = dynamicCast!(inout T)(upCastSource = a._wrap_getSource())) - return d; - } - // Unwrap a structure of type T - else if (auto a = dynamicCast!(Structural!T)(upCastSource)) - { - return a._wrap_getSource(); - } - // Unwrap class that already inherited from interface - else if (auto d = dynamicCast!(inout T)(upCastSource)) - { - return d; - } - // Recurse to find the struct Target within a wrapped tree - else - { - static if (hasMember!(Source, "_wrap_getSource")) - return unwrap!Target(src._wrap_getSource()); - else - break; - } - } while (upCastSource); - return null; - } - } -} - -@system unittest -{ - // Validate const/immutable - class A - { - int draw() { return 1; } - int draw(int v) { return v; } - - int draw() const { return 2; } - int draw() shared { return 3; } - int draw() shared const { return 4; } - int draw() immutable { return 5; } - } - interface Drawable - { - int draw(); - int draw() const; - int draw() shared; - int draw() shared const; - int draw() immutable; - } - interface Drawable2 - { - int draw(int v); - } - - auto ma = new A(); - auto sa = new shared A(); - auto ia = new immutable A(); - { - Drawable md = ma.wrap!Drawable; - const Drawable cd = ma.wrap!Drawable; - shared Drawable sd = sa.wrap!Drawable; - shared const Drawable scd = sa.wrap!Drawable; - immutable Drawable id = ia.wrap!Drawable; - assert( md.draw() == 1); - assert( cd.draw() == 2); - assert( sd.draw() == 3); - assert(scd.draw() == 4); - assert( id.draw() == 5); - } - { - Drawable2 d = ma.wrap!Drawable2; - static assert(!__traits(compiles, d.draw())); - assert(d.draw(10) == 10); - } -} - -// https://issues.dlang.org/show_bug.cgi?id=10377 -@system unittest -{ - import std.algorithm, std.range; - - interface MyInputRange(T) - { - @property T front(); - void popFront(); - @property bool empty(); - } - - //auto o = iota(0,10,1).inputRangeObject(); - //pragma(msg, __traits(allMembers, typeof(o))); - auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)(); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); -} - -// https://issues.dlang.org/show_bug.cgi?id=10536 -@system unittest -{ - interface Interface - { - int foo(); - } - class Pluggable - { - int foo() { return 1; } - @disable void opCast(T, this X)(); // ! - } - - Interface i = new Pluggable().wrap!Interface; - assert(i.foo() == 1); -} - -// https://issues.dlang.org/show_bug.cgi?id=10538 -@system unittest -{ - interface Interface - { - int foo(); - int bar(int); - } - class Pluggable - { - int opDispatch(string name, A...)(A args) { return 100; } - } - - Interface i = wrap!Interface(new Pluggable()); - assert(i.foo() == 100); - assert(i.bar(10) == 100); -} - -// Concat all Targets function members into one tuple -private template ConcatInterfaceMembers(Targets...) -{ - static if (Targets.length == 0) - alias ConcatInterfaceMembers = AliasSeq!(); - else static if (Targets.length == 1) - alias ConcatInterfaceMembers - = AliasSeq!(GetOverloadedMethods!(Targets[0])); - else - alias ConcatInterfaceMembers = AliasSeq!( - GetOverloadedMethods!(Targets[0]), - ConcatInterfaceMembers!(Targets[1..$])); -} -// Remove duplicated functions based on the identifier name and function type covariance -private template UniqMembers(members...) -{ - template FuncInfo(string s, F) - { - enum name = s; - alias type = F; - } - - static if (members.length == 0) - alias UniqMembers = AliasSeq!(); - else - { - alias func = members[0]; - enum name = __traits(identifier, func); - alias type = FunctionTypeOf!func; - template check(size_t i, mem...) - { - static if (i >= mem.length) - enum ptrdiff_t check = -1; - else static if - (__traits(identifier, func) == __traits(identifier, mem[i]) && - !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void)) - { - enum ptrdiff_t check = i; - } - else - enum ptrdiff_t check = check!(i + 1, mem); - } - enum ptrdiff_t x = 1 + check!(0, members[1 .. $]); - static if (x >= 1) - { - alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x])); - alias remain = UniqMembers!(members[1 .. x], members[x + 1 .. $]); - - static if (remain.length >= 1 && remain[0].name == name && - !is(DerivedFunctionType!(typex, remain[0].type) == void)) - { - alias F = DerivedFunctionType!(typex, remain[0].type); - alias UniqMembers = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]); - } - else - alias UniqMembers = AliasSeq!(FuncInfo!(name, typex), remain); - } - else - { - alias UniqMembers = AliasSeq!(FuncInfo!(name, type), UniqMembers!(members[1 .. $])); - } - } -} - -// find a function from Fs that has same identifier and covariant type with f -private template findCovariantFunction(alias finfo, Source, Fs...) -{ - template check(size_t i = 0) - { - static if (i >= Fs.length) - enum ptrdiff_t check = -1; - else - { - enum ptrdiff_t check = - (finfo.name == __traits(identifier, Fs[i])) && - isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type) - ? i : check!(i + 1); - } - } - enum x = check!(); - static if (x == -1 && is(typeof(Source.opDispatch))) - { - alias Params = Parameters!(finfo.type); - enum ptrdiff_t findCovariantFunction = - is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) || - is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init))) - ? ptrdiff_t.max : -1; - } - else - enum ptrdiff_t findCovariantFunction = x; -} - -/** -Type constructor for final (aka head-const) variables. - -Final variables cannot be directly mutated or rebound, but references -reached through the variable are typed with their original mutability. -It is equivalent to `final` variables in D1 and Java, as well as -`readonly` variables in C#. - -When `T` is a `const` or `immutable` type, `Final` aliases -to `T`. -*/ -template Final(T) -{ -static if (is(T == const) || is(T == immutable)) - alias Final = T; -else -{ - struct Final - { - import std.typecons : Proxy; - - private T final_value; - mixin Proxy!final_value; - - /** - * Construction is forwarded to the underlying type. - */ - this(T other) - { - this.final_value = other; - } - - /// Ditto - this(Args...)(auto ref Args args) - if (__traits(compiles, T(args))) - { - static assert((!is(T == struct) && !is(T == union)) || !isNested!T, - "Non-static nested type " ~ fullyQualifiedName!T ~ " must be " ~ - "constructed explicitly at the call-site (e.g. auto s = " ~ - "makeFinal(" ~ T.stringof ~ "(...));)"); - this.final_value = T(args); - } - - // Attaching function attributes gives less noisy error messages - pure nothrow @safe @nogc - { - /++ - + All operators, including member access, are forwarded to the - + underlying value of type `T` except for these mutating operators, - + which are disabled. - +/ - void opAssign(Other)(Other other) - { - static assert(0, typeof(this).stringof ~ - " cannot be reassigned."); - } - - /// Ditto - void opOpAssign(string op, Other)(Other other) - { - static assert(0, typeof(this).stringof ~ - " cannot be reassigned."); - } - - /// Ditto - void opUnary(string op : "--")() - { - static assert(0, typeof(this).stringof ~ - " cannot be mutated."); - } - - /// Ditto - void opUnary(string op : "++")() - { - static assert(0, typeof(this).stringof ~ - " cannot be mutated."); - } - } - - /** - * - * `Final!T` implicitly converts to an rvalue of type `T` through - * `AliasThis`. - */ - inout(T) final_get() inout - { - return final_value; - } - - /// Ditto - alias final_get this; - - /// Ditto - auto ref opUnary(string op)() - if (__traits(compiles, mixin(op ~ "T.init"))) - { - return mixin(op ~ "this.final_value"); - } - } -} -} - -/// Ditto -Final!T makeFinal(T)(T t) -{ - return Final!T(t); -} - -/// `Final` can be used to create class references which cannot be rebound: -pure nothrow @safe unittest -{ - static class A - { - int i; - - this(int i) pure nothrow @nogc @safe - { - this.i = i; - } - } - - auto a = makeFinal(new A(42)); - assert(a.i == 42); - - //a = new A(24); // Reassignment is illegal, - a.i = 24; // But fields are still mutable. - - assert(a.i == 24); -} - -/// `Final` can also be used to create read-only data fields without using transitive immutability: -pure nothrow @safe unittest -{ - static class A - { - int i; - - this(int i) pure nothrow @nogc @safe - { - this.i = i; - } - } - - static class B - { - Final!A a; - - this(A a) pure nothrow @nogc @safe - { - this.a = a; // Construction, thus allowed. - } - } - - auto b = new B(new A(42)); - assert(b.a.i == 42); - - // b.a = new A(24); // Reassignment is illegal, - b.a.i = 24; // but `a` is still mutable. - - assert(b.a.i == 24); -} - -pure nothrow @safe unittest -{ - static class A { int i; } - static assert(!is(Final!A == A)); - static assert(is(Final!(const A) == const A)); - static assert(is(Final!(immutable A) == immutable A)); - - Final!A a = new A; - static assert(!__traits(compiles, a = new A)); - - assert(a.i == 0); - a.i = 42; - assert(a.i == 42); - - Final!int i = 42; - static assert(!__traits(compiles, i = 24)); - static assert(!__traits(compiles, --i)); - static assert(!__traits(compiles, ++i)); - assert(i == 42); - int iCopy = i; - assert(iCopy == 42); - iCopy = -i; // non-mutating unary operators must work - assert(iCopy == -42); - - static struct S - { - int i; - - pure nothrow @safe @nogc: - this(int i){} - this(string s){} - this(int i, string s, float f){ this.i = i; } - } - - Final!S sint = 42; - Final!S sstr = "foo"; - static assert(!__traits(compiles, sint = sstr)); - - auto sboth = Final!S(42, "foo", 3.14); - assert(sboth.i == 42); - - sboth.i = 24; - assert(sboth.i == 24); - - struct NestedS - { - int i; - int get() pure nothrow @safe @nogc { return sboth.i + i; } - } - - // Nested structs must be constructed at the call-site - static assert(!__traits(compiles, Final!NestedS(6))); - auto s = makeFinal(NestedS(6)); - assert(s.i == 6); - assert(s.get == 30); - - class NestedC - { - int i; - - pure nothrow @safe @nogc: - this(int i) { this.i = i; } - int get() { return sboth.i + i; } - } - - auto c = makeFinal(new NestedC(6)); - assert(c.i == 6); - assert(c.get == 30); -} - -pure nothrow @safe unittest -{ - auto arr = makeFinal([1, 2, 3]); - static assert(!__traits(compiles, arr = null)); - static assert(!__traits(compiles, arr ~= 4)); - assert((arr ~ 4) == [1, 2, 3, 4]); -} - -// https://issues.dlang.org/show_bug.cgi?id=17270 -pure nothrow @nogc @system unittest -{ - int i = 1; - Final!(int*) fp = &i; - assert(*fp == 1); - static assert(!__traits(compiles, - fp = &i // direct assignment - )); - static assert(is(typeof(*fp) == int)); - *fp = 2; // indirect assignment - assert(*fp == 2); - int* p = fp; - assert(*p == 2); -} - -pure nothrow @system unittest -{ - Final!(int[]) arr; - // static assert(!__traits(compiles, - // arr.length = 10; // bug! - // )); - static assert(!__traits(compiles, - arr.ptr = null - )); - static assert(!__traits(compiles, - arr.ptr++ - )); -} diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d index c1c5cd2..b239901 100644 --- a/libphobos/src/std/getopt.d +++ b/libphobos/src/std/getopt.d @@ -1835,7 +1835,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, st assert(flag); } -@safe unittest // Delegates as callbacks +@system unittest // Delegates as callbacks { alias TwoArgOptionHandler = void delegate(string option, string value) @safe; diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index c6e746a..ac397e5 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -1537,19 +1537,15 @@ if (isOutputRange!(Out,char)) toStringImpl!char(str); } - // recursive @safe inference is broken here - // workaround: if json.put is @safe, we should be too, - // so annotate the recursion as @safe manually - static if (isSafe!({ json.put(""); })) - { - void delegate(ref const JSONValue, ulong) @safe toValue; - } - else - { - void delegate(ref const JSONValue, ulong) @system toValue; - } + /* make the function infer @system when json.put() is @system + */ + if (0) + json.put(' '); - void toValueImpl(ref const JSONValue value, ulong indentLevel) + /* Mark as @trusted because json.put() may be @system. This has difficulty + * inferring @safe because it is recursive. + */ + void toValueImpl(ref const JSONValue value, ulong indentLevel) @trusted { void putTabs(ulong additionalIndent = 0) { @@ -1594,7 +1590,7 @@ if (isOutputRange!(Out,char)) json.put(':'); if (pretty) json.put(' '); - toValue(member, indentLevel + 1); + toValueImpl(member, indentLevel + 1); } } @@ -1631,7 +1627,7 @@ if (isOutputRange!(Out,char)) if (i) putCharAndEOL(','); putTabs(1); - toValue(el, indentLevel + 1); + toValueImpl(el, indentLevel + 1); } putEOL(); putTabs(); @@ -1710,9 +1706,7 @@ if (isOutputRange!(Out,char)) } } - toValue = &toValueImpl; - - toValue(root, 0); + toValueImpl(root, 0); } // https://issues.dlang.org/show_bug.cgi?id=12897 diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d new file mode 100644 index 0000000..be2bd8d --- /dev/null +++ b/libphobos/src/std/logger/core.d @@ -0,0 +1,3049 @@ +// Written in the D programming language. +/** +Source: $(PHOBOSSRC std/logger/core.d) +*/ +module std.logger.core; + +import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder; +import core.sync.mutex : Mutex; +import std.datetime.date : DateTime; +import std.datetime.systime : Clock, SysTime; +import std.range.primitives; +import std.traits; + +import std.logger.filelogger; + +/** This functions is used at runtime to determine if a `LogLevel` is +active. The same previously defined version statements are used to disable +certain levels. Again the version statements are associated with a compile +unit and can therefore not disable logging in other compile units. +pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc +*/ +bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL, + LogLevel globalLL, lazy bool condition = true) @safe +{ + return ll >= globalLL + && ll >= loggerLL + && ll != LogLevel.off + && globalLL != LogLevel.off + && loggerLL != LogLevel.off + && condition; +} + +/* This function formates a `SysTime` into an `OutputRange`. + +The `SysTime` is formatted similar to +$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part. +The fractional second part is in milliseconds and is always 3 digits. +*/ +void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time) +if (isOutputRange!(OutputRange,string)) +{ + import std.format.write : formattedWrite; + + const auto dt = cast(DateTime) time; + const auto fsec = time.fracSecs.total!"msecs"; + + formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + fsec); +} + +/** This function logs data. + +In order for the data to be processed, the `LogLevel` of the log call must +be greater or equal to the `LogLevel` of the `sharedLog` and the +`defaultLogLevel`; additionally the condition passed must be `true`. + +Params: + ll = The `LogLevel` used by this log call. + condition = The condition must be `true` for the data to be logged. + args = The data that should be logged. + +Example: +-------------------- +log(LogLevel.warning, true, "Hello World", 3.1415); +-------------------- +*/ +void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, + lazy bool condition, lazy A args) +if (args.length != 1) +{ + stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) + (ll, condition, args); +} + +/// Ditto +void log(T, string moduleName = __MODULE__)(const LogLevel ll, + lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__) +{ + stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName, + prettyFuncName); +} + +/** This function logs data. + +In order for the data to be processed the `LogLevel` of the log call must +be greater or equal to the `LogLevel` of the `sharedLog`. + +Params: + ll = The `LogLevel` used by this log call. + args = The data that should be logged. + +Example: +-------------------- +log(LogLevel.warning, "Hello World", 3.1415); +-------------------- +*/ +void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) +if (args.length > 1 && !is(Unqual!(A[0]) : bool)) +{ + stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) + (ll, args); +} + +/// Ditto +void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg, + int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__) +{ + stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName, + moduleName); +} + +/** This function logs data. + +In order for the data to be processed the `LogLevel` of the +`sharedLog` must be greater or equal to the `defaultLogLevel` +add the condition passed must be `true`. + +Params: + condition = The condition must be `true` for the data to be logged. + args = The data that should be logged. + +Example: +-------------------- +log(true, "Hello World", 3.1415); +-------------------- +*/ +void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) +if (args.length != 1) +{ + stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) + (stdThreadLocalLog.logLevel, condition, args); +} + +/// Ditto +void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg, + int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__) +{ + stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel, + condition, arg, line, file, funcName, prettyFuncName); +} + +/** This function logs data. + +In order for the data to be processed the `LogLevel` of the +`sharedLog` must be greater or equal to the `defaultLogLevel`. + +Params: + args = The data that should be logged. + +Example: +-------------------- +log("Hello World", 3.1415); +-------------------- +*/ +void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy A args) +if ((args.length > 1 && !is(Unqual!(A[0]) : bool) + && !is(Unqual!(A[0]) == LogLevel)) + || args.length == 0) +{ + stdThreadLocalLog.log!(line, file, funcName, + prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args); +} + +void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__) +{ + stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file, + funcName, prettyFuncName, moduleName); +} + +/** This function logs data in a `printf`-style manner. + +In order for the data to be processed the `LogLevel` of the log call must +be greater or equal to the `LogLevel` of the `sharedLog` and the +`defaultLogLevel` additionally the condition passed must be `true`. + +Params: + ll = The `LogLevel` used by this log call. + condition = The condition must be `true` for the data to be logged. + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +logf(LogLevel.warning, true, "Hello World %f", 3.1415); +-------------------- +*/ +void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, + lazy bool condition, lazy string msg, lazy A args) +{ + stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) + (ll, condition, msg, args); +} + +/** This function logs data in a `printf`-style manner. + +In order for the data to be processed the `LogLevel` of the log call must +be greater or equal to the `LogLevel` of the `sharedLog` and the +`defaultLogLevel`. + +Params: + ll = The `LogLevel` used by this log call. + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +logf(LogLevel.warning, "Hello World %f", 3.1415); +-------------------- +*/ +void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg, + lazy A args) +{ + stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) + (ll, msg, args); +} + +/** This function logs data in a `printf`-style manner. + +In order for the data to be processed the `LogLevel` of the log call must +be greater or equal to the `defaultLogLevel` additionally the condition +passed must be `true`. + +Params: + condition = The condition must be `true` for the data to be logged. + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +logf(true, "Hello World %f", 3.1415); +-------------------- +*/ +void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, + lazy string msg, lazy A args) +{ + stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) + (stdThreadLocalLog.logLevel, condition, msg, args); +} + +/** This function logs data in a `printf`-style manner. + +In order for the data to be processed the `LogLevel` of the log call must +be greater or equal to the `defaultLogLevel`. + +Params: + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +logf("Hello World %f", 3.1415); +-------------------- +*/ +void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) +{ + stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName) + (stdThreadLocalLog.logLevel, msg, args); +} + +/** This template provides the global log functions with the `LogLevel` +is encoded in the function name. + +The aliases following this template create the public names of these log +functions. +*/ +template defaultLogFunction(LogLevel ll) +{ + void defaultLogFunction(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy A args) + if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + { + stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, + prettyFuncName, moduleName)(args); + } + + void defaultLogFunction(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) + { + stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, + prettyFuncName, moduleName)(condition, args); + } +} + +/** This function logs data to the `stdThreadLocalLog`, optionally depending +on a condition. + +In order for the resulting log message to be logged the `LogLevel` must +be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and +must be greater or equal than the global `LogLevel`. +Additionally the `LogLevel` must be greater or equal than the `LogLevel` +of the `stdSharedLogger`. +If a condition is given, it must evaluate to `true`. + +Params: + condition = The condition must be `true` for the data to be logged. + args = The data that should be logged. + +Example: +-------------------- +trace(1337, "is number"); +info(1337, "is number"); +error(1337, "is number"); +critical(1337, "is number"); +fatal(1337, "is number"); +trace(true, 1337, "is number"); +info(false, 1337, "is number"); +error(true, 1337, "is number"); +critical(false, 1337, "is number"); +fatal(true, 1337, "is number"); +-------------------- +*/ +alias trace = defaultLogFunction!(LogLevel.trace); +/// Ditto +alias info = defaultLogFunction!(LogLevel.info); +/// Ditto +alias warning = defaultLogFunction!(LogLevel.warning); +/// Ditto +alias error = defaultLogFunction!(LogLevel.error); +/// Ditto +alias critical = defaultLogFunction!(LogLevel.critical); +/// Ditto +alias fatal = defaultLogFunction!(LogLevel.fatal); + +/** This template provides the global `printf`-style log functions with +the `LogLevel` is encoded in the function name. + +The aliases following this template create the public names of the log +functions. +*/ +template defaultLogFunctionf(LogLevel ll) +{ + void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) + { + stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, + prettyFuncName, moduleName)(msg, args); + } + + void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, + lazy string msg, lazy A args) + { + stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, + prettyFuncName, moduleName)(condition, msg, args); + } +} + +/** This function logs data to the `sharedLog` in a `printf`-style +manner. + +In order for the resulting log message to be logged the `LogLevel` must +be greater or equal than the `LogLevel` of the `sharedLog` and +must be greater or equal than the global `LogLevel`. +Additionally the `LogLevel` must be greater or equal than the `LogLevel` +of the `stdSharedLogger`. + +Params: + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +tracef("is number %d", 1); +infof("is number %d", 2); +errorf("is number %d", 3); +criticalf("is number %d", 4); +fatalf("is number %d", 5); +-------------------- + +The second version of the function logs data to the `sharedLog` in a $(D +printf)-style manner. + +In order for the resulting log message to be logged the `LogLevel` must +be greater or equal than the `LogLevel` of the `sharedLog` and +must be greater or equal than the global `LogLevel`. +Additionally the `LogLevel` must be greater or equal than the `LogLevel` +of the `stdSharedLogger`. + +Params: + condition = The condition must be `true` for the data to be logged. + msg = The `printf`-style string. + args = The data that should be logged. + +Example: +-------------------- +tracef(false, "is number %d", 1); +infof(false, "is number %d", 2); +errorf(true, "is number %d", 3); +criticalf(true, "is number %d", 4); +fatalf(someFunct(), "is number %d", 5); +-------------------- +*/ +alias tracef = defaultLogFunctionf!(LogLevel.trace); +/// Ditto +alias infof = defaultLogFunctionf!(LogLevel.info); +/// Ditto +alias warningf = defaultLogFunctionf!(LogLevel.warning); +/// Ditto +alias errorf = defaultLogFunctionf!(LogLevel.error); +/// Ditto +alias criticalf = defaultLogFunctionf!(LogLevel.critical); +/// Ditto +alias fatalf = defaultLogFunctionf!(LogLevel.fatal); + +private struct MsgRange +{ + import std.traits : isSomeString, isSomeChar; + + private Logger log; + + this(Logger log) @safe + { + this.log = log; + } + + void put(T)(T msg) @safe + if (isSomeString!T) + { + log.logMsgPart(msg); + } + + void put(dchar elem) @safe + { + import std.utf : encode; + char[4] buffer; + size_t len = encode(buffer, elem); + log.logMsgPart(buffer[0 .. len]); + } +} + +private void formatString(A...)(MsgRange oRange, A args) +{ + import std.format.write : formattedWrite; + + foreach (arg; args) + { + formattedWrite(oRange, "%s", arg); + } +} + +@system unittest +{ + void dummy() @safe + { + auto tl = new TestLogger(); + auto dst = MsgRange(tl); + formatString(dst, "aaa", "bbb"); + } + + dummy(); +} + +/** +There are eight usable logging level. These level are $(I all), $(I trace), +$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off). +If a log function with `LogLevel.fatal` is called the shutdown handler of +that logger is called. +*/ +enum LogLevel : ubyte +{ + all = 1, /** Lowest possible assignable `LogLevel`. */ + trace = 32, /** `LogLevel` for tracing the execution of the program. */ + info = 64, /** This level is used to display information about the + program. */ + warning = 96, /** warnings about the program should be displayed with this + level. */ + error = 128, /** Information about errors should be logged with this + level.*/ + critical = 160, /** Messages that inform about critical errors should be + logged with this level. */ + fatal = 192, /** Log messages that describe fatal errors should use this + level. */ + off = ubyte.max /** Highest possible `LogLevel`. */ +} + +/** This class is the base of every logger. In order to create a new kind of +logger a deriving class needs to implement the `writeLogMsg` method. By +default this is not thread-safe. + +It is also possible to `override` the three methods `beginLogMsg`, +`logMsgPart` and `finishLogMsg` together, this option gives more +flexibility. +*/ +abstract class Logger +{ + import std.array : appender, Appender; + import std.concurrency : thisTid, Tid; + + /** LogEntry is a aggregation combining all information associated + with a log message. This aggregation will be passed to the method + writeLogMsg. + */ + protected struct LogEntry + { + /// the filename the log function was called from + string file; + /// the line number the log function was called from + int line; + /// the name of the function the log function was called from + string funcName; + /// the pretty formatted name of the function the log function was + /// called from + string prettyFuncName; + /// the name of the module the log message is coming from + string moduleName; + /// the `LogLevel` associated with the log message + LogLevel logLevel; + /// thread id of the log message + Tid threadId; + /// the time the message was logged + SysTime timestamp; + /// the message of the log message + string msg; + /// A refernce to the `Logger` used to create this `LogEntry` + Logger logger; + } + + /** + Every subclass of `Logger` has to call this constructor from their + constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal + handler will throw an `Error` if a log call is made with level + `LogLevel.fatal`. + + Params: + lv = `LogLevel` to use for this `Logger` instance. + */ + this(this This)(LogLevel lv) + { + this.logLevel_ = lv; + this.fatalHandler_ = delegate() { + throw new Error("A fatal log message was logged"); + }; + + this.mutex = new typeof(mutex)(); + } + + /** A custom logger must implement this method in order to work in a + `MultiLogger` and `ArrayLogger`. + + Params: + payload = All information associated with call to log function. + + See_Also: beginLogMsg, logMsgPart, finishLogMsg + */ + abstract protected void writeLogMsg(ref LogEntry payload) @safe; + + /* The default implementation will use an `std.array.appender` + internally to construct the message string. This means dynamic, + GC memory allocation. A logger can avoid this allocation by + reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`. + `beginLogMsg` is always called first, followed by any number of calls + to `logMsgPart` and one call to `finishLogMsg`. + + As an example for such a custom `Logger` compare this: + ---------------- + class CLogger : Logger + { + override void beginLogMsg(string file, int line, string funcName, + string prettyFuncName, string moduleName, LogLevel logLevel, + Tid threadId, SysTime timestamp) + { + ... logic here + } + + override void logMsgPart(const(char)[] msg) + { + ... logic here + } + + override void finishLogMsg() + { + ... logic here + } + + void writeLogMsg(ref LogEntry payload) + { + this.beginLogMsg(payload.file, payload.line, payload.funcName, + payload.prettyFuncName, payload.moduleName, payload.logLevel, + payload.threadId, payload.timestamp, payload.logger); + + this.logMsgPart(payload.msg); + this.finishLogMsg(); + } + } + ---------------- + */ + protected void beginLogMsg(string file, int line, string funcName, + string prettyFuncName, string moduleName, LogLevel logLevel, + Tid threadId, SysTime timestamp, Logger logger) + @safe + { + msgAppender = appender!string(); + header = LogEntry(file, line, funcName, prettyFuncName, + moduleName, logLevel, threadId, timestamp, null, logger); + } + + /** Logs a part of the log message. */ + protected void logMsgPart(scope const(char)[] msg) @safe + { + msgAppender.put(msg); + } + + /** Signals that the message has been written and no more calls to + `logMsgPart` follow. */ + protected void finishLogMsg() @safe + { + header.msg = msgAppender.data; + this.writeLogMsg(header); + } + + /** The `LogLevel` determines if the log call are processed or dropped + by the `Logger`. In order for the log call to be processed the + `LogLevel` of the log call must be greater or equal to the `LogLevel` + of the `logger`. + + These two methods set and get the `LogLevel` of the used `Logger`. + + Example: + ----------- + auto f = new FileLogger(stdout); + f.logLevel = LogLevel.info; + assert(f.logLevel == LogLevel.info); + ----------- + */ + @property final LogLevel logLevel() const pure @safe @nogc + { + return trustedLoad(this.logLevel_); + } + + /// Ditto + @property final void logLevel(const LogLevel lv) @safe @nogc + { + atomicStore(this.logLevel_, lv); + } + + /** This `delegate` is called in case a log message with + `LogLevel.fatal` gets logged. + + By default an `Error` will be thrown. + */ + @property final void delegate() fatalHandler() @safe @nogc + { + synchronized (mutex) return this.fatalHandler_; + } + + /// Ditto + @property final void fatalHandler(void delegate() @safe fh) @safe @nogc + { + synchronized (mutex) this.fatalHandler_ = fh; + } + + /** This method allows forwarding log entries from one logger to another. + + `forwardMsg` will ensure proper synchronization and then call + `writeLogMsg`. This is an API for implementing your own loggers and + should not be called by normal user code. A notable difference from other + logging functions is that the `globalLogLevel` wont be evaluated again + since it is assumed that the caller already checked that. + */ + void forwardMsg(ref LogEntry payload) @trusted + { + if (isLoggingEnabled(payload.logLevel, this.logLevel_, + globalLogLevel)) + { + this.writeLogMsg(payload); + + if (payload.logLevel == LogLevel.fatal) + this.fatalHandler_(); + } + } + + /** This template provides the log functions for the `Logger` `class` + with the `LogLevel` encoded in the function name. + + For further information see the the two functions defined inside of this + template. + + The aliases following this template create the public names of these log + functions. + */ + template memLogFunctions(LogLevel ll) + { + /** This function logs data to the used `Logger`. + + In order for the resulting log message to be logged the `LogLevel` + must be greater or equal than the `LogLevel` of the used `Logger` + and must be greater or equal than the global `LogLevel`. + + Params: + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.trace(1337, "is number"); + s.info(1337, "is number"); + s.error(1337, "is number"); + s.critical(1337, "is number"); + s.fatal(1337, "is number"); + -------------------- + */ + void logImpl(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy A args) + if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + static if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` depending on a + condition. + + In order for the resulting log message to be logged the `LogLevel` must + be greater or equal than the `LogLevel` of the used `Logger` and + must be greater or equal than the global `LogLevel` additionally the + condition passed must be `true`. + + Params: + condition = The condition must be `true` for the data to be logged. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.trace(true, 1337, "is number"); + s.info(false, 1337, "is number"); + s.error(true, 1337, "is number"); + s.critical(false, 1337, "is number"); + s.fatal(true, 1337, "is number"); + -------------------- + */ + void logImpl(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, + lazy A args) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, + condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + static if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` in a + `printf`-style manner. + + In order for the resulting log message to be logged the `LogLevel` + must be greater or equal than the `LogLevel` of the used `Logger` + and must be greater or equal than the global `LogLevel` additionally + the passed condition must be `true`. + + Params: + condition = The condition must be `true` for the data to be logged. + msg = The `printf`-style string. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stderr); + s.tracef(true, "is number %d", 1); + s.infof(true, "is number %d", 2); + s.errorf(false, "is number %d", 3); + s.criticalf(someFunc(), "is number %d", 4); + s.fatalf(true, "is number %d", 5); + -------------------- + */ + void logImplf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, + lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, + condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + static if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` in a + `printf`-style manner. + + In order for the resulting log message to be logged the `LogLevel` must + be greater or equal than the `LogLevel` of the used `Logger` and + must be greater or equal than the global `LogLevel`. + + Params: + msg = The `printf`-style string. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stderr); + s.tracef("is number %d", 1); + s.infof("is number %d", 2); + s.errorf("is number %d", 3); + s.criticalf("is number %d", 4); + s.fatalf("is number %d", 5); + -------------------- + */ + void logImplf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + static if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + } + + /// Ditto + alias trace = memLogFunctions!(LogLevel.trace).logImpl; + /// Ditto + alias tracef = memLogFunctions!(LogLevel.trace).logImplf; + /// Ditto + alias info = memLogFunctions!(LogLevel.info).logImpl; + /// Ditto + alias infof = memLogFunctions!(LogLevel.info).logImplf; + /// Ditto + alias warning = memLogFunctions!(LogLevel.warning).logImpl; + /// Ditto + alias warningf = memLogFunctions!(LogLevel.warning).logImplf; + /// Ditto + alias error = memLogFunctions!(LogLevel.error).logImpl; + /// Ditto + alias errorf = memLogFunctions!(LogLevel.error).logImplf; + /// Ditto + alias critical = memLogFunctions!(LogLevel.critical).logImpl; + /// Ditto + alias criticalf = memLogFunctions!(LogLevel.critical).logImplf; + /// Ditto + alias fatal = memLogFunctions!(LogLevel.fatal).logImpl; + /// Ditto + alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf; + + /** This method logs data with the `LogLevel` of the used `Logger`. + + This method takes a `bool` as first argument. In order for the + data to be processed the `bool` must be `true` and the `LogLevel` + of the Logger must be greater or equal to the global `LogLevel`. + + Params: + args = The data that should be logged. + condition = The condition must be `true` for the data to be logged. + args = The data that is to be logged. + + Returns: The logger used by the logging function as reference. + + Example: + -------------------- + auto l = new StdioLogger(); + l.log(1337); + -------------------- + */ + void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, + lazy bool condition, lazy A args) + if (args.length != 1) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /// Ditto + void log(T, string moduleName = __MODULE__)(const LogLevel ll, + lazy bool condition, lazy T args, int line = __LINE__, + string file = __FILE__, string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` with a specific + `LogLevel`. + + In order for the resulting log message to be logged the `LogLevel` + must be greater or equal than the `LogLevel` of the used `Logger` + and must be greater or equal than the global `LogLevel`. + + Params: + ll = The specific `LogLevel` used for logging the log message. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.log(LogLevel.trace, 1337, "is number"); + s.log(LogLevel.info, 1337, "is number"); + s.log(LogLevel.warning, 1337, "is number"); + s.log(LogLevel.error, 1337, "is number"); + s.log(LogLevel.fatal, 1337, "is number"); + -------------------- + */ + void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) + if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /// Ditto + void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, + string file = __FILE__, string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__) + { + synchronized (mutex) + { + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` depending on a + explicitly passed condition with the `LogLevel` of the used + `Logger`. + + In order for the resulting log message to be logged the `LogLevel` + of the used `Logger` must be greater or equal than the global + `LogLevel` and the condition must be `true`. + + Params: + condition = The condition must be `true` for the data to be logged. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.log(true, 1337, "is number"); + s.log(true, 1337, "is number"); + s.log(true, 1337, "is number"); + s.log(false, 1337, "is number"); + s.log(false, 1337, "is number"); + -------------------- + */ + void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) + if (args.length != 1) + { + synchronized (mutex) + { + if (isLoggingEnabled(this.logLevel_, this.logLevel_, + globalLogLevel, condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /// Ditto + void log(T)(lazy bool condition, lazy T args, int line = __LINE__, + string file = __FILE__, string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__) + { + synchronized (mutex) + { + if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, + condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` with the `LogLevel` + of the used `Logger`. + + In order for the resulting log message to be logged the `LogLevel` + of the used `Logger` must be greater or equal than the global + `LogLevel`. + + Params: + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.log(1337, "is number"); + s.log(info, 1337, "is number"); + s.log(1337, "is number"); + s.log(1337, "is number"); + s.log(1337, "is number"); + -------------------- + */ + void log(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy A args) + if ((args.length > 1 + && !is(Unqual!(A[0]) : bool) + && !is(immutable A[0] == immutable LogLevel)) + || args.length == 0) + { + synchronized (mutex) + { + if (isLoggingEnabled(this.logLevel_, this.logLevel_, + globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + auto writer = MsgRange(this); + formatString(writer, args); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /// Ditto + void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__) + { + synchronized (mutex) + { + if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + auto writer = MsgRange(this); + formatString(writer, arg); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` with a specific + `LogLevel` and depending on a condition in a `printf`-style manner. + + In order for the resulting log message to be logged the `LogLevel` + must be greater or equal than the `LogLevel` of the used `Logger` + and must be greater or equal than the global `LogLevel` and the + condition must be `true`. + + Params: + ll = The specific `LogLevel` used for logging the log message. + condition = The condition must be `true` for the data to be logged. + msg = The format string used for this log call. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number"); + s.logf(LogLevel.info, true ,"%d %s", 1337, "is number"); + s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number"); + s.logf(LogLevel.error, false ,"%d %s", 1337, "is number"); + s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number"); + -------------------- + */ + void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, + lazy bool condition, lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` with a specific + `LogLevel` in a `printf`-style manner. + + In order for the resulting log message to be logged the `LogLevel` + must be greater or equal than the `LogLevel` of the used `Logger` + and must be greater or equal than the global `LogLevel`. + + Params: + ll = The specific `LogLevel` used for logging the log message. + msg = The format string used for this log call. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.logf(LogLevel.trace, "%d %s", 1337, "is number"); + s.logf(LogLevel.info, "%d %s", 1337, "is number"); + s.logf(LogLevel.warning, "%d %s", 1337, "is number"); + s.logf(LogLevel.error, "%d %s", 1337, "is number"); + s.logf(LogLevel.fatal, "%d %s", 1337, "is number"); + -------------------- + */ + void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(const LogLevel ll, + lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, ll, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + if (ll == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This function logs data to the used `Logger` depending on a + condition with the `LogLevel` of the used `Logger` in a + `printf`-style manner. + + In order for the resulting log message to be logged the `LogLevel` + of the used `Logger` must be greater or equal than the global + `LogLevel` and the condition must be `true`. + + Params: + condition = The condition must be `true` for the data to be logged. + msg = The format string used for this log call. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.logf(true ,"%d %s", 1337, "is number"); + s.logf(true ,"%d %s", 1337, "is number"); + s.logf(true ,"%d %s", 1337, "is number"); + s.logf(false ,"%d %s", 1337, "is number"); + s.logf(true ,"%d %s", 1337, "is number"); + -------------------- + */ + void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy bool condition, + lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, + condition)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + /** This method logs data to the used `Logger` with the `LogLevel` + of the this `Logger` in a `printf`-style manner. + + In order for the data to be processed the `LogLevel` of the `Logger` + must be greater or equal to the global `LogLevel`. + + Params: + msg = The format string used for this log call. + args = The data that should be logged. + + Example: + -------------------- + auto s = new FileLogger(stdout); + s.logf("%d %s", 1337, "is number"); + s.logf("%d %s", 1337, "is number"); + s.logf("%d %s", 1337, "is number"); + s.logf("%d %s", 1337, "is number"); + s.logf("%d %s", 1337, "is number"); + -------------------- + */ + void logf(int line = __LINE__, string file = __FILE__, + string funcName = __FUNCTION__, + string prettyFuncName = __PRETTY_FUNCTION__, + string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) + { + synchronized (mutex) + { + import std.format.write : formattedWrite; + + if (isLoggingEnabled(this.logLevel_, this.logLevel_, + globalLogLevel)) + { + this.beginLogMsg(file, line, funcName, prettyFuncName, + moduleName, this.logLevel_, thisTid, Clock.currTime, this); + + auto writer = MsgRange(this); + formattedWrite(writer, msg, args); + + this.finishLogMsg(); + + if (this.logLevel_ == LogLevel.fatal) + this.fatalHandler_(); + } + } + } + + private void delegate() @safe fatalHandler_; + private shared LogLevel logLevel_ = LogLevel.info; + private Mutex mutex; + + protected Appender!string msgAppender; + protected LogEntry header; +} + +// Thread Global + +private shared Logger stdSharedDefaultLogger; +private shared Logger stdSharedLogger; +private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; + +/* This method returns the global default Logger. + * Marked @trusted because of excessive reliance on __gshared data + */ +private @property shared(Logger) defaultSharedLoggerImpl() @trusted +{ + import core.lifetime : emplace; + import std.stdio : stderr; + + __gshared align(__traits(classInstanceAlignment, FileLogger)) + void[__traits(classInstanceSize, FileLogger)] _buffer = void; + + import std.concurrency : initOnce; + initOnce!stdSharedDefaultLogger({ + auto buffer = cast(ubyte[]) _buffer; + return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info); + }()); + + return atomicLoad(stdSharedDefaultLogger); +} + +/** This property sets and gets the default `Logger`. Unless set to another +logger by the user, the default logger's log level is LogLevel.info. + +Example: +------------- +sharedLog = new FileLogger(yourFile); +------------- +The example sets a new `FileLogger` as new `sharedLog`. + +If at some point you want to use the original default logger again, you can +use $(D sharedLog = null;). This will put back the original. + +Note: +While getting and setting `sharedLog` is thread-safe, it has to be considered +that the returned reference is only a current snapshot and in the following +code, you must make sure no other thread reassigns to it between reading and +writing `sharedLog`. + +`sharedLog` is only thread-safe if the the used `Logger` is thread-safe. +The default `Logger` is thread-safe. +------------- +if (sharedLog !is myLogger) + sharedLog = new myLogger; +------------- +*/ +@property shared(Logger) sharedLog() @safe +{ + // If we have set up our own logger use that + if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger)) + { + return atomicLoad(logger); + } + else + { + // Otherwise resort to the default logger + return defaultSharedLoggerImpl; + } +} + +/// Ditto +@property void sharedLog(shared(Logger) logger) @safe +{ + atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger)); +} + +/** This methods get and set the global `LogLevel`. + +Every log message with a `LogLevel` lower as the global `LogLevel` +will be discarded before it reaches `writeLogMessage` method of any +`Logger`. +*/ +/* Implementation note: +For any public logging call, the global log level shall only be queried once on +entry. Otherwise when another threads changes the level, we would work with +different levels at different spots in the code. +*/ +@property LogLevel globalLogLevel() @safe @nogc +{ + return trustedLoad(stdLoggerGlobalLogLevel); +} + +/// Ditto +@property void globalLogLevel(LogLevel ll) @safe +{ + trustedStore(stdLoggerGlobalLogLevel, ll); +} + +// Thread Local + +/** The `StdForwardLogger` will always forward anything to the sharedLog. + +The `StdForwardLogger` will not throw if data is logged with $(D +LogLevel.fatal). +*/ +class StdForwardLogger : Logger +{ + /** The default constructor for the `StdForwardLogger`. + + Params: + lv = The `LogLevel` for the `MultiLogger`. By default the $(D + LogLevel) is `all`. + */ + this(const LogLevel lv = LogLevel.all) @safe + { + super(lv); + this.fatalHandler = delegate() {}; + } + + override protected void writeLogMsg(ref LogEntry payload) @trusted + { + synchronized (sharedLog.mutex) + { + (cast() sharedLog).forwardMsg(payload); + } + } +} + +/// +@safe unittest +{ + auto nl1 = new StdForwardLogger(LogLevel.all); +} + +@safe unittest +{ + import core.thread : Thread, msecs; + + static class RaceLogger : Logger + { + int value; + this() @safe shared + { + super(LogLevel.init); + } + override void writeLogMsg(ref LogEntry payload) @safe + { + import core.thread : Thread, msecs; + if (payload.msg == "foo") + { + value = 42; + () @trusted { Thread.sleep(100.msecs); }(); + assert(value == 42, "Another thread changed the value"); + } + else + { + () @trusted { Thread.sleep(50.msecs); } (); + value = 13; + } + } + } + + sharedLog = new shared RaceLogger; + scope(exit) { sharedLog = null; } + () @trusted { new Thread(() { log("foo"); }).start(); }(); + log("bar"); +} + +/** This `LogLevel` is unqiue to every thread. + +The thread local `Logger` will use this `LogLevel` to filter log calls +every same way as presented earlier. +*/ +//public LogLevel threadLogLevel = LogLevel.all; +private Logger stdLoggerThreadLogger; +private Logger stdLoggerDefaultThreadLogger; + +/* This method returns the thread local default Logger. +*/ +private @property Logger stdThreadLocalLogImpl() @trusted +{ + import core.lifetime : emplace; + + static align(__traits(classInstanceAlignment, StdForwardLogger)) + void[__traits(classInstanceSize, StdForwardLogger)] buffer; + + if (stdLoggerDefaultThreadLogger is null) + { + stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all); + } + return stdLoggerDefaultThreadLogger; +} + +/** This function returns a thread unique `Logger`, that by default +propagates all data logged to it to the `sharedLog`. + +These properties can be used to set and get this `Logger`. Every +modification to this `Logger` will only be visible in the thread the +modification has been done from. + +This `Logger` is called by the free standing log functions. This allows to +create thread local redirections and still use the free standing log +functions. +*/ +@property Logger stdThreadLocalLog() @safe +{ + // If we have set up our own logger use that + if (auto logger = stdLoggerThreadLogger) + return logger; + else + // Otherwise resort to the default logger + return stdThreadLocalLogImpl; +} + +/// Ditto +@property void stdThreadLocalLog(Logger logger) @safe +{ + stdLoggerThreadLogger = logger; +} + +/// Ditto +@system unittest +{ + import std.logger.filelogger : FileLogger; + import std.file : deleteme, remove; + Logger l = stdThreadLocalLog; + stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log"); + scope(exit) remove(deleteme ~ "-someFile.log"); + + auto tempLog = stdThreadLocalLog; + stdThreadLocalLog = l; + destroy(tempLog); +} + +@safe unittest +{ + LogLevel ll = globalLogLevel; + globalLogLevel = LogLevel.fatal; + assert(globalLogLevel == LogLevel.fatal); + globalLogLevel = ll; +} + +package class TestLogger : Logger +{ + int line = -1; + string file = null; + string func = null; + string prettyFunc = null; + string msg = null; + LogLevel lvl; + + this(const LogLevel lv = LogLevel.all) @safe + { + super(lv); + } + + override protected void writeLogMsg(ref LogEntry payload) @safe + { + this.line = payload.line; + this.file = payload.file; + this.func = payload.funcName; + this.prettyFunc = payload.prettyFuncName; + this.lvl = payload.logLevel; + this.msg = payload.msg; + } +} + +version (StdUnittest) private void testFuncNames(Logger logger) @safe +{ + string s = "I'm here"; + logger.log(s); +} + +@safe unittest +{ + auto tl1 = new TestLogger(); + testFuncNames(tl1); + assert(tl1.func == "std.logger.core.testFuncNames", tl1.func); + assert(tl1.prettyFunc == + "void std.logger.core.testFuncNames(Logger logger) @safe", + tl1.prettyFunc); + assert(tl1.msg == "I'm here", tl1.msg); +} + +@safe unittest +{ + auto tl1 = new TestLogger(LogLevel.all); + tl1.log(); + assert(tl1.line == __LINE__ - 1); + tl1.log(true); + assert(tl1.line == __LINE__ - 1); + tl1.log(false); + assert(tl1.line == __LINE__ - 3); + tl1.log(LogLevel.info); + assert(tl1.line == __LINE__ - 1); + tl1.log(LogLevel.off); + assert(tl1.line == __LINE__ - 3); + tl1.log(LogLevel.info, true); + assert(tl1.line == __LINE__ - 1); + tl1.log(LogLevel.info, false); + assert(tl1.line == __LINE__ - 3); + + auto oldunspecificLogger = sharedLog; + scope(exit) { + sharedLog = atomicLoad(oldunspecificLogger); + } + + () @trusted { + sharedLog = cast(shared) tl1; + }(); + + log(); + assert(tl1.line == __LINE__ - 1); + + log(LogLevel.info); + assert(tl1.line == __LINE__ - 1); + + log(true); + assert(tl1.line == __LINE__ - 1); + + log(LogLevel.warning, true); + assert(tl1.line == __LINE__ - 1); + + trace(); + assert(tl1.line == __LINE__ - 1); +} + +@safe unittest +{ + import std.logger.multilogger : MultiLogger; + + auto tl1 = new TestLogger; + auto tl2 = new TestLogger; + + auto ml = new MultiLogger(); + ml.insertLogger("one", tl1); + ml.insertLogger("two", tl2); + + string msg = "Hello Logger World"; + ml.log(msg); + int lineNumber = __LINE__ - 1; + assert(tl1.msg == msg); + assert(tl1.line == lineNumber); + assert(tl2.msg == msg); + assert(tl2.line == lineNumber); + + ml.removeLogger("one"); + ml.removeLogger("two"); + auto n = ml.removeLogger("one"); + assert(n is null); +} + +@safe unittest +{ + bool errorThrown = false; + auto tl = new TestLogger; + auto dele = delegate() { + errorThrown = true; + }; + tl.fatalHandler = dele; + tl.fatal(); + assert(errorThrown); +} + +@safe unittest +{ + import std.conv : to; + import std.exception : assertThrown, assertNotThrown; + import std.format : format; + + auto l = new TestLogger(LogLevel.all); + string msg = "Hello Logger World"; + l.log(msg); + int lineNumber = __LINE__ - 1; + assert(l.msg == msg); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + l.log(true, msg); + lineNumber = __LINE__ - 1; + assert(l.msg == msg, l.msg); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + l.log(false, msg); + assert(l.msg == msg); + assert(l.line == lineNumber, to!string(l.line)); + assert(l.logLevel == LogLevel.all); + + msg = "%s Another message"; + l.logf(msg, "Yet"); + lineNumber = __LINE__ - 1; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + l.logf(true, msg, "Yet"); + lineNumber = __LINE__ - 1; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + l.logf(false, msg, "Yet"); + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + () @trusted { + assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet")); + } (); + lineNumber = __LINE__ - 2; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + () @trusted { + assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet")); + } (); + lineNumber = __LINE__ - 2; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet")); + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + Logger oldunspecificLogger; + () @trusted { + oldunspecificLogger = cast() sharedLog; + }(); + + assert(oldunspecificLogger.logLevel == LogLevel.info, + to!string(oldunspecificLogger.logLevel)); + + assert(l.logLevel == LogLevel.all); + + () @trusted { + sharedLog = cast(shared) l; + }(); + + assert(globalLogLevel == LogLevel.all, + to!string(globalLogLevel)); + + scope(exit) + { + () @trusted { + sharedLog = atomicLoad(cast(shared) oldunspecificLogger); + }(); + } + + () @trusted { + assert((cast() sharedLog).logLevel == LogLevel.all); + }(); + + assert(stdThreadLocalLog.logLevel == LogLevel.all); + assert(globalLogLevel == LogLevel.all); + + msg = "Another message"; + log(msg); + lineNumber = __LINE__ - 1; + assert(l.logLevel == LogLevel.all); + assert(l.line == lineNumber, to!string(l.line)); + assert(l.msg == msg, l.msg); + + log(true, msg); + lineNumber = __LINE__ - 1; + assert(l.msg == msg); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + log(false, msg); + assert(l.msg == msg); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + msg = "%s Another message"; + logf(msg, "Yet"); + lineNumber = __LINE__ - 1; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + logf(true, msg, "Yet"); + lineNumber = __LINE__ - 1; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + logf(false, msg, "Yet"); + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + msg = "%s Another message"; + () @trusted { + assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet")); + } (); + lineNumber = __LINE__ - 2; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + () @trusted { + assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet")); + } (); + lineNumber = __LINE__ - 2; + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); + + assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet")); + assert(l.msg == msg.format("Yet")); + assert(l.line == lineNumber); + assert(l.logLevel == LogLevel.all); +} + +@system unittest // default logger +{ + import std.file : deleteme, exists, remove; + import std.stdio : File; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; + FileLogger l = new FileLogger(filename); + auto oldunspecificLogger = sharedLog; + + sharedLog = cast(shared) l; + + scope(exit) + { + remove(filename); + assert(!exists(filename)); + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + string notWritten = "this should not be written to file"; + string written = "this should be written to file"; + + globalLogLevel = LogLevel.critical; + assert(globalLogLevel == LogLevel.critical); + + log(LogLevel.warning, notWritten); + log(LogLevel.critical, written); + + l.file.flush(); + l.file.close(); + + auto file = File(filename, "r"); + assert(!file.eof); + + string readLine = file.readln(); + assert(readLine.indexOf(written) != -1, readLine); + assert(readLine.indexOf(notWritten) == -1, readLine); + file.close(); +} + +@system unittest +{ + import std.file : deleteme, remove; + import std.stdio : File; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; + auto oldunspecificLogger = sharedLog; + + scope(exit) + { + remove(filename); + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + string notWritten = "this should not be written to file"; + string written = "this should be written to file"; + + auto l = new FileLogger(filename); + sharedLog = cast(shared) l; + + () @trusted { + (cast() sharedLog).logLevel = LogLevel.critical; + }(); + + log(LogLevel.error, false, notWritten); + log(LogLevel.critical, true, written); + destroy(l); + + auto file = File(filename, "r"); + auto readLine = file.readln(); + assert(!readLine.empty, readLine); + assert(readLine.indexOf(written) != -1); + assert(readLine.indexOf(notWritten) == -1); + file.close(); +} + +@safe unittest +{ + import std.conv : to; + + auto tl = new TestLogger(LogLevel.all); + int l = __LINE__; + tl.info("a"); + assert(tl.line == l+1); + assert(tl.msg == "a"); + assert(tl.logLevel == LogLevel.all); + assert(globalLogLevel == LogLevel.all); + l = __LINE__; + tl.trace("b"); + assert(tl.msg == "b", tl.msg); + assert(tl.line == l+1, to!string(tl.line)); +} + +// testing possible log conditions +@safe unittest +{ + import std.conv : to; + import std.format : format; + import std.string : indexOf; + + auto oldunspecificLogger = sharedLog; + + auto mem = new TestLogger; + mem.fatalHandler = delegate() {}; + + () @trusted { + sharedLog = cast(shared) mem; + }(); + + scope(exit) + { + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + int value = 0; + foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + + globalLogLevel = gll; + + foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + + mem.logLevel = ll; + + foreach (cond; [true, false]) + { + foreach (condValue; [true, false]) + { + foreach (memOrG; [true, false]) + { + foreach (prntf; [true, false]) + { + foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, + LogLevel.error, LogLevel.critical, + LogLevel.fatal, LogLevel.off]) + { + foreach (singleMulti; 0 .. 2) + { + int lineCall; + mem.msg = "-1"; + if (memOrG) + { + if (prntf) + { + if (cond) + { + if (singleMulti == 0) + { + mem.logf(ll2, condValue, "%s", + value); + lineCall = __LINE__; + } + else + { + mem.logf(ll2, condValue, + "%d %d", value, value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + mem.logf(ll2, "%s", value); + lineCall = __LINE__; + } + else + { + mem.logf(ll2, "%d %d", + value, value); + lineCall = __LINE__; + } + } + } + else + { + if (cond) + { + if (singleMulti == 0) + { + mem.log(ll2, condValue, + to!string(value)); + lineCall = __LINE__; + } + else + { + mem.log(ll2, condValue, + to!string(value), value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + mem.log(ll2, to!string(value)); + lineCall = __LINE__; + } + else + { + mem.log(ll2, + to!string(value), + value); + lineCall = __LINE__; + } + } + } + } + else + { + if (prntf) + { + if (cond) + { + if (singleMulti == 0) + { + logf(ll2, condValue, "%s", + value); + lineCall = __LINE__; + } + else + { + logf(ll2, condValue, + "%s %d", value, value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + logf(ll2, "%s", value); + lineCall = __LINE__; + } + else + { + logf(ll2, "%s %s", value, + value); + lineCall = __LINE__; + } + } + } + else + { + if (cond) + { + if (singleMulti == 0) + { + log(ll2, condValue, + to!string(value)); + lineCall = __LINE__; + } + else + { + log(ll2, condValue, value, + to!string(value)); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + log(ll2, to!string(value)); + lineCall = __LINE__; + } + else + { + log(ll2, value, + to!string(value)); + lineCall = __LINE__; + } + } + } + } + + string valueStr = to!string(value); + ++value; + + bool ll2Off = (ll2 != LogLevel.off); + bool gllOff = (gll != LogLevel.off); + bool llOff = (ll != LogLevel.off); + bool condFalse = (cond ? condValue : true); + bool ll2VSgll = (ll2 >= gll); + bool ll2VSll = (ll2 >= ll); + + bool shouldLog = ll2Off && gllOff && llOff + && condFalse && ll2VSgll && ll2VSll; + + /* + writefln( + "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)" + , gll != LogLevel.off, ll2 != LogLevel.off, + cond ? condValue : true, + ll2 >= gll, ll2 >= ll, shouldLog); + */ + + + if (shouldLog) + { + assert(mem.msg.indexOf(valueStr) != -1, + format( + "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ + "cond(%b) condValue(%b)" ~ + " memOrG(%b) shouldLog(%b) %s == %s" ~ + " %b %b %b %b %b", + lineCall, ll2Off, gll, ll, ll2, cond, + condValue, memOrG, shouldLog, mem.msg, + valueStr, gllOff, llOff, condFalse, + ll2VSgll, ll2VSll + )); + } + else + { + assert(mem.msg.indexOf(valueStr), + format( + "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ + "cond(%b) condValue(%b)" ~ + " memOrG(%b) shouldLog(%b) %s == %s" ~ + " %b %b %b %b %b", + lineCall, ll2Off, gll, ll, ll2, cond, + condValue, memOrG, shouldLog, mem.msg, + valueStr, gllOff, llOff, condFalse, + ll2VSgll, ll2VSll + )); + } + } + } + } + } + } + } + } + } +} + +// more testing +@safe unittest +{ + import std.conv : to; + import std.format : format; + import std.string : indexOf; + + auto oldunspecificLogger = sharedLog; + + auto mem = new TestLogger; + mem.fatalHandler = delegate() {}; + + () @trusted { + sharedLog = cast(shared) mem; + }(); + + scope(exit) + { + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + int value = 0; + foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + + globalLogLevel = gll; + + foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + mem.logLevel = ll; + + foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + stdThreadLocalLog.logLevel = tll; + + foreach (cond; [true, false]) + { + foreach (condValue; [true, false]) + { + foreach (memOrG; [true, false]) + { + foreach (prntf; [true, false]) + { + foreach (singleMulti; 0 .. 2) + { + int lineCall; + mem.msg = "-1"; + if (memOrG) + { + if (prntf) + { + if (cond) + { + if (singleMulti == 0) + { + mem.logf(condValue, "%s", + value); + lineCall = __LINE__; + } + else + { + mem.logf(condValue, + "%d %d", value, value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + mem.logf("%s", value); + lineCall = __LINE__; + } + else + { + mem.logf("%d %d", + value, value); + lineCall = __LINE__; + } + } + } + else + { + if (cond) + { + if (singleMulti == 0) + { + mem.log(condValue, + to!string(value)); + lineCall = __LINE__; + } + else + { + mem.log(condValue, + to!string(value), value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + mem.log(to!string(value)); + lineCall = __LINE__; + } + else + { + mem.log(to!string(value), + value); + lineCall = __LINE__; + } + } + } + } + else + { + if (prntf) + { + if (cond) + { + if (singleMulti == 0) + { + logf(condValue, "%s", value); + lineCall = __LINE__; + } + else + { + logf(condValue, "%s %d", value, + value); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + logf("%s", value); + lineCall = __LINE__; + } + else + { + logf("%s %s", value, value); + lineCall = __LINE__; + } + } + } + else + { + if (cond) + { + if (singleMulti == 0) + { + log(condValue, + to!string(value)); + lineCall = __LINE__; + } + else + { + log(condValue, value, + to!string(value)); + lineCall = __LINE__; + } + } + else + { + if (singleMulti == 0) + { + log(to!string(value)); + lineCall = __LINE__; + } + else + { + log(value, to!string(value)); + lineCall = __LINE__; + } + } + } + } + + string valueStr = to!string(value); + ++value; + + bool gllOff = (gll != LogLevel.off); + bool llOff = (ll != LogLevel.off); + bool tllOff = (tll != LogLevel.off); + bool llVSgll = (ll >= gll); + bool tllVSll = + (stdThreadLocalLog.logLevel >= ll); + bool condFalse = (cond ? condValue : true); + + bool shouldLog = gllOff && llOff + && (memOrG ? true : tllOff) + && (memOrG ? + (ll >= gll) : + (tll >= gll && tll >= ll)) + && condFalse; + + if (shouldLog) + { + assert(mem.msg.indexOf(valueStr) != -1, + format("\ngll(%s) ll(%s) tll(%s) " ~ + "cond(%s) condValue(%s) " ~ + "memOrG(%s) prntf(%s) " ~ + "singleMulti(%s)", + gll, ll, tll, cond, condValue, + memOrG, prntf, singleMulti) + ~ format(" gllOff(%s) llOff(%s) " ~ + "llVSgll(%s) tllVSll(%s) " ~ + "tllOff(%s) condFalse(%s) " + ~ "shoudlLog(%s)", + gll != LogLevel.off, + ll != LogLevel.off, llVSgll, + tllVSll, tllOff, condFalse, + shouldLog) + ~ format("msg(%s) line(%s) " ~ + "lineCall(%s) valueStr(%s)", + mem.msg, mem.line, lineCall, + valueStr) + ); + } + else + { + assert(mem.msg.indexOf(valueStr) == -1, + format("\ngll(%s) ll(%s) tll(%s) " ~ + "cond(%s) condValue(%s) " ~ + "memOrG(%s) prntf(%s) " ~ + "singleMulti(%s)", + gll, ll, tll, cond, condValue, + memOrG, prntf, singleMulti) + ~ format(" gllOff(%s) llOff(%s) " ~ + "llVSgll(%s) tllVSll(%s) " ~ + "tllOff(%s) condFalse(%s) " + ~ "shoudlLog(%s)", + gll != LogLevel.off, + ll != LogLevel.off, llVSgll, + tllVSll, tllOff, condFalse, + shouldLog) + ~ format("msg(%s) line(%s) " ~ + "lineCall(%s) valueStr(%s)", + mem.msg, mem.line, lineCall, + valueStr) + ); + } + } + } + } + } + } + } + } + } +} + +// testing more possible log conditions +@safe unittest +{ + bool fatalLog; + auto mem = new TestLogger; + mem.fatalHandler = delegate() { fatalLog = true; }; + auto oldunspecificLogger = sharedLog; + + stdThreadLocalLog.logLevel = LogLevel.all; + + () @trusted { + sharedLog = cast(shared) mem; + }(); + + scope(exit) + { + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + + globalLogLevel = gll; + + foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + mem.logLevel = ll; + + foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, + LogLevel.info, LogLevel.warning, LogLevel.error, + LogLevel.critical, LogLevel.fatal, LogLevel.off]) + { + stdThreadLocalLog.logLevel = tll; + + foreach (cond; [true, false]) + { + assert(globalLogLevel == gll); + assert(mem.logLevel == ll); + + bool gllVSll = LogLevel.trace >= globalLogLevel; + bool llVSgll = ll >= globalLogLevel; + bool lVSll = LogLevel.trace >= ll; + bool gllOff = globalLogLevel != LogLevel.off; + bool llOff = mem.logLevel != LogLevel.off; + bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off; + bool tllVSll = tll >= ll; + bool tllVSgll = tll >= gll; + bool lVSgll = LogLevel.trace >= tll; + + bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; + bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond; + + mem.line = -1; + /* + writefln("gll(%3u) ll(%3u) cond(%b) test(%b)", + gll, ll, cond, test); + writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll, + gllOff, llOff, cond, test2); + */ + + mem.trace(__LINE__); int line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + trace(__LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.trace(cond, __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + trace(cond, __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.tracef("%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + tracef("%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.tracef(cond, "%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + tracef(cond, "%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + llVSgll = ll >= globalLogLevel; + lVSll = LogLevel.info >= ll; + lVSgll = LogLevel.info >= tll; + test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; + testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && + lVSgll && cond; + + mem.info(__LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + info(__LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.info(cond, __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + info(cond, __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.infof("%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + infof("%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.infof(cond, "%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + infof(cond, "%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + llVSgll = ll >= globalLogLevel; + lVSll = LogLevel.warning >= ll; + lVSgll = LogLevel.warning >= tll; + test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; + testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && + lVSgll && cond; + + mem.warning(__LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + warning(__LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.warning(cond, __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + warning(cond, __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.warningf("%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + warningf("%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.warningf(cond, "%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + warningf(cond, "%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + llVSgll = ll >= globalLogLevel; + lVSll = LogLevel.critical >= ll; + lVSgll = LogLevel.critical >= tll; + test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; + testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && + lVSgll && cond; + + mem.critical(__LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + critical(__LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.critical(cond, __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + critical(cond, __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.criticalf("%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + criticalf("%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + mem.criticalf(cond, "%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + + criticalf(cond, "%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + + llVSgll = ll >= globalLogLevel; + lVSll = LogLevel.fatal >= ll; + lVSgll = LogLevel.fatal >= tll; + test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; + testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && + lVSgll && cond; + + mem.fatal(__LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + assert(test ? fatalLog : true); + fatalLog = false; + + fatal(__LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + assert(testG ? fatalLog : true); + fatalLog = false; + + mem.fatal(cond, __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + assert(test ? fatalLog : true); + fatalLog = false; + + fatal(cond, __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + assert(testG ? fatalLog : true); + fatalLog = false; + + mem.fatalf("%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + assert(test ? fatalLog : true); + fatalLog = false; + + fatalf("%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + assert(testG ? fatalLog : true); + fatalLog = false; + + mem.fatalf(cond, "%d", __LINE__); line = __LINE__; + assert(test ? mem.line == line : true); line = -1; + assert(test ? fatalLog : true); + fatalLog = false; + + fatalf(cond, "%d", __LINE__); line = __LINE__; + assert(testG ? mem.line == line : true); line = -1; + assert(testG ? fatalLog : true); + fatalLog = false; + } + } + } + } +} + +// Issue #5 +@safe unittest +{ + import std.string : indexOf; + + auto oldunspecificLogger = sharedLog; + + scope(exit) + { + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + auto tl = new TestLogger(LogLevel.info); + + () @trusted { + sharedLog = cast(shared) tl; + }(); + + trace("trace"); + assert(tl.msg.indexOf("trace") == -1); +} + +// Issue #5 +@safe unittest +{ + import std.logger.multilogger : MultiLogger; + import std.string : indexOf; + + stdThreadLocalLog.logLevel = LogLevel.all; + + auto oldunspecificLogger = sharedLog; + + scope(exit) + { + sharedLog = atomicLoad(oldunspecificLogger); + globalLogLevel = LogLevel.all; + } + + auto logger = new MultiLogger(LogLevel.error); + + auto tl = new TestLogger(LogLevel.info); + logger.insertLogger("required", tl); + + () @trusted { + sharedLog = cast(shared) logger; + }(); + + trace("trace"); + assert(tl.msg.indexOf("trace") == -1); + info("info"); + assert(tl.msg.indexOf("info") == -1); + error("error"); + assert(tl.msg.indexOf("error") == 0); +} + +@system unittest +{ + import std.exception : assertThrown; + auto tl = new TestLogger(); + assertThrown!Throwable(tl.fatal("fatal")); +} + +// log objects with non-safe toString +@system unittest +{ + struct Test + { + string toString() const @system + { + return "test"; + } + } + + auto tl = new TestLogger(); + tl.info(Test.init); + assert(tl.msg == "test"); +} + +// Workaround for atomics not allowed in @safe code +private auto trustedLoad(T)(ref shared T value) @trusted +{ + return atomicLoad!(MemoryOrder.acq)(value); +} + +// ditto +private void trustedStore(T)(ref shared T dst, ref T src) @trusted +{ + atomicStore!(MemoryOrder.rel)(dst, src); +} + +// check that thread-local logging does not propagate +// to shared logger +@system unittest +{ + import core.thread, std.concurrency; + + static shared logged_count = 0; + + class TestLog : Logger + { + Tid tid; + + this() + { + super (LogLevel.trace); + this.tid = thisTid; + } + + override void writeLogMsg(ref LogEntry payload) @trusted + { + assert(thisTid == this.tid); + atomicOp!"+="(logged_count, 1); + } + } + + class IgnoredLog : Logger + { + this() + { + super (LogLevel.trace); + } + + override void writeLogMsg(ref LogEntry payload) @trusted + { + assert(false); + } + } + + auto oldSharedLog = sharedLog; + scope(exit) + { + sharedLog = atomicLoad(oldSharedLog); + } + + () @trusted { + sharedLog = cast(shared) new IgnoredLog; + }(); + + Thread[] spawned; + + foreach (i; 0 .. 4) + { + spawned ~= new Thread({ + stdThreadLocalLog = new TestLog; + trace("zzzzzzzzzz"); + }); + spawned[$-1].start(); + } + + foreach (t; spawned) + t.join(); + + assert(atomicOp!"=="(logged_count, 4)); +} + +@safe unittest +{ + auto dl = () @trusted { + return cast(FileLogger) cast() sharedLog; + }(); + assert(dl !is null); + assert(dl.logLevel == LogLevel.info); + assert(globalLogLevel == LogLevel.all); + + auto tl = cast(StdForwardLogger) stdThreadLocalLog; + assert(tl !is null); + stdThreadLocalLog.logLevel = LogLevel.all; +} + +// https://issues.dlang.org/show_bug.cgi?id=14940 +@safe unittest +{ + import std.typecons : Nullable; + + Nullable!int a = 1; + auto l = new TestLogger(); + l.infof("log: %s", a); + assert(l.msg == "log: 1"); +} + +// Ensure @system toString methods work +@system unittest +{ + enum SystemToStringMsg = "SystemToString"; + static struct SystemToString + { + string toString() @system + { + return SystemToStringMsg; + } + } + + auto tl = new TestLogger(); + + SystemToString sts; + tl.logf("%s", sts); + assert(tl.msg == SystemToStringMsg); +} + +// https://issues.dlang.org/show_bug.cgi?id=17328 +@safe unittest +{ + import std.format : format; + + ubyte[] data = [0]; + string s = format("%(%02x%)", data); // format 00 + assert(s == "00"); + + auto tl = new TestLogger(); + + tl.infof("%(%02x%)", data); // infof 000 + + size_t i; + string fs = tl.msg; + for (; i < s.length; ++i) + { + assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs); + } + assert(fs.length == 2); +} + +// https://issues.dlang.org/show_bug.cgi?id=15954 +@safe unittest +{ + import std.conv : to; + auto tl = new TestLogger(); + tl.log("123456789".to!wstring); + assert(tl.msg == "123456789"); +} + +// https://issues.dlang.org/show_bug.cgi?id=16256 +@safe unittest +{ + import std.conv : to; + auto tl = new TestLogger(); + tl.log("123456789"d); + assert(tl.msg == "123456789"); +} + +// https://issues.dlang.org/show_bug.cgi?id=15517 +@system unittest +{ + import std.file : exists, remove, tempDir; + import std.path : buildPath; + import std.stdio : File; + import std.string : indexOf; + + string fn = tempDir.buildPath("logfile.log"); + if (exists(fn)) + { + remove(fn); + } + + auto oldShared = sharedLog; + scope(exit) + { + sharedLog = atomicLoad(oldShared); + if (exists(fn)) + { + remove(fn); + } + } + + auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; + + auto fl = new FileLogger(fn); + + () @trusted { + sharedLog = cast(shared) fl; + }(); + + assert(exists(fn)); + + foreach (t; ts) + { + log(t); + } + + auto f = File(fn); + auto l = f.byLine(); + assert(!l.empty); + size_t idx; + foreach (it; l) + { + assert(it.indexOf(ts[idx]) != -1, it); + ++idx; + } + + assert(exists(fn)); + fl.file.close(); +} diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d new file mode 100644 index 0000000..c662ca7 --- /dev/null +++ b/libphobos/src/std/logger/filelogger.d @@ -0,0 +1,272 @@ +// Written in the D programming language. +/** +Source: $(PHOBOSSRC std/logger/filelogger.d) +*/ +module std.logger.filelogger; + +import std.logger.core; +import std.stdio; + +import std.typecons : Flag; + +/** An option to create $(LREF FileLogger) directory if it is non-existent. +*/ +alias CreateFolder = Flag!"CreateFolder"; + +/** This `Logger` implementation writes log messages to the associated +file. The name of the file has to be passed on construction time. If the file +is already present new log messages will be append at its end. +*/ +class FileLogger : Logger +{ + import std.concurrency : Tid; + import std.datetime.systime : SysTime; + import std.format.write : formattedWrite; + + /** A constructor for the `FileLogger` Logger. + + Params: + fn = The filename of the output file of the `FileLogger`. If that + file can not be opened for writting an exception will be thrown. + lv = The `LogLevel` for the `FileLogger`. By default the + + Example: + ------------- + auto l1 = new FileLogger("logFile"); + auto l2 = new FileLogger("logFile", LogLevel.fatal); + auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes); + ------------- + */ + this(const string fn, const LogLevel lv = LogLevel.all) @safe + { + this(fn, lv, CreateFolder.yes); + } + + /** A constructor for the `FileLogger` Logger that takes a reference to + a `File`. + + The `File` passed must be open for all the log call to the + `FileLogger`. If the `File` gets closed, using the `FileLogger` + for logging will result in undefined behaviour. + + Params: + fn = The file used for logging. + lv = The `LogLevel` for the `FileLogger`. By default the + `LogLevel` for `FileLogger` is `LogLevel.all`. + createFileNameFolder = if yes and fn contains a folder name, this + folder will be created. + + Example: + ------------- + auto file = File("logFile.log", "w"); + auto l1 = new FileLogger(file); + auto l2 = new FileLogger(file, LogLevel.fatal); + ------------- + */ + this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe + { + import std.file : exists, mkdirRecurse; + import std.path : dirName; + import std.conv : text; + + super(lv); + this.filename = fn; + + if (createFileNameFolder) + { + auto d = dirName(this.filename); + mkdirRecurse(d); + assert(exists(d), text("The folder the FileLogger should have", + " created in '", d,"' could not be created.")); + } + + this.file_.open(this.filename, "a"); + } + + /** A constructor for the `FileLogger` Logger that takes a reference to + a `File`. + + The `File` passed must be open for all the log call to the + `FileLogger`. If the `File` gets closed, using the `FileLogger` + for logging will result in undefined behaviour. + + Params: + file = The file used for logging. + lv = The `LogLevel` for the `FileLogger`. By default the + `LogLevel` for `FileLogger` is `LogLevel.all`. + + Example: + ------------- + auto file = File("logFile.log", "w"); + auto l1 = new FileLogger(file); + auto l2 = new FileLogger(file, LogLevel.fatal); + ------------- + */ + this(File file, const LogLevel lv = LogLevel.all) @safe + { + super(lv); + this.file_ = file; + } + + /** If the `FileLogger` is managing the `File` it logs to, this + method will return a reference to this File. + */ + @property File file() @safe + { + return this.file_; + } + + /* This method overrides the base class method in order to log to a file + without requiring heap allocated memory. Additionally, the `FileLogger` + local mutex is logged to serialize the log calls. + */ + override protected void beginLogMsg(string file, int line, string funcName, + string prettyFuncName, string moduleName, LogLevel logLevel, + Tid threadId, SysTime timestamp, Logger logger) + @safe + { + import std.string : lastIndexOf; + ptrdiff_t fnIdx = file.lastIndexOf('/') + 1; + ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1; + + auto lt = this.file_.lockingTextWriter(); + systimeToISOString(lt, timestamp); + import std.conv : to; + formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string, + file[fnIdx .. $], line, funcName[funIdx .. $]); + } + + /* This methods overrides the base class method and writes the parts of + the log call directly to the file. + */ + override protected void logMsgPart(scope const(char)[] msg) + { + formattedWrite(this.file_.lockingTextWriter(), "%s", msg); + } + + /* This methods overrides the base class method and finalizes the active + log call. This requires flushing the `File` and releasing the + `FileLogger` local mutex. + */ + override protected void finishLogMsg() + { + this.file_.lockingTextWriter().put("\n"); + this.file_.flush(); + } + + /* This methods overrides the base class method and delegates the + `LogEntry` data to the actual implementation. + */ + override protected void writeLogMsg(ref LogEntry payload) + { + this.beginLogMsg(payload.file, payload.line, payload.funcName, + payload.prettyFuncName, payload.moduleName, payload.logLevel, + payload.threadId, payload.timestamp, payload.logger); + this.logMsgPart(payload.msg); + this.finishLogMsg(); + } + + /** If the `FileLogger` was constructed with a filename, this method + returns this filename. Otherwise an empty `string` is returned. + */ + string getFilename() + { + return this.filename; + } + + /** The `File` log messages are written to. */ + protected File file_; + + /** The filename of the `File` log messages are written to. */ + protected string filename; +} + +@system unittest +{ + import std.array : empty; + import std.file : deleteme, remove; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; + auto l = new FileLogger(filename); + + scope(exit) + { + remove(filename); + } + + string notWritten = "this should not be written to file"; + string written = "this should be written to file"; + + l.logLevel = LogLevel.critical; + l.log(LogLevel.warning, notWritten); + l.log(LogLevel.critical, written); + destroy(l); + + auto file = File(filename, "r"); + string readLine = file.readln(); + assert(readLine.indexOf(written) != -1, readLine); + readLine = file.readln(); + assert(readLine.indexOf(notWritten) == -1, readLine); +} + +@safe unittest +{ + import std.file : rmdirRecurse, exists, deleteme; + import std.path : dirName; + + const string tmpFolder = dirName(deleteme); + const string filepath = tmpFolder ~ "/bug15771/minas/oops/"; + const string filename = filepath ~ "output.txt"; + assert(!exists(filepath)); + + auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes); + scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }(); + + f.log("Hello World!"); + assert(exists(filepath)); + f.file.close(); +} + +@system unittest +{ + import std.array : empty; + import std.file : deleteme, remove; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; + auto file = File(filename, "w"); + auto l = new FileLogger(file); + + scope(exit) + { + remove(filename); + } + + string notWritten = "this should not be written to file"; + string written = "this should be written to file"; + + l.logLevel = LogLevel.critical; + l.log(LogLevel.warning, notWritten); + l.log(LogLevel.critical, written); + file.close(); + + file = File(filename, "r"); + string readLine = file.readln(); + assert(readLine.indexOf(written) != -1, readLine); + readLine = file.readln(); + assert(readLine.indexOf(notWritten) == -1, readLine); + file.close(); +} + +@system unittest +{ + auto dl = cast(FileLogger) sharedLog; + assert(dl !is null); + assert(dl.logLevel == LogLevel.info); + assert(globalLogLevel == LogLevel.all); + + auto tl = cast(StdForwardLogger) stdThreadLocalLog; + assert(tl !is null); + stdThreadLocalLog.logLevel = LogLevel.all; +} diff --git a/libphobos/src/std/logger/multilogger.d b/libphobos/src/std/logger/multilogger.d new file mode 100644 index 0000000..eda922d --- /dev/null +++ b/libphobos/src/std/logger/multilogger.d @@ -0,0 +1,200 @@ +// Written in the D programming language. +/** +Source: $(PHOBOSSRC std/logger/multilogger.d) +*/ +module std.logger.multilogger; + +import std.logger.core; +import std.logger.filelogger; + +/** This Element is stored inside the `MultiLogger` and associates a +`Logger` to a `string`. +*/ +struct MultiLoggerEntry +{ + string name; /// The name if the `Logger` + Logger logger; /// The stored `Logger` +} + +/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an +`Logger[]` in their order of insertion. + +Every data logged to this `MultiLogger` will be distributed to all the $(D +Logger)s inserted into it. This `MultiLogger` implementation can +hold multiple `Logger`s with the same name. If the method `removeLogger` +is used to remove a `Logger` only the first occurrence with that name will +be removed. +*/ +class MultiLogger : Logger +{ + /** A constructor for the `MultiLogger` Logger. + + Params: + lv = The `LogLevel` for the `MultiLogger`. By default the + `LogLevel` for `MultiLogger` is `LogLevel.all`. + + Example: + ------------- + auto l1 = new MultiLogger(LogLevel.trace); + ------------- + */ + this(const LogLevel lv = LogLevel.all) @safe + { + super(lv); + } + + /** This member holds all `Logger`s stored in the `MultiLogger`. + + When inheriting from `MultiLogger` this member can be used to gain + access to the stored `Logger`. + */ + protected MultiLoggerEntry[] logger; + + /** This method inserts a new Logger into the `MultiLogger`. + + Params: + name = The name of the `Logger` to insert. + newLogger = The `Logger` to insert. + */ + void insertLogger(string name, Logger newLogger) @safe + { + this.logger ~= MultiLoggerEntry(name, newLogger); + } + + /** This method removes a Logger from the `MultiLogger`. + + Params: + toRemove = The name of the `Logger` to remove. If the `Logger` + is not found `null` will be returned. Only the first occurrence of + a `Logger` with the given name will be removed. + + Returns: The removed `Logger`. + */ + Logger removeLogger(in char[] toRemove) @safe + { + import std.algorithm.mutation : copy; + import std.range.primitives : back, popBack; + for (size_t i = 0; i < this.logger.length; ++i) + { + if (this.logger[i].name == toRemove) + { + Logger ret = this.logger[i].logger; + this.logger[i] = this.logger.back; + this.logger.popBack(); + + return ret; + } + } + + return null; + } + + /* The override to pass the payload to all children of the + `MultiLoggerBase`. + */ + override protected void writeLogMsg(ref LogEntry payload) @safe + { + foreach (it; this.logger) + { + /* We don't perform any checks here to avoid race conditions. + Instead the child will check on its own if its log level matches + and assume LogLevel.all for the globalLogLevel (since we already + know the message passes this test). + */ + it.logger.forwardMsg(payload); + } + } +} + +@safe unittest +{ + import std.exception : assertThrown; + import std.logger.nulllogger; + auto a = new MultiLogger; + auto n0 = new NullLogger(); + auto n1 = new NullLogger(); + a.insertLogger("zero", n0); + a.insertLogger("one", n1); + + auto n0_1 = a.removeLogger("zero"); + assert(n0_1 is n0); + auto n = a.removeLogger("zero"); + assert(n is null); + + auto n1_1 = a.removeLogger("one"); + assert(n1_1 is n1); + n = a.removeLogger("one"); + assert(n is null); +} + +@safe unittest +{ + auto a = new MultiLogger; + auto n0 = new TestLogger; + auto n1 = new TestLogger; + a.insertLogger("zero", n0); + a.insertLogger("one", n1); + + a.log("Hello TestLogger"); int line = __LINE__; + assert(n0.msg == "Hello TestLogger"); + assert(n0.line == line); + assert(n1.msg == "Hello TestLogger"); + assert(n1.line == line); +} + +// Issue #16 +@system unittest +{ + import std.file : deleteme; + import std.stdio : File; + import std.string : indexOf; + string logName = deleteme ~ __FUNCTION__ ~ ".log"; + auto logFileOutput = File(logName, "w"); + scope(exit) + { + import std.file : remove; + logFileOutput.close(); + remove(logName); + } + auto traceLog = new FileLogger(logFileOutput, LogLevel.all); + auto infoLog = new TestLogger(LogLevel.info); + + auto root = new MultiLogger(LogLevel.all); + root.insertLogger("fileLogger", traceLog); + root.insertLogger("stdoutLogger", infoLog); + + string tMsg = "A trace message"; + root.trace(tMsg); int line1 = __LINE__; + + assert(infoLog.line != line1); + assert(infoLog.msg != tMsg); + + string iMsg = "A info message"; + root.info(iMsg); int line2 = __LINE__; + + assert(infoLog.line == line2); + assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg); + + logFileOutput.close(); + logFileOutput = File(logName, "r"); + assert(logFileOutput.isOpen); + assert(!logFileOutput.eof); + + auto line = logFileOutput.readln(); + assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg); + assert(!logFileOutput.eof); + line = logFileOutput.readln(); + assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg); +} + +@system unittest +{ + auto dl = cast(FileLogger) sharedLog; + assert(dl !is null); + assert(dl.logLevel == LogLevel.info); + assert(globalLogLevel == LogLevel.all); + + auto tl = cast(StdForwardLogger) stdThreadLocalLog; + assert(tl !is null); + stdThreadLocalLog.logLevel = LogLevel.all; +} diff --git a/libphobos/src/std/logger/nulllogger.d b/libphobos/src/std/logger/nulllogger.d new file mode 100644 index 0000000..fd48b85 --- /dev/null +++ b/libphobos/src/std/logger/nulllogger.d @@ -0,0 +1,41 @@ +// Written in the D programming language. +/** +Source: $(PHOBOSSRC std/logger/nulllogger.d) +*/ +module std.logger.nulllogger; + +import std.logger.core; + +/** The `NullLogger` will not process any log messages. + +In case of a log message with `LogLevel.fatal` nothing will happen. +*/ +class NullLogger : Logger +{ + /** The default constructor for the `NullLogger`. + + Independent of the parameter this Logger will never log a message. + + Params: + lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel` + for `NullLogger` is `LogLevel.all`. + */ + this(const LogLevel lv = LogLevel.all) @safe + { + super(lv); + this.fatalHandler = delegate() {}; + } + + override protected void writeLogMsg(ref LogEntry payload) @safe @nogc + { + } +} + +/// +@safe unittest +{ + import std.logger.core : LogLevel; + auto nl1 = new NullLogger(LogLevel.all); + nl1.info("You will never read this."); + nl1.fatal("You will never read this, either and it will not throw"); +} diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d new file mode 100644 index 0000000..4f4183c --- /dev/null +++ b/libphobos/src/std/logger/package.d @@ -0,0 +1,168 @@ +// Written in the D programming language. +/** +Implements logging facilities. + +Copyright: Copyright Robert "burner" Schadek 2013 -- +License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. +Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek) + +$(H3 Basic Logging) + +Message logging is a common approach to expose runtime information of a +program. Logging should be easy, but also flexible and powerful, therefore +`D` provides a standard interface for logging. + +The easiest way to create a log message is to write: +------------- +import std.logger; + +void main() { + log("Hello World"); +} +------------- +This will print a message to the `stderr` device. The message will contain +the filename, the line number, the name of the surrounding function, the time +and the message. + +More complex log call can go along the lines like: +------------- +log("Logging to the sharedLog with its default LogLevel"); +logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging"); +info("Logging to the sharedLog with its info LogLevel"); +warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6"); +error("Logging to the sharedLog with its error LogLevel"); +errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with"); +critical("Logging to the"," sharedLog with its error LogLevel"); +fatal("Logging to the sharedLog with its fatal LogLevel"); + +auto fLogger = new FileLogger("NameOfTheLogFile"); +fLogger.log("Logging to the fileLogger with its default LogLevel"); +fLogger.info("Logging to the fileLogger with its default LogLevel"); +fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6"); +fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less"); +fLogger.critical("Logging to the fileLogger with its info LogLevel"); +fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6"); +fLogger.fatal("Logging to the fileLogger with its warning LogLevel"); +------------- +Additionally, this example shows how a new `FileLogger` is created. +Individual `Logger` and the global log functions share commonly named +functions to log data. + +The names of the functions are as follows: +$(UL + $(LI `log`) + $(LI `trace`) + $(LI `info`) + $(LI `warning`) + $(LI `critical`) + $(LI `fatal`) +) +The default `Logger` will by default log to `stderr` and has a default +`LogLevel` of `LogLevel.all`. The default Logger can be accessed by +using the property called `sharedLog`. This property is a reference to the +current default `Logger`. This reference can be used to assign a new +default `Logger`. +------------- +sharedLog = new FileLogger("New_Default_Log_File.log"); +------------- + +Additional `Logger` can be created by creating a new instance of the +required `Logger`. + +$(H3 Logging Fundamentals) +$(H4 LogLevel) +The `LogLevel` of a log call can be defined in two ways. The first is by +calling `log` and passing the `LogLevel` explicitly as the first argument. +The second way of setting the `LogLevel` of a +log call, is by calling either `trace`, `info`, `warning`, +`critical`, or `fatal`. The log call will then have the respective +`LogLevel`. If no `LogLevel` is defined the log call will use the +current `LogLevel` of the used `Logger`. If data is logged with +`LogLevel` `fatal` by default an `Error` will be thrown. +This behaviour can be modified by using the member `fatalHandler` to +assign a custom delegate to handle log call with `LogLevel` `fatal`. + +$(H4 Conditional Logging) +Conditional logging can be achieved be passing a `bool` as first +argument to a log function. If conditional logging is used the condition must +be `true` in order to have the log message logged. + +In order to combine an explicit `LogLevel` passing with conditional +logging, the `LogLevel` has to be passed as first argument followed by the +`bool`. + +$(H4 Filtering Log Messages) +Messages are logged if the `LogLevel` of the log message is greater than or +equal to the `LogLevel` of the used `Logger` and additionally if the +`LogLevel` of the log message is greater than or equal to the global `LogLevel`. +If a condition is passed into the log call, this condition must be true. + +The global `LogLevel` is accessible by using `globalLogLevel`. +To assign a `LogLevel` of a `Logger` use the `logLevel` property of +the logger. + +$(H4 Printf Style Logging) +If `printf`-style logging is needed add a $(B f) to the logging call, such as +$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)). +The additional $(B f) appended to the function name enables `printf`-style +logging for all combinations of explicit `LogLevel` and conditional +logging functions and methods. + +$(H4 Thread Local Redirection) +Calls to the free standing log functions are not directly forwarded to the +global `Logger` `sharedLog`. Actually, a thread local `Logger` of +type `StdForwardLogger` processes the log call and then, by default, forwards +the created `Logger.LogEntry` to the `sharedLog` `Logger`. +The thread local `Logger` is accessible by the `stdThreadLocalLog` +property. This property allows to assign user defined `Logger`. The default +`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all` +and it will therefore forward all messages to the `sharedLog` `Logger`. +The `LogLevel` of the `stdThreadLocalLog` can be used to filter log +calls before they reach the `sharedLog` `Logger`. + +$(H3 User Defined Logger) +To customize the `Logger` behavior, create a new `class` that inherits from +the abstract `Logger` `class`, and implements the `writeLogMsg` +method. +------------- +class MyCustomLogger : Logger +{ + this(LogLevel lv) @safe + { + super(lv); + } + + override void writeLogMsg(ref LogEntry payload) + { + // log message in my custom way + } +} + +auto logger = new MyCustomLogger(LogLevel.info); +logger.log("Awesome log message with LogLevel.info"); +------------- + +To gain more precise control over the logging process, additionally to +overriding the `writeLogMsg` method the methods `beginLogMsg`, +`logMsgPart` and `finishLogMsg` can be overridden. + +$(H3 Provided Logger) +By default four `Logger` implementations are given. The `FileLogger` +logs data to files. It can also be used to log to `stdout` and `stderr` +as these devices are files as well. A `Logger` that logs to `stdout` can +therefore be created by $(D new FileLogger(stdout)). +The `MultiLogger` is basically an associative array of `string`s to +`Logger`. It propagates log calls to its stored `Logger`. The +`ArrayLogger` contains an array of `Logger` and also propagates log +calls to its stored `Logger`. The `NullLogger` does not do anything. It +will never log a message and will never throw on a log call with `LogLevel` +`error`. + +Source: $(PHOBOSSRC std/logger/package.d) +*/ +module std.logger; + +public import std.logger.core; +public import std.logger.filelogger; +public import std.logger.multilogger; +public import std.logger.nulllogger; diff --git a/libphobos/src/std/meta.d b/libphobos/src/std/meta.d index 0075ed7..2db341d 100644 --- a/libphobos/src/std/meta.d +++ b/libphobos/src/std/meta.d @@ -627,35 +627,9 @@ Evaluates to `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`. */ template staticMap(alias fun, args...) { - version (__staticMap_simplest_but_buggy) - { - // @@@ BUG @@@ - // The following straightforward implementation exposes a bug. - // See issue https://issues.dlang.org/show_bug.cgi?id=22421 and unittest below. - alias staticMap = AliasSeq!(); - static foreach (arg; args) - staticMap = AliasSeq!(staticMap, fun!arg); - } - else version (__staticMap_simple_but_slow) - { - // This has a performance bug. Appending to the staticMap seems to be quadratic. - alias staticMap = AliasSeq!(); - static foreach (i; 0 .. args.length) - staticMap = AliasSeq!(staticMap, fun!(args[i])); - } - else // Current best-of-breed implementation imitates quicksort - { - static if (args.length <= 8) - { - alias staticMap = AliasSeq!(); - static foreach (i; 0 .. args.length) - staticMap = AliasSeq!(staticMap, fun!(args[i])); - } - else - { - alias staticMap = AliasSeq!(staticMap!(fun, args[0 .. $ / 2]), staticMap!(fun, args[$ / 2 .. $])); - } - } + alias staticMap = AliasSeq!(); + static foreach (arg; args) + staticMap = AliasSeq!(staticMap, fun!arg); } /// diff --git a/libphobos/src/std/package.d b/libphobos/src/std/package.d index bfb135b..e2eca7b 100644 --- a/libphobos/src/std/package.d +++ b/libphobos/src/std/package.d @@ -53,6 +53,7 @@ public import std.getopt, std.int128, std.json, + std.logger, std.math, std.mathspecial, std.meta, diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index b2206ce..70b87dd 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -2806,7 +2806,7 @@ auto ref choice(Range)(ref Range range) { import std.algorithm.searching : canFind; - class MyTestClass + static class MyTestClass { int x; diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d index e24abc5..1562d79 100644 --- a/libphobos/src/std/regex/package.d +++ b/libphobos/src/std/regex/package.d @@ -43,11 +43,16 @@ $(TR $(TD Objects) $(TD )) $(SECTION Synopsis) - --- - import std.regex; - import std.stdio; - void main() - { + + Create a regex at runtime: + $(RUNNABLE_EXAMPLE + $(RUNNABLE_EXAMPLE_STDIN +They met on 24/01/1970. +7/8/99 wasn't as hot as 7/8/2022. +) + --- + import std.regex; + import std.stdio; // Print out all possible dd/mm/yy(yy) dates found in user input. auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b"); foreach (line; stdin.byLine) @@ -57,19 +62,24 @@ $(TR $(TD Objects) $(TD foreach (c; matchAll(line, r)) writeln(c.hit); } - } - ... - - // Create a static regex at compile-time, which contains fast native code. + --- + ) + Create a static regex at compile-time, which contains fast native code: + $(RUNNABLE_EXAMPLE + --- + import std.regex; auto ctr = ctRegex!(`^.*/([^/]+)/?$`); // It works just like a normal regex: auto c2 = matchFirst("foo/bar", ctr); // First match found here, if any assert(!c2.empty); // Be sure to check if there is a match before examining contents! assert(c2[1] == "bar"); // Captures is a range of submatches: 0 = full match. - - ... - // multi-pattern regex + --- + ) + Multi-pattern regex: + $(RUNNABLE_EXAMPLE + --- + import std.regex; auto multi = regex([`\d+,\d+`, `([a-z]+):(\d+)`]); auto m = "abc:43 12,34".matchAll(multi); assert(m.front.whichPattern == 2); @@ -77,21 +87,27 @@ $(TR $(TD Objects) $(TD assert(m.front[2] == "43"); m.popFront(); assert(m.front.whichPattern == 1); - assert(m.front[1] == "12"); - ... - + assert(m.front[0] == "12,34"); + --- + ) + $(LREF Captures) and `opCast!bool`: + $(RUNNABLE_EXAMPLE + --- + import std.regex; // The result of `matchAll/matchFirst` is directly testable with `if/assert/while`, // e.g. test if a string consists of letters only: assert(matchFirst("LettersOnly", `^\p{L}+$`)); - // And we can take advantage of the ability to define a variable in the $(LINK2 https://dlang.org/spec/statement.html#IfCondition `IfCondition`): - if (const auto captures = matchFirst("At l34st one digit, but maybe more...", `((\d)(\d*))`)) + // And we can take advantage of the ability to define a variable in the IfCondition: + if (const captures = matchFirst("At l34st one digit, but maybe more...", `((\d)(\d*))`)) { assert(captures[2] == "3"); assert(captures[3] == "4"); assert(captures[1] == "34"); } --- + ) + See_Also: $(LINK2 https://dlang.org/spec/statement.html#IfCondition, `IfCondition`). $(SECTION Syntax and general information) The general usage guideline is to keep regex complexity on the side of simplicity, @@ -470,7 +486,7 @@ private struct CTRegexWrapper(Char) alias getRe this; } -template ctRegexImpl(alias pattern, string flags=[]) +template ctRegexImpl(alias pattern, string flags="") { import std.regex.internal.backtracking, std.regex.internal.parser; static immutable r = cast(immutable) regex(pattern, flags); @@ -518,7 +534,7 @@ template ctRegexImpl(alias pattern, string flags=[]) pattern = Regular expression flags = The _attributes (g, i, m, s and x accepted) +/ -public enum ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).wrapper; +public enum ctRegex(alias pattern, string flags="") = ctRegexImpl!(pattern, flags).wrapper; enum isRegexFor(RegEx, R) = is(immutable RegEx == immutable Regex!(BasicElementOf!R)) || is(RegEx : const(Regex!(BasicElementOf!R))) diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index 5c9a2c9..7ed24f7 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -6884,7 +6884,7 @@ if (isSomeString!S) if (inword) { - if (col + 1 + (s.length - wordstart) >= columns) + if (col + 1 + (s.length - wordstart) > columns) { result ~= '\n'; result ~= indent; @@ -6929,6 +6929,13 @@ if (isSomeString!S) }); } +@safe pure unittest // https://issues.dlang.org/show_bug.cgi?id=23298 +{ + assert("1 2 3 4 5 6 7 8 9".wrap(17) == "1 2 3 4 5 6 7 8 9\n"); + assert("1 2 3 4 5 6 7 8 9 ".wrap(17) == "1 2 3 4 5 6 7 8 9\n"); + assert("1 2 3 4 5 6 7 8 99".wrap(17) == "1 2 3 4 5 6 7 8\n99\n"); +} + /****************************************** * Removes one level of indentation from a multi-line string. * diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index 4ecfb10..c7cdc24 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -1146,7 +1146,7 @@ if (distinctFieldNames!(Specs)) } /// - static if (Specs.length == 0) @safe unittest + static if (Specs.length == 0) @system unittest { //replace names by their position @@ -1166,7 +1166,7 @@ if (distinctFieldNames!(Specs)) assert(t2Named.c == 3); } - static if (Specs.length == 0) @safe unittest + static if (Specs.length == 0) @system unittest { //check that empty translations work fine enum string[string] a0 = null; |