d: Merge upstream dmd 817610b16d, phobos b578dfad9

D front-end changes:

    - Import latest bug fixes to mainline.

Phobos changes:

    - Import latest bug fixes to mainline.
    - std.logger module has been moved out of experimental.
    - Removed std.experimental.typecons module.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 817610b16d.
	* d-ctfloat.cc (CTFloat::parse): Update for new front-end interface.
	* d-lang.cc (d_parse_file): Likewise.
	* expr.cc (ExprVisitor::visit (AssignExp *)): Remove handling of array
	assignments to non-trivial static and dynamic arrays.
	* runtime.def (ARRAYASSIGN): Remove.
	(ARRAYASSIGN_L): Remove.
	(ARRAYASSIGN_R): Remove.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 817610b16d.
	* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add
	core/internal/array/arrayassign.d.
	* libdruntime/Makefile.in: Regenerate.
	* src/MERGE: Merge upstream phobos b578dfad9.
	* src/Makefile.am (PHOBOS_DSOURCES): Remove
	std/experimental/typecons.d. Add std/logger package.
	* src/Makefile.in: Regenerate.
This commit is contained in:
Iain Buclaw 2022-08-25 19:04:50 +02:00
parent cace77f4fb
commit b7a586beae
101 changed files with 5810 additions and 5672 deletions

View file

@ -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;
}

View file

@ -1191,7 +1191,6 @@ d_parse_file (void)
}
/* Do deferred semantic analysis. */
Module::dprogress = 1;
Module::runDeferredSemantic ();
if (Module::deferred.length)

View file

@ -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.

View file

@ -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 |

View file

@ -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`

View file

@ -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;

View file

@ -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 |

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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_)

View file

@ -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)

View file

@ -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.

View file

@ -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()

View file

@ -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++;

View file

@ -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;
@ -6812,6 +6825,135 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
ds.semanticRun = PASS.semanticdone;
}
/***************************************
* 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`.
*

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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))

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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" },

View file

@ -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;
}

View file

@ -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':

View file

@ -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();

View file

@ -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())
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())
{
if (tn.isMutable() && mayHavePointers(tn))
return stc; // escape through pointers
}
else if (t.ty == Tarray || t.ty == Tpointer)
{
Type tn = t.nextOf().toBasetype();
if (!(tn.isMutable() && tn.hasPointers()))
continue;
}
return stc;
}
}

View file

@ -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

View file

@ -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 |

View file

@ -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]);
}
/**

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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");

View file

@ -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. */

View file

@ -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)

View file

@ -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!();

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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!();

View file

@ -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;
}

View file

@ -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
---
*/

View file

@ -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
---
*/

View file

@ -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

View file

@ -0,0 +1,3 @@
module imports.import15525;
template Tuple{ static if }

View file

@ -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;
}

View file

@ -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
---
*/

View file

@ -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++;
}
}

View file

@ -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;
}

View file

@ -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`
---
*/

View file

@ -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. */
}

View file

@ -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`
---
*/

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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

View file

@ -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.

View file

@ -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 \

View file

@ -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: \

View file

@ -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];

View file

@ -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);

View file

@ -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);
}

View file

@ -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)

View file

@ -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

View file

@ -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);
// create
aa.update("key",
() => 1,
(int) {} // not executed
);
assert(aa["key"] == 1);
aa.update("k2", {
return 0; // create
}, (ref int v) {
v = -1; // update (won't be executed)
});
assert(aa["k2"] == 0);
// 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;

View file

@ -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.

View file

@ -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.

View file

@ -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 \

View file

@ -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)

View file

@ -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.
)
)
)
)

View file

@ -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; }

View file

@ -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"`

View file

@ -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

View file

@ -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();

View file

@ -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);
}}
}

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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;

View file

@ -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);
}
///

View file

@ -53,6 +53,7 @@ public import
std.getopt,
std.int128,
std.json,
std.logger,
std.math,
std.mathspecial,
std.meta,

View file

@ -2806,7 +2806,7 @@ auto ref choice(Range)(ref Range range)
{
import std.algorithm.searching : canFind;
class MyTestClass
static class MyTestClass
{
int x;

View file

@ -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)))

View file

@ -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.
*

Some files were not shown because too many files have changed in this diff Show more