d/func.o \
d/funcsem.o \
d/globals.o \
- d/gluelayer.o \
d/hdrgen.o \
- d/iasm.o \
- d/iasmgcc.o \
+ d/iasm-package.o \
+ d/iasm-gcc.o \
d/id.o \
d/identifier.o \
d/impcnvtab.o \
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
+d/iasm-%.o: d/dmd/iasm/%.d
+ $(DCOMPILE) $(D_INCLUDES) $<
+ $(DPOSTCOMPILE)
+
d/mangle-%.o: d/dmd/mangle/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
break;
}
-
- /* The offset can only be determined at run-time, do dynamic cast. */
- libcall_fn libcall = cdfrom->isInterfaceDeclaration ()
- ? LIBCALL_INTERFACE_CAST : LIBCALL_DYNAMIC_CAST;
-
- return build_libcall (libcall, totype, 2, exp,
- build_address (get_classinfo_decl (cdto)));
}
/* else default conversion. */
break;
global.params.useDeprecated = DIAGNOSTICinform;
global.params.useWarnings = DIAGNOSTICoff;
global.params.v.errorLimit = flag_max_errors;
+ global.params.v.errorSupplementLimit = flag_max_errors;
global.params.v.messageStyle = MessageStyle::gnu;
/* Extra GDC-specific options. */
if (dmd::isError (d)|| !d->members)
return;
- if (!d->needsCodegen ())
+ if (!dmd::needsCodegen (d))
return;
for (size_t i = 0; i < d->members->length; i++)
Expression *ie = dmd::initializerToExpression (d->_init);
add_stmt (build_expr (ie));
- Expression *e = d->type->defaultInitLiteral (d->loc);
+ Expression *e = dmd::defaultInitLiteral (d->type, d->loc);
add_stmt (build_expr (e));
}
if (d->aliasTuple)
{
- this->build_dsymbol (d->toAlias ());
+ this->build_dsymbol (dmd::toAlias (d));
return;
}
DECL_INITIAL (decl) = layout_struct_initializer (ts->sym);
else
{
- Expression *e = d->type->defaultInitLiteral (d->loc);
+ Expression *e = dmd::defaultInitLiteral (d->type, d->loc);
DECL_INITIAL (decl) = build_expr (e, true);
}
}
-1017635a9636ef6a7a4bda35b1e091875d829871
+e1f6680f50d147846316c2fa3363461a2aa7ac1d
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
| [mars.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d) | Argument parsing, path manipulation. |
| [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 |
+| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d) | Configures and initializes the back-end |
| [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 |
| File | Purpose |
|-------------------------------------------------------------------------|-------------------------------------------|
-| [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 |
+| [iasm/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/package.d) | Inline assembly depending on the compiler |
+| [iasm/dmdx86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/dmd.d) | Inline assembly for DMD X86_64 |
+| [iasm/dmdaarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/aarch64.d) | Inline assembly for DMD AArch64 |
+| [iasm/gcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/gcc.d) | Inline assembly for GDC |
**Other**
| [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d) | Extract symbol names from a library in Mach-O format |
| [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format |
+
+### ABI
+| File | Purpose |
+| [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 |
### Code generation / back-end interfacing
| File | Purpose |
|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
-| [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. |
+| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph |
+| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d) | Objective-C interfacing |
+| [irgen/toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toobj.d) | Convert an AST that went through all semantic phases into an object file|
+| [irgen/toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toir.d) | Convert Dsymbols intermediate representation |
+| [irgen/e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/e2ir.d) | Convert Expressions to intermediate representation |
+| [irgen/s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/s2ir.d) | Convert Statements to intermediate representation |
+| [irgen/toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toctype.d) | Convert a D type to a type the back-end understands |
+| [irgen/tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) |
+| [irgen/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/package.d) | Generate the object file for function declarations |
+| [irgen/todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/todt.d) | Convert initializers into structures that the back-end will add to the data segment |
+| [irgen/tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tovcdebug.d)| Generate debug info in the CV4 debug format. |
+| [irgen/objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/objc.d)| irgen code for Objective-C interop. |
**Name mangling**
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
import dmd.errors;
import dmd.expression;
import dmd.funcsem : overloadApply;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include;
+import dmd.dsymbolsem : determineSize;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.func;
-import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
import dmd.tokens;
-import dmd.typesem : defaultInit, addMod, size;
import dmd.visitor;
/**
sc2.parent = this;
sc2.inunion = isUnionDeclaration();
sc2.visibility = Visibility(Visibility.Kind.public_);
- sc2.explicitVisibility = 0;
+ sc2.explicitVisibility = false;
sc2.aligndecl = null;
sc2.userAttribDecl = null;
sc2.namespace = null;
return fields.length - isNested() - (vthis2 !is null);
}
- override final uinteger_t size(Loc loc)
+ override final ulong size(Loc loc)
{
//printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
bool ok = determineSize(this, loc);
return ok ? structsize : SIZE_INVALID;
}
- /***************************************
- * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
- * field initializers have unique memory space on instance.
- * Returns:
- * true if any errors happen.
- */
- extern (D) final bool checkOverlappedFields()
- {
- //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
- assert(sizeok == Sizeok.done);
- size_t nfields = fields.length;
- if (isNested())
- {
- auto cd = isClassDeclaration();
- if (!cd || !cd.baseClass || !cd.baseClass.isNested())
- nfields--;
- if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
- nfields--;
- }
- bool errors = false;
-
- // Fill in missing any elements with default initializers
- foreach (i; 0 .. nfields)
- {
- auto vd = fields[i];
- if (vd.errors)
- {
- errors = true;
- continue;
- }
-
- const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
-
- // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
- foreach (j; 0 .. nfields)
- {
- if (i == j)
- continue;
- auto v2 = fields[j];
- if (v2.errors)
- {
- errors = true;
- continue;
- }
- if (!vd.isOverlappedWith(v2))
- continue;
-
- // vd and v2 are overlapping.
- vd.overlapped = true;
- v2.overlapped = true;
-
- if (!MODimplicitConv(vd.type.mod, v2.type.mod))
- v2.overlapUnsafe = true;
- if (!MODimplicitConv(v2.type.mod, vd.type.mod))
- vd.overlapUnsafe = true;
-
- if (i > j)
- continue;
-
- if (!v2._init)
- continue;
-
- if (v2._init.isVoidInitializer())
- continue;
-
- if (vd._init && !vdIsVoidInit && v2._init)
- {
- .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
- errors = true;
- }
- else if (v2._init && i < j)
- {
- .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
- v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars());
- errors = true;
- }
- }
- }
- return errors;
- }
override final Type getType()
{
return visibility.kind == Visibility.Kind.export_;
}
- /*******************************************
- * Look for constructor declaration.
- */
- extern (D) final Dsymbol searchCtor()
- {
- auto s = this.search(Loc.initial, Id.ctor);
- if (s)
- {
- if (!(s.isCtorDeclaration() ||
- s.isTemplateDeclaration() ||
- s.isOverloadSet()))
- {
- error(s.loc, "%s name `__ctor` is not allowed", s.kind);
- errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use");
- errors = true;
- s = null;
- }
- }
- if (s && s.toParent() != this)
- s = null; // search() looks through ancestor classes
- if (s)
- {
- // Finish all constructors semantics to determine this.noDefaultCtor.
- static int searchCtor(Dsymbol s, void*)
- {
- auto f = s.isCtorDeclaration();
- if (f && f.semanticRun == PASS.initial)
- f.dsymbolSemantic(null);
- return 0;
- }
-
- for (size_t i = 0; i < members.length; i++)
- {
- auto sm = (*members)[i];
- sm.apply(&searchCtor, null);
- }
- }
- return s;
- }
-
override final Visibility visible() pure nothrow @nogc @safe
{
return visibility;
}
}
-/*********************************
- * Iterate this dsymbol or members of this scoped dsymbol, then
- * call `fp` with the found symbol and `params`.
- * Params:
- * symbol = the dsymbol or parent of members to call fp on
- * fp = function pointer to process the iterated symbol.
- * If it returns nonzero, the iteration will be aborted.
- * ctx = context parameter passed to fp.
- * Returns:
- * nonzero if the iteration is aborted by the return value of fp,
- * or 0 if it's completed.
- */
-int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx)
-{
- if (auto nd = symbol.isNspace())
- {
- return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
- }
- if (auto ad = symbol.isAttribDeclaration())
- {
- return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
- }
- if (auto tm = symbol.isTemplateMixin())
- {
- if (tm._scope) // if fwd reference
- dsymbolSemantic(tm, null); // try to resolve it
-
- return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
- }
-
- return fp(symbol, ctx);
-}
-
/****************************
* Do byte or word alignment as necessary.
* Align sizes of 0, as we may not know array sizes yet.
static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
StructDeclaration *syntaxCopy(Dsymbol *s) override;
const char *kind() const override;
- bool isPOD();
bool zeroInit() const; // !=0 if initialize with 0 fill
bool zeroInit(bool v);
bool hasIdentityAssign() const; // true if has identity opAssign
unsigned numArgTypes() const;
Type *argType(unsigned index);
- bool hasRegularCtor(bool ignoreDisabled = false);
};
class UnionDeclaration final : public StructDeclaration
ThreeState isabstract; // if abstract class
Baseok baseok; // set the progress of base classes resolving
ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration
- Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
const char *toPrettyChars(bool QualifyTypes = false) override;
import dmd.mtype;
import dmd.common.outbuffer;
import dmd.tokens;
+import dmd.typesem : isAssignable;
import dmd.visitor;
/**********************************************
}
/// D Language version
-enum Edition : ubyte
+enum Edition : ushort
{
- none,
- legacy, /// Before the introduction of editions
- v2024, /// Experimental first new edition
- latest = v2024 /// Newest edition that this compiler knows of
+ v2023 = 2023, /// Edition.min for default edition
+ v2024, /// first Edition
+ v2025, /// next Edition
+ /// use Edition.max for latest edition
}
enum Baseok : ubyte
ref_ = 0x4_0000, /// `ref`
scope_ = 0x8_0000, /// `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`
+ scopeinferred = 0x10_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set
+ return_ = 0x20_0000, /// 'return ref' or 'return scope' for function parameters
+ returnScope = 0x40_0000, /// if `ref return scope` then resolve to `ref` and `return scope`
+ returnRef = 0x80_0000, /// if `return ref`
returninferred = 0x100_0000, /// `return` has been inferred and should not be part of mangling, `return_` must also be set
immutable_ = 0x200_0000, /// `immutable`
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : include;
import dmd.expression;
import dmd.func;
-import dmd.globals;
import dmd.hdrgen : visibilityToBuffer;
import dmd.id;
import dmd.identifier;
* the scope after it used.
*/
extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage,
- CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility,
+ CPPMANGLE cppmangle, Visibility visibility, bool explicitVisibility,
AlignDeclaration aligndecl, PragmaDeclaration inlining)
{
Scope* sc2 = sc;
this.dsym = DSYM.alignDeclaration;
if (exp)
{
- exps = new Expressions();
- exps.push(exp);
+ exps = new Expressions(exp);
}
}
import dmd.blockexit : BE, checkThrow;
import dmd.declaration;
import dmd.dsymbol;
-import dmd.dsymbolsem : include;
+import dmd.dsymbolsem : include, toAlias;
import dmd.errorsink;
import dmd.expression;
import dmd.expressionsem : errorSupplementalInferredAttr;
stc = (stc & ~STC.safe) | STC.trusted;
}
- auto fparams = new Parameters();
- fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null));
+ auto fparams = new Parameters(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null));
auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_);
auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf);
fop.storage_class |= STC.inference;
scx.eSink = sc.eSink;
/* const bool opEquals(ref const S s);
*/
- auto parameters = new Parameters();
- parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
+ auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type,
+ null, null, null));
tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
tfeqptr.mod = MODFlags.const_;
tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
}
Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
Loc loc; // loc is unnecessary so errors are gagged
- auto parameters = new Parameters();
- parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+ auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_);
tf = tf.addSTC(STC.const_).toTypeFunction();
Identifier id = Id.xopEquals;
scx.eSink = sc.eSink;
/* const int opCmp(ref const S s);
*/
- auto parameters = new Parameters();
- parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
+ auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type,
+ null, null, null));
tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
tfcmpptr.mod = MODFlags.const_;
tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
}
Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
Loc loc; // loc is unnecessary so errors are gagged
- auto parameters = new Parameters();
- parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+ auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_);
tf = tf.addSTC(STC.const_).toTypeFunction();
Identifier id = Id.xopCmp;
//printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
Loc loc; // internal code should have no loc to prevent coverage
- auto parameters = new Parameters();
- parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+ auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
Identifier id = Id.xtoHash;
auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
// workaround https://issues.dlang.org/show_bug.cgi?id=17968
" static if(is(T* : const(.object.Object)*)) " ~
" h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
+ // and another workaround for bitfields https://github.com/dlang/dmd/issues/20473
+ " else static if (!__traits(compiles, &p.tupleof[i])) {" ~
+ " auto t = p.tupleof[i];" ~
+ " h = h * 33 + typeid(T).getHash(cast(const void*)&t);" ~
+ " } " ~
" else " ~
" h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
"return h;";
*/
private CtorDeclaration generateCtorDeclaration(StructDeclaration sd, const STC paramStc, const STC funcStc, bool move)
{
- auto fparams = new Parameters();
auto structType = sd.type;
STC stc = move ? STC.none : STC.ref_; // the only difference between copy or move
- fparams.push(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null));
+ auto fparams = new Parameters(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null));
ParameterList pList = ParameterList(fparams);
auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf);
*/
module dmd.common.bitfields;
+//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
+version(Has_Bitfields)
+ version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S
+
/**
* Generate code for bit fields inside a struct/class body
* Params:
* S = type of a struct with only boolean fields, which should become bit fields
* T = type of bit fields variable, must have enough bits to store all booleans
+ * field = if provided, assume it is declared and initialized elsewhere
+ * bitOff = start using bits at the given offset
* Returns: D code with a bit fields variable and getter / setter functions
*/
-extern (D) string generateBitFields(S, T)()
+extern (D) string generateBitFields(S, T, string field = "", int bitOff = 0, int ID = __LINE__)()
if (__traits(isUnsigned, T))
{
import core.bitop: bsr;
-
+ // if _fieldName provided, assume it declared and initialized elsewhere
+ enum fieldName = field.length == 0 ? "bitFields" : field;
string result = "extern (C++) pure nothrow @nogc @safe final {";
struct BitInfo
// Iterate over members to compute bit offset and bit size for each of them
enum BitInfo bitInfo = () {
BitInfo result;
- int bitOffset = 0;
+ int bitOffset = bitOff;
foreach (size_t i, mem; __traits(allMembers, S))
{
alias memType = typeof(__traits(getMember, S, mem));
static assert(bitInfo.totalSize <= T.sizeof * 8,
"sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`");
+ version(Debugger_friendly)
+ {
+ // unique name needed to allow same name as in base class using `alias`, but without overloading
+ string bitfieldsName = fieldName ~ toString!(ID);
+ string bitfieldsRead = T.stringof~" "~bitfieldsName~"() const pure { return 0";
+ string bitfieldsWrite = "void "~bitfieldsName~"("~T.stringof~" v) {\n";
+ }
+
foreach (size_t i, mem; __traits(allMembers, S))
{
enum typeName = typeof(__traits(getMember, S, mem)).stringof;
enum shift = toString!(bitInfo.offset[i]);
enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc.
- result ~= "
- "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); }
- "~typeName~" "~mem~"("~typeName~" v) scope
+ version(Debugger_friendly)
+ {
+ string memacc = mem;
+ bitfieldsRead ~= "\n| (cast("~T.stringof~")("~memacc~" & "~sizeMask~") << "~shift~")";
+ bitfieldsWrite ~= memacc~" = cast("~typeName~")((v >> "~shift~") & "~sizeMask~");\n";
+ result ~= typeName~" "~mem;
+ version(Has_Bitfields)
+ result ~= " : "~toString!(bitInfo.size[i]);
+ enum meminit = __traits(getMember, S.init, mem);
+ result ~= " = "~meminit.stringof~";\n";
+ }
+ else
{
- bitFields &= ~("~sizeMask~" << "~shift~");
- bitFields |= v << "~shift~";
- return v;
- }";
+ result ~= "
+ "~typeName~" "~mem~"() const scope { return cast("~typeName~") (("~fieldName~" >>> "~shift~") & "~sizeMask~"); }
+ "~typeName~" "~mem~"("~typeName~" v) scope
+ {
+ "~fieldName~" &= ~("~sizeMask~" << "~shift~");
+ "~fieldName~" |= v << "~shift~";
+ return v;
+ }";
+ }
+ }
+ version(Debugger_friendly)
+ {
+ bitfieldsRead ~= ";\n}\n";
+ bitfieldsWrite ~= "}\n";
+ if (field.length == 0)
+ result ~= "alias "~fieldName~" = "~bitfieldsName~";\n";
+ result ~= bitfieldsRead ~ bitfieldsWrite;
+ result ~= "\n}\n";
+ return result;
+ }
+ else
+ {
+ result ~= "\n}\n";
+ enum TP initVal = bitInfo.initialValue;
+ if (field.length == 0)
+ result ~= " private "~T.stringof~" "~fieldName~" = " ~ toString!(initVal) ~ ";\n";
+ return result;
}
- enum TP initVal = bitInfo.initialValue;
- return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n";
}
///
offset += len;
}
- extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow @system
+ alias put = write; // transition to output range which uses put()
+
+ extern (C++) void write(scope const(void)* data, size_t nbytes) pure nothrow @system
{
- write(data[0 .. nbytes]);
+ put(data[0 .. nbytes]);
}
void write(scope const(void)[] buf) pure nothrow @trusted
offset += buf.length;
}
+ void write(scope string buf) pure nothrow @trusted // so write("hello") chooses this overload
+ {
+ if (doindent && !notlinehead)
+ indent();
+ reserve(buf.length);
+ memcpy(this.data.ptr + offset, buf.ptr, buf.length);
+ offset += buf.length;
+ }
+
+ extern (C++) void write(scope const(char)* s) pure nothrow @trusted
+ {
+ if (!s)
+ return;
+ import core.stdc.string : strlen;
+ put(s[0 .. strlen(s)]);
+ }
+
/**
* Writes a 16 bit value, no reserve check.
*/
void write16(int v) nothrow
{
auto u = cast(ushort) v;
- write(&u, u.sizeof);
+ put(&u, u.sizeof);
}
/**
*/
void write32(int v) nothrow @trusted
{
- write(&v, v.sizeof);
+ put(&v, v.sizeof);
}
/**
*/
@trusted void write64(long v) nothrow
{
- write(&v, v.sizeof);
+ put(&v, v.sizeof);
}
/// Buffer will NOT be zero-terminated
extern (C++) void writestring(const(char)* s) pure nothrow @system
{
- if (!s)
- return;
- import core.stdc.string : strlen;
- write(s[0 .. strlen(s)]);
+ put(s);
}
/// ditto
void writestring(scope const(char)[] s) pure nothrow @safe
{
- write(s);
+ put(s);
}
/// ditto
void writestring(scope string s) pure nothrow @safe
{
- write(s);
+ put(s);
}
/// Buffer will NOT be zero-terminated, followed by newline
void writestringln(const(char)[] s) pure nothrow @safe
{
- writestring(s);
+ put(s);
writenl();
}
*/
void writeStringz(const(char)* s) pure nothrow @system
{
- write(s[0 .. strlen(s)+1]);
+ put(s[0 .. strlen(s)+1]);
}
/// ditto
void writeStringz(const(char)[] s) pure nothrow @safe
{
- write(s);
+ put(s);
writeByte(0);
}
* Writes an 8 bit byte, no reserve check.
*/
extern (C++) nothrow @safe
- void writeByten(int b)
+ void writeByten(ubyte b)
{
- this.data[offset++] = cast(ubyte) b;
+ this.data[offset++] = b;
}
- extern (C++) void writeByte(uint b) pure nothrow @safe
+ extern (C++) void writeByte(ubyte b) pure nothrow @safe
{
if (doindent && !notlinehead && b != '\n')
indent();
reserve(1);
- this.data[offset] = cast(ubyte)b;
+ this.data[offset] = b;
+ offset++;
+ }
+
+ void write(ubyte b) pure nothrow @safe
+ {
+ if (doindent && !notlinehead && b != '\n')
+ indent();
+ reserve(1);
+ this.data[offset] = b;
offset++;
}
offset += 4;
}
- extern (C++) void write(const OutBuffer* buf) pure nothrow @trusted
+ extern (C++) void write(scope const OutBuffer* buf) pure nothrow @trusted
{
if (buf)
- {
- reserve(buf.offset);
- memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
- offset += buf.offset;
- }
+ put(*buf);
+ }
+
+ void write(ref scope const OutBuffer buf) pure nothrow @trusted
+ {
+ reserve(buf.offset);
+ memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
+ offset += buf.offset;
}
extern (C++) void fill0(size_t nbytes) pure nothrow @trusted
buf.prependbyte('x');
OutBuffer buf2;
buf2.writestring("mmm");
- buf.write(&buf2);
+ buf.put(&buf2);
char[] s = buf.extractSlice();
assert(s == "xdefabcmmm");
}
void writestring(const char *string);
void prependstring(const char *string);
void writenl(); // write newline
- void writeByte(unsigned b);
+ void writeByte(uint8_t b);
void writeUTF8(unsigned b);
void prependbyte(unsigned b);
void writewchar(unsigned w);
import dmd.location;
import dmd.mtype;
import dmd.optimize;
-import dmd.typesem;
import dmd.common.outbuffer;
import dmd.rootobject;
import dmd.root.string;
auto sid = Identifier.generateId("Tuple");
auto sdecl = new StructDeclaration(loc, sid, false);
sdecl.storage_class |= STC.static_;
- sdecl.members = new Dsymbols();
auto fid = Identifier.idPool(tupleFieldName);
auto ty = new TypeTypeof(loc, new TupleExp(loc, e));
- sdecl.members.push(new VarDeclaration(loc, ty, fid, null, STC.none));
+ sdecl.members = new Dsymbols(new VarDeclaration(loc, ty, fid, null, STC.none));
auto r = cast(TypeStruct)sdecl.type;
if (global.params.useTypeInfo && Type.dtypeinfo)
r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file
import dmd.dstruct;
import dmd.errors;
import dmd.expression;
+import dmd.expressionsem : getField;
import dmd.globals;
import dmd.location;
import dmd.mtype;
else
{
// Create an ArrayLiteralExp
- auto elements = new Expressions();
- elements.push(e);
+ auto elements = new Expressions(e);
emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements);
}
assert(ue.exp().type);
Type tb = t.toBasetype();
if (tb.ty == Tarray && tb.nextOf().equivalent(e.type))
{
- auto expressions = new Expressions();
- expressions.push(e);
+ auto expressions = new Expressions(e);
emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions);
e = ue.exp();
}
case TOK.asm_:
switch (peekNext())
{
+ case TOK.const_:
case TOK.goto_:
case TOK.inline:
+ case TOK.restrict:
case TOK.volatile:
case TOK.leftParenthesis:
s = cparseGnuAsm();
* logical-OR-expression
* logical-OR-expression ? expression : conditional-expression
*/
- private AST.Expression cparseCondExp()
+ AST.Expression cparseCondExp()
{
const loc = token.loc;
*/
private AST.Expression cparseConstantExp()
{
- return cparseAssignExp();
+ return cparseCondExp();
}
/*****************************
cparseParens();
}
}
+ else if (token.value == TOK._Noreturn)
+ {
+ specifier.noreturn = true;
+ nextToken();
+ }
else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
nextToken();
else
nextToken();
- // Consume all asm-qualifiers. As a future optimization, we could record
- // the `inline` and `volatile` storage classes against the statement.
- while (token.value == TOK.goto_ ||
- token.value == TOK.inline ||
- token.value == TOK.volatile)
- nextToken();
+ // Consume all asm-qualifiers.
+ enum AsmQualifiers { none = 0, goto_ = 1, inline = 2, volatile = 4, }
+ AsmQualifiers asmQualifiers;
+ while (1)
+ {
+ AsmQualifiers qual;
+ switch (token.value)
+ {
+ case TOK.goto_:
+ qual = AsmQualifiers.goto_;
+ goto Lcontinue;
+
+ case TOK.inline:
+ qual = AsmQualifiers.inline;
+ goto Lcontinue;
+
+ case TOK.volatile:
+ qual = AsmQualifiers.volatile;
+ goto Lcontinue;
+
+ Lcontinue:
+ if (asmQualifiers & qual)
+ error(token.loc, "duplicate `asm` qualifier `%s`", token.toChars());
+ else
+ asmQualifiers |= qual;
+ nextToken();
+ continue;
+
+ case TOK.const_:
+ case TOK.restrict:
+ error(token.loc, "`%s` is not a valid `asm` qualifier", token.toChars());
+ nextToken();
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
check(TOK.leftParenthesis);
if (token.value != TOK.string_)
error("string literal expected for Assembler Template, not `%s`", token.toChars());
Token* toklist = null;
Token** ptoklist = &toklist;
- //Identifier label = null;
auto statements = new AST.Statements();
int parens;
error("matching `)` expected, not end of file");
break;
- case TOK.colonColon: // treat as two separate : tokens for iasmgcc
- *ptoklist = allocateToken();
- **ptoklist = this.token;
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
-
- *ptoklist = allocateToken();
- **ptoklist = this.token;
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
-
- *ptoklist = null;
- nextToken();
- continue;
-
default:
*ptoklist = allocateToken();
**ptoklist = this.token;
if (toklist)
{
// Create AsmStatement from list of tokens we've saved
- AST.Statement s = new AST.AsmStatement(token.loc, toklist);
+ auto s = new AST.AsmStatement(token.loc, toklist);
+ if (asmQualifiers & AsmQualifiers.volatile)
+ s.isVolatile = true;
+ if (asmQualifiers & AsmQualifiers.inline)
+ s.isInline = true;
statements.push(s);
}
break;
{
//printf("addSym() %s\n", s.toChars());
if (auto v = s.isVarDeclaration())
- v.isCmacro(true); // mark it as coming from a C #define
+ v.isCmacro = true; // mark it as coming from a C #define
if (auto td = s.isTemplateDeclaration())
td.isCmacro = true; // mark as coming from a C #define
/* If it's already defined, replace the earlier
import dmd.errors;
import dmd.expression;
import dmd.func;
-import dmd.globals;
+import dmd.globals : dinteger_t, sinteger_t, uinteger_t;
import dmd.location;
import dmd.mtype;
import dmd.root.bitarray;
AssocArrayLiteralExp r = ue.exp().isAssocArrayLiteralExp();
r.type = aae.type;
r.lowering = aae.lowering;
+ r.loweringCtfe = aae.loweringCtfe;
r.ownedByCtfe = OwnedBy.ctfe;
return ue;
}
/* Given an AA literal 'ae', and a key 'e2':
* Return ae[e2] if present, or NULL if not found.
*/
-Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2)
+Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2, size_t* pidx = null)
{
/* Search the keys backwards, in case there are duplicate keys
*/
const int eq = ctfeEqual(loc, EXP.equal, ekey, e2);
if (eq)
{
+ if (pidx)
+ *pidx = i;
return (*ae.values)[i];
}
}
*/
void assignInPlace(Expression dest, Expression src)
{
- if (!(dest.op == EXP.structLiteral || dest.op == EXP.arrayLiteral || dest.op == EXP.string_))
- {
- printf("invalid op %d %d\n", src.op, dest.op);
- assert(0);
- }
Expressions* oldelems;
Expressions* newelems;
if (dest.op == EXP.structLiteral)
sliceAssignStringFromArrayLiteral(dest.isStringExp(), src.isArrayLiteralExp(), 0);
return;
}
+ else if (dest.op == EXP.int64 && src.op == EXP.int64)
+ {
+ dest.isIntegerExp().setInteger(src.isIntegerExp().getInteger());
+ return;
+ }
+ else if (dest.op == EXP.float64 && src.op == EXP.float64)
+ {
+ dest.isRealExp().value = src.isRealExp().value;
+ return;
+ }
else
{
printf("invalid op %d %d\n", src.op, dest.op);
(
(e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) ||
e1.op == EXP.variable ||
+ e1.op == EXP.int64 ||
+ e1.op == EXP.float64 ||
+ e1.op == EXP.string_ ||
e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) ||
e1.op == EXP.index && isCtfeReferenceValid(e1) ||
e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray
import dmd.errorsink : ErrorSink;
import dmd.expression /*: Expression*/;
import dmd.func : FuncDeclaration;
-import dmd.globals;
+import dmd.globals : dinteger_t, uinteger_t, JsonFieldFlags;
import dmd.identifier : Identifier;
import dmd.init : Initializer, NeedInterpret;
import dmd.location : Loc;
import dmd.mtype /*: Covariant, Type, Parameter, ParameterList*/;
import dmd.rootobject : RootObject;
+import dmd.semantic3;
import dmd.statement : Statement, AsmStatement, GccAsmStatement;
// NB: At some point in the future, we can switch to shortened function syntax.
*/
FuncDeclaration search_toString(StructDeclaration sd)
{
- return dmd.dstruct.search_toString(sd);
+ return dmd.semantic3.search_toString(sd);
}
/***********************************************************
return dmd.dsymbolsem.getLocalClasses(mod, aclasses);
}
+Dsymbol toAlias(Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.toAlias(s);
+}
+
+Dsymbol toAlias2(Dsymbol s)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.toAlias2(s);
+}
+
+bool isPOD(StructDeclaration sd)
+{
+ import dmd.dsymbolsem;
+ return dmd.dsymbolsem.isPOD(sd);
+}
+
/***********************************************************
* dtemplate.d
*/
return dmd.dtemplate.printTemplateStats(listInstances, eSink);
}
+void printInstantiationTrace(TemplateInstance ti)
+{
+ return ti.printInstantiationTrace();
+}
+
/***********************************************************
* dtoh.d
*/
*/
Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
{
- import dmd.iasmgcc;
- return dmd.iasmgcc.gccAsmSemantic(s, sc);
+ import dmd.iasm.gcc;
+ return dmd.iasm.gcc.gccAsmSemantic(s, sc);
}
void gccAsmSemantic(CAsmDeclaration d, Scope* sc)
{
- import dmd.iasmgcc;
- return dmd.iasmgcc.gccAsmSemantic(d, sc);
+ import dmd.iasm.gcc;
+ return dmd.iasm.gcc.gccAsmSemantic(d, sc);
}
/***********************************************************
return dmd.typesem.constConv(from, to);
}
+Expression defaultInitLiteral(Type t, Loc loc)
+{
+ import dmd.typesem;
+ return dmd.typesem.defaultInitLiteral(t, loc);
+}
+
/***********************************************************
* typinf.d
*/
return dmd.typinf.getTypeInfoAssocArrayDeclaration(t, sc);
}
+/**
+ * templatesem.d
+ */
+bool needsCodegen(TemplateInstance ti)
+{
+ import dmd.templatesem;
+ return dmd.templatesem.needsCodegen(ti);
+}
+
+bool isDiscardable(TemplateInstance ti)
+{
+ import dmd.templatesem;
+ return dmd.templatesem.isDiscardable(ti);
+}
version (IN_LLVM)
{
/***********************************************************
(*ae.keys)[i] = ex;
}
ae.type = t;
- semanticTypeInfo(sc, ae.type);
+ ae.lowering = null; // we need a different lowering
+ ae.loweringCtfe = null;
+ ae.expressionSemantic(sc);
return ae;
}
return visit(e);
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
-import dmd.gluelayer;
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
*/
ObjcClassDeclaration objc;
- Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
-
final extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
{
objc = ObjcClassDeclaration(this);
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic;
+import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.func;
import dmd.globals;
-import dmd.gluelayer;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.init;
-import dmd.initsem : initializerToExpression, initializerSemantic;
import dmd.intrange;
import dmd.location;
import dmd.mtype;
import dmd.root.filename;
import dmd.target;
import dmd.tokens;
-import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers;
+import dmd.typesem : typeSemantic, size;
import dmd.visitor;
version (IN_GCC) {}
return "declaration";
}
- override final uinteger_t size(Loc loc)
+ override final ulong size(Loc loc)
{
assert(type);
const sz = type.size();
return tupletype;
}
- override Dsymbol toAlias2()
- {
- //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
- for (size_t i = 0; i < objects.length; i++)
- {
- RootObject o = (*objects)[i];
- if (Dsymbol s = isDsymbol(o))
- {
- s = s.toAlias2();
- (*objects)[i] = s;
- }
- }
- return this;
- }
-
override bool needThis()
{
//printf("TupleDeclaration::needThis(%s)\n", toChars());
{
if (type)
return type;
- return toAlias().getType();
- }
-
- override Dsymbol toAlias()
- {
- static if (0)
- printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
- loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse);
- assert(this != aliassym);
- //static int count; if (++count == 10) *(char*)0=0;
-
- Dsymbol err()
- {
- // Avoid breaking "recursive alias" state during errors gagged
- if (global.gag)
- return this;
- aliassym = new AliasDeclaration(loc, ident, Type.terror);
- type = Type.terror;
- return aliassym;
- }
- // Reading the AliasDeclaration
- if (!this.ignoreRead)
- this.wasRead = true; // can never assign to this AliasDeclaration again
-
- if (inuse == 1 && type && _scope)
- {
- inuse = 2;
- const olderrors = global.errors;
- Dsymbol s = type.toDsymbol(_scope);
- //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
- if (global.errors != olderrors)
- return err();
- if (s)
- {
- s = s.toAlias();
- if (global.errors != olderrors)
- return err();
- aliassym = s;
- inuse = 0;
- }
- else
- {
- Type t = type.typeSemantic(loc, _scope);
- if (t.ty == Terror)
- return err();
- if (global.errors != olderrors)
- return err();
- //printf("t = %s\n", t.toChars());
- inuse = 0;
- }
- }
- if (inuse)
- {
- .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars);
- return err();
- }
-
- if (semanticRun >= PASS.semanticdone)
- {
- // semantic is already done.
-
- // Do not see aliassym !is null, because of lambda aliases.
-
- // Do not see type.deco !is null, even so "alias T = const int;` needs
- // semantic analysis to take the storage class `const` as type qualifier.
- }
- 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,
- * load the module first.
- */
- _import.dsymbolSemantic(null);
- }
- if (_scope)
- {
- aliasSemantic(this, _scope);
- }
- }
-
- inuse = 1;
- Dsymbol s = aliassym ? aliassym.toAlias() : this;
- inuse = 0;
- return s;
- }
-
- override Dsymbol toAlias2()
- {
- if (inuse)
- {
- .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars);
- return this;
- }
- inuse = 1;
- Dsymbol s = aliassym ? aliassym.toAlias2() : this;
- inuse = 0;
- return s;
+ return toAlias(this).getType();
}
override bool isOverloadable() const
{
isdataseg = 2; // The Variables does not go into the datasegment
- if (!canTakeAddressOf())
+ if (!canTakeAddressOf() || (storage_class & STC.exptemp))
{
return false;
}
auto bitoffset = offset * 8;
auto vbitoffset = v.offset * 8;
- // Bitsize of types are overridden by any bit-field widths.
+ // Bitsize of types are overridden by any bitfield widths.
ulong tbitsize;
if (auto bf = isBitFieldDeclaration())
{
return edtor && !(storage_class & STC.nodtor);
}
- /*******************************************
- * If variable has a constant expression initializer, get it.
- * Otherwise, return null.
- */
- extern (D) final Expression getConstInitializer(bool needFullType = true)
- {
- assert(type && _init);
-
- // Ungag errors when not speculative
- const oldgag = global.gag;
- if (global.gag)
- {
- Dsymbol sym = isMember();
- if (sym && !sym.isSpeculative())
- global.gag = 0;
- }
-
- if (_scope)
- {
- inuse++;
- _init = _init.initializerSemantic(_scope, type, INITinterpret);
- import dmd.semantic2 : lowerStaticAAs;
- lowerStaticAAs(this, _scope);
- _scope = null;
- inuse--;
- }
-
- Expression e = _init.initializerToExpression(needFullType ? type : null);
- global.gag = oldgag;
- return e;
- }
-
- override final Dsymbol toAlias()
- {
- //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
- if ((!type || !type.deco) && _scope)
- dsymbolSemantic(this, _scope);
-
- assert(this != aliasTuple);
- Dsymbol s = aliasTuple ? aliasTuple.toAlias() : this;
- return s;
- }
-
override void accept(Visitor v)
{
v.visit(this);
extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration
{
Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next)
+ Declaration xopEqual; // implementation of TypeInfo_AssociativeArray.equals
+ Declaration xtoHash; // implementation of TypeInfo_AssociativeArray.getHash
extern (D) this(Type tinfo)
{
#define STCref 0x40000ULL /// `ref`
#define STCscope 0x80000ULL /// `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`
+ #define STCscopeinferred 0x100000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set
+ #define STCreturn 0x200000ULL /// 'return ref' or 'return scope' for function parameters
+ #define STCreturnScope 0x400000ULL /// if `ref return scope` then resolve to `ref` and `return scope`
+ #define STCreturnRef 0x800000ULL, /// if `return ref`
#define STCreturninferred 0x1000000ULL /// `return` has been inferred and should not be part of mangling, `return` must also be set
#define STCimmutable 0x2000000ULL /// `immutable`
TupleDeclaration *syntaxCopy(Dsymbol *) override;
const char *kind() const override;
Type *getType() override;
- Dsymbol *toAlias2() override;
bool needThis() override;
void accept(Visitor *v) override { v->visit(this); }
bool overloadInsert(Dsymbol *s) override;
const char *kind() const override;
Type *getType() override;
- Dsymbol *toAlias() override;
- Dsymbol *toAlias2() override;
bool isOverloadable() const override;
void accept(Visitor *v) override { v->visit(this); }
bool equals(const RootObject * const o) const override;
bool overloadInsert(Dsymbol *s) override;
- Dsymbol *toAlias() override;
Dsymbol *isUnique();
bool isOverloadable() const override;
bool isOverlappedWith(VarDeclaration *v);
bool canTakeAddressOf();
bool needsScopeDtor();
- Dsymbol *toAlias() override final;
// Eliminate need for dynamic_cast
void accept(Visitor *v) override { v->visit(this); }
};
{
public:
Type* entry;
+ Declaration* xopEqual;
+ Declaration* xtoHash;
static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo);
virtual bool addPreInvariant();
virtual bool addPostInvariant();
const char *kind() const override;
- bool isUnique();
bool needsClosure();
bool hasNestedFrameRefs();
ParameterList getParameterList();
iz.accept(this);
}
}
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (e.lowering)
+ walkPostorder(e.lowering, this);
+ }
}
scope LambdaSetParent lsp = new LambdaSetParent(fd);
import dmd.astenums;
import dmd.attrib;
-import dmd.gluelayer;
import dmd.declaration;
import dmd.dsymbol;
import dmd.expression;
import dmd.common.bitfields : generateBitFields;
mixin(generateBitFields!(BitFields, ubyte));
- Symbol* sinit;
+ void* sinit;
extern (D) this(Loc loc, Identifier ident, Type memtype)
{
scopesym.addAccessiblePackage(mod, visibility); // d
}
- override Dsymbol toAlias()
- {
- if (aliasId)
- return mod;
- return this;
- }
-
override bool overloadInsert(Dsymbol s)
{
/* Allow multiple imports with the same package base, but disallow
import dmd.root.utf;
import dmd.statement;
import dmd.tokens;
-import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size;
+import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size, merge, defaultInitLiteral;
import dmd.utils : arrayCastBigEndian;
import dmd.visitor;
istatex.caller = istate;
istatex.fd = fd;
- if (fd.hasDualContext())
+ if (fd.hasDualContext)
{
Expression arg0 = thisarg;
if (arg0 && arg0.type.ty == Tstruct)
if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis)
e = thisarg;
- if (tf.isRef && fd.hasDualContext() && e.op == EXP.index)
+ if (tf.isRef && fd.hasDualContext && e.op == EXP.index)
{
auto ie = e.isIndexExp();
auto pe = ie.e1.isPtrExp();
if (istate && istate.fd.vthis)
{
result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis);
- if (istate.fd.hasDualContext())
+ if (istate.fd.hasDualContext)
{
result = ctfeEmplaceExp!PtrExp(e.loc, result);
result.type = Type.tvoidptr.sarrayOf(2);
result = ctfeGlobals.stack.getThis();
if (result)
{
- if (istate && istate.fd.hasDualContext())
+ if (istate && istate.fd.hasDualContext)
{
assert(result.op == EXP.address);
result = result.isAddrExp().e1;
if (ExpInitializer ie = v._init.isExpInitializer())
{
result = interpretRegion(ie.exp, istate, goal);
+ if (result !is null && v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone)
+ if (!getValue(v))
+ setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref
return;
}
else if (v._init.isVoidInitializer())
{
result = v._init.initializerToExpression(v.type);
if (result !is null)
+ {
+ if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone)
+ if (!getValue(v))
+ setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref
return;
+ }
}
error(e.loc, "declaration `%s` is not yet implemented in CTFE", e.toChars());
result = CTFEExp.cantexp;
valuesx !is e.values);
auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
aae.type = e.type;
+ aae.lowering = e.lowering;
+ aae.loweringCtfe = e.loweringCtfe;
aae.ownedByCtfe = OwnedBy.ctfe;
result = aae;
}
// ---------------------------------------
// Interpret left hand side
// ---------------------------------------
- AssocArrayLiteralExp existingAA = null;
- Expression lastIndex = null;
Expression oldval = null;
if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
- // ---------------------------------------
- // Deal with AA index assignment
- // ---------------------------------------
- /* This needs special treatment if the AA doesn't exist yet.
- * There are two special cases:
- * (1) If the AA is itself an index of another AA, we may need to create
- * multiple nested AA literals before we can insert the new value.
- * (2) If the ultimate AA is null, no insertion happens at all. Instead,
- * we create nested AA literals, and change it into a assignment.
- */
- IndexExp ie = e1.isIndexExp();
- int depth = 0; // how many nested AA indices are there?
- while (ie.e1.op == EXP.index && ie.e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
- {
- assert(ie.modifiable);
- ie = ie.e1.isIndexExp();
- ++depth;
- }
-
- // Get the AA value to be modified.
- Expression aggregate = interpretRegion(ie.e1, istate);
- if (exceptionOrCant(aggregate))
- return;
- if ((existingAA = aggregate.isAssocArrayLiteralExp()) !is null)
- {
- // Normal case, ultimate parent AA already exists
- // We need to walk from the deepest index up, checking that an AA literal
- // already exists on each level.
- lastIndex = interpretRegion(e1.isIndexExp().e2, istate);
- lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
- if (exceptionOrCant(lastIndex))
- return;
-
- while (depth > 0)
- {
- // Walk the syntax tree to find the indexExp at this depth
- IndexExp xe = e1.isIndexExp();
- foreach (d; 0 .. depth)
- xe = xe.e1.isIndexExp();
-
- Expression ekey = interpretRegion(xe.e2, istate);
- if (exceptionOrCant(ekey))
- return;
- UnionExp ekeyTmp = void;
- ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment
-
- // Look up this index in it up in the existing AA, to get the next level of AA.
- AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey);
- if (exceptionOrCant(newAA))
- return;
- if (!newAA)
- {
- // Doesn't exist yet, create an empty AA...
- auto keysx = new Expressions();
- auto valuesx = new Expressions();
- newAA = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
- newAA.type = xe.type;
- newAA.ownedByCtfe = OwnedBy.ctfe;
- //... and insert it into the existing AA.
- existingAA.keys.push(ekey);
- existingAA.values.push(newAA);
- }
- existingAA = newAA;
- --depth;
- }
-
- if (fp)
- {
- oldval = findKeyInAA(e.loc, existingAA, lastIndex);
- if (!oldval)
- oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
- }
- }
- else
- {
- /* The AA is currently null. 'aggregate' is actually a reference to
- * whatever contains it. It could be anything: var, dotvarexp, ...
- * We rewrite the assignment from:
- * aa[i][j] op= newval;
- * into:
- * aa = [i:[j:T.init]];
- * aa[j] op= newval;
- */
- oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
-
- Expression newaae = oldval;
- while (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
- {
- Expression ekey = interpretRegion(e1.isIndexExp().e2, istate);
- if (exceptionOrCant(ekey))
- return;
- ekey = resolveSlice(ekey); // only happens with AA assignment
-
- auto keysx = new Expressions();
- auto valuesx = new Expressions();
- keysx.push(ekey);
- valuesx.push(newaae);
-
- auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
- aae.type = e1.isIndexExp().e1.type;
- aae.ownedByCtfe = OwnedBy.ctfe;
- if (!existingAA)
- {
- existingAA = aae;
- lastIndex = ekey;
- }
- newaae = aae;
- e1 = e1.isIndexExp().e1;
- }
-
- // We must set to aggregate with newaae
- e1 = interpretRegion(e1, istate, CTFEGoal.LValue);
- if (exceptionOrCant(e1))
- return;
- e1 = assignToLvalue(e, e1, newaae);
- if (exceptionOrCant(e1))
- return;
- }
- assert(existingAA && lastIndex);
- e1 = null; // stomp
+ assert(false, "indexing AA should have been lowered in semantic analysis");
}
else if (e1.op == EXP.arrayLength)
{
if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
- IndexExp ie = e1.isIndexExp();
- assert(ie.e1.op == EXP.assocArrayLiteral);
- existingAA = ie.e1.isAssocArrayLiteralExp();
- lastIndex = ie.e2;
+ assert(false, "indexing AA should have been lowered in semantic analysis");
}
}
}
}
- if (existingAA)
- {
- if (existingAA.ownedByCtfe != OwnedBy.ctfe)
- {
- error(e.loc, "cannot modify read-only constant `%s`", existingAA.toChars());
- result = CTFEExp.cantexp;
- return;
- }
-
- //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
- // __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars());
- assignAssocArrayElement(e.loc, existingAA, lastIndex, newval);
-
- // Determine the return value
- result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
- return;
- }
if (e1.op == EXP.arrayLength)
{
/* Change the assignment from:
if (newval == pue.exp())
newval = pue.copy();
- e1 = assignToLvalue(e, e1, newval);
+ e1 = assignToLvalue(e, e1, newval, istate);
if (exceptionOrCant(e1))
return;
/* Assignment to a CTFE reference.
*/
- if (Expression ex = assignToLvalue(e, e1, newval))
+ if (Expression ex = assignToLvalue(e, e1, newval, istate))
result = ex;
return;
/* Set all sibling fields which overlap with v to VoidExp.
*/
- private void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v)
+ private static void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v)
{
if (!v.overlapped)
return;
}
}
- private Expression assignToLvalue(BinExp e, Expression e1, Expression newval)
+ private static Expression assignToLvalue(BinExp e, Expression e1, Expression newval, InterState* istate)
{
//printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
VarDeclaration vd = null;
ArrayLiteralExp existingAE = aggregate.isArrayLiteralExp();
if (existingAE.ownedByCtfe != OwnedBy.ctfe)
{
- error(e.loc, "cannot modify read-only constant `%s`", existingAE.toChars());
+ Expression literal = existingAE.aaLiteral ? existingAE.aaLiteral : existingAE;
+ error(e.loc, "cannot modify read-only constant `%s`", literal.toChars());
return CTFEExp.cantexp;
}
}
}
- if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors())
+ if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors)
{
error(e.loc, "CTFE failed because of previous errors in `%s`", fd.toChars());
result = CTFEExp.cantexp;
dinteger_t ofs;
Expression agg = getAggregateFromPointer(e1, &ofs);
- if (agg.op == EXP.null_)
- {
- error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars());
- return false;
- }
- if (agg.op == EXP.int64)
- {
- error(e.loc, "cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
- return false;
- }
// Pointer to a non-array variable
if (agg.op == EXP.symbolOffset)
{
}
else
{
+ // agg is the value accessed, it is not dereferenced again, so offset 0 is always ok
if (ofs + indx != 0)
{
- error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx);
+ if (agg.op == EXP.null_)
+ error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars());
+ else if (agg.op == EXP.int64)
+ error(e.loc, "cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
+ else
+ error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx);
return false;
}
}
if (e.e1.type.toBasetype().ty == Taarray)
{
- Expression e1 = interpretRegion(e.e1, istate);
- if (exceptionOrCant(e1))
- return;
- if (e1.op == EXP.null_)
- {
- if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable)
- {
- assert(0); // does not reach here?
- }
- error(e.loc, "cannot index null array `%s`", e.e1.toChars());
- result = CTFEExp.cantexp;
- return;
- }
- Expression e2 = interpretRegion(e.e2, istate);
- if (exceptionOrCant(e2))
- return;
-
- if (goal == CTFEGoal.LValue)
- {
- // Pointer or reference of a scalar type
- if (e1 == e.e1 && e2 == e.e2)
- result = e;
- else
- {
- emplaceExp!(IndexExp)(pue, e.loc, e1, e2);
- result = pue.exp();
- result.type = e.type;
- }
- return;
- }
-
- assert(e1.op == EXP.assocArrayLiteral);
- UnionExp e2tmp = void;
- e2 = resolveSlice(e2, &e2tmp);
- result = findKeyInAA(e.loc, e1.isAssocArrayLiteralExp(), e2);
- if (!result)
- {
- error(e.loc, "key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars());
- result = CTFEExp.cantexp;
- }
- return;
+ assert(false, "indexing AA should have been lowered in semantic analysis");
}
Expression agg;
result.type = e.type;
}
- override void visit(InExp e)
- {
- debug (LOG)
- {
- printf("%s InExp::interpret() %s\n", e.loc.toChars(), e.toChars());
- }
- Expression e1 = interpretRegion(e.e1, istate);
- if (exceptionOrCant(e1))
- return;
- Expression e2 = interpretRegion(e.e2, istate);
- if (exceptionOrCant(e2))
- return;
- if (e2.op == EXP.null_)
- {
- emplaceExp!(NullExp)(pue, e.loc, e.type);
- result = pue.exp();
- return;
- }
- if (e2.op != EXP.assocArrayLiteral)
- {
- error(e.loc, "`%s` cannot be interpreted at compile time", e.toChars());
- result = CTFEExp.cantexp;
- return;
- }
-
- e1 = resolveSlice(e1);
- result = findKeyInAA(e.loc, e2.isAssocArrayLiteralExp(), e1);
- if (exceptionOrCant(result))
- return;
- if (!result)
- {
- emplaceExp!(NullExp)(pue, e.loc, e.type);
- result = pue.exp();
- }
- else
- {
- // Create a CTFE pointer &aa[index]
- result = ctfeEmplaceExp!IndexExp(e.loc, e2, e1);
- result.type = e.type.nextOf();
- emplaceExp!(AddrExp)(pue, e.loc, result, e.type);
- result = pue.exp();
- }
- }
-
override void visit(CatExp e)
{
debug (LOG)
}
}
- override void visit(RemoveExp e)
- {
- debug (LOG)
- {
- printf("%s RemoveExp::interpret() %s\n", e.loc.toChars(), e.toChars());
- }
- Expression agg = interpret(e.e1, istate);
- if (exceptionOrCant(agg))
- return;
- Expression index = interpret(e.e2, istate);
- if (exceptionOrCant(index))
- return;
- if (agg.op == EXP.null_)
- {
- result = CTFEExp.voidexp;
- return;
- }
-
- AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp();
- Expressions* keysx = aae.keys;
- Expressions* valuesx = aae.values;
- size_t removed = 0;
- foreach (j, evalue; *valuesx)
- {
- Expression ekey = (*keysx)[j];
- int eq = ctfeEqual(e.loc, EXP.equal, ekey, index);
- if (eq)
- ++removed;
- else if (removed != 0)
- {
- (*keysx)[j - removed] = ekey;
- (*valuesx)[j - removed] = evalue;
- }
- }
- valuesx.length = valuesx.length - removed;
- keysx.length = keysx.length - removed;
- result = IntegerExp.createBool(removed != 0);
- }
-
override void visit(ClassReferenceExp e)
{
//printf("ClassReferenceExp::interpret() %s\n", e.value.toChars());
}
// Little sanity check to make sure it's really a Throwable
ClassReferenceExp boss = oldest.thrown;
- const next = 5; // index of Throwable.next
- assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
+ const next = 5; // index of Throwable._nextInChainPtr
+ with ((*boss.value.elements)[next].type) // Throwable._nextInChainPtr
+ assert(ty == Tpointer || ty == Tclass);
ClassReferenceExp collateral = newest.thrown;
if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
{
return pue.exp();
}
+// signature is bool _d_aaDel(V[K] aa, K key)
+private Expression interpret_aaDel(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+ Expression agg = interpret(aa, istate);
+ if (exceptionOrCantInterpret(agg))
+ return agg;
+ Expression index = interpret(key, istate);
+ if (exceptionOrCantInterpret(index))
+ return index;
+ if (agg.op == EXP.null_)
+ return CTFEExp.voidexp; //???
+
+ AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp();
+ Expressions* keysx = aae.keys;
+ Expressions* valuesx = aae.values;
+ size_t removed = 0;
+ foreach (j, evalue; *valuesx)
+ {
+ Expression ekey = (*keysx)[j];
+ int eq = ctfeEqual(aa.loc, EXP.equal, ekey, index);
+ if (eq)
+ ++removed;
+ else if (removed != 0)
+ {
+ (*keysx)[j - removed] = ekey;
+ (*valuesx)[j - removed] = evalue;
+ }
+ }
+ valuesx.length = valuesx.length - removed;
+ keysx.length = keysx.length - removed;
+ return IntegerExp.createBool(removed != 0);
+}
+
+// signature is bool _d_aaDel(V[K] aa1, V[K] aa2)
+private Expression interpret_aaEqual(UnionExp* pue, InterState* istate, Expression aa1, Expression aa2)
+{
+ Expression e1 = interpret(aa1, istate);
+ if (exceptionOrCantInterpret(e1))
+ return e1;
+ Expression e2 = interpret(aa2, istate);
+ if (exceptionOrCantInterpret(e2))
+ return e2;
+
+ bool equal = ctfeEqual(aa1.loc, EXP.equal, e1, e2);
+ return IntegerExp.createBool(equal);
+}
+
private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression earg)
{
debug (LOG)
if (Expression e = evaluatePostblit(istate, (*aae.values)[i]))
return e;
}
- aae.type = earg.type.mutableOf(); // repaint type from const(int[int]) to const(int)[int]
+ // repaint type from const(int[int]) to int[int]
+ if (auto taa = earg.type.toBasetype().isTypeAArray())
+ {
+ auto aatype = new TypeAArray(taa.next.mutableOf(), taa.index);
+ aae.type = aatype.merge();
+ }
+ else
+ aae.type = earg.type.mutableOf();
//printf("result is %s\n", aae.toChars());
return aae;
}
+// signature is bool V* _d_aaIn(V[K] aa, K key)
+private Expression interpret_aaIn(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+ debug (LOG)
+ {
+ printf("%s _d_aaIn::interpret() %s in %s\n", aa.loc.toChars(), key.toChars(), aa.toChars());
+ }
+ Expression eaa = interpretRegion(aa, istate);
+ if (exceptionOrCantInterpret(eaa))
+ return eaa;
+ Expression ekey = interpretRegion(key, istate);
+ if (exceptionOrCantInterpret(ekey))
+ return ekey;
+
+ if (eaa.op != EXP.null_)
+ {
+ auto aalit = eaa.isAssocArrayLiteralExp();
+ if (!aalit)
+ {
+ error(aa.loc, "`%s` cannot be interpreted at compile time", aa.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ size_t idx;
+ auto result = findKeyInAA(aa.loc, aalit, ekey, &idx);
+ if (exceptionOrCantInterpret(result))
+ return result;
+ if (result)
+ return pointerToAAValue(pue, aa, aalit, idx);
+ }
+ emplaceExp!(NullExp)(pue, aa.loc, aa.type.nextOf().pointerTo());
+ return pue.exp();
+}
+
+// signature is V* _d_aaGetRvalueX(V[K] aa, K key)
+private Expression interpret_aaGetRvalueX(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+ Expression e1 = interpret(aa, istate);
+ if (exceptionOrCantInterpret(e1))
+ return e1;
+ Expression e2 = interpretRegion(key, istate);
+ if (exceptionOrCantInterpret(e2))
+ return e2;
+
+ auto aalit = e1.isAssocArrayLiteralExp();
+ if (!aalit)
+ {
+ error(aa.loc, "cannot index null array `%s`", aa.toChars());
+ return CTFEExp.cantexp;
+ }
+ size_t idx;
+ Expression result = findKeyInAA(aa.loc, aalit, e2, &idx);
+ if (!result)
+ {
+ error(aa.loc, "key `%s` not found in associative array `%s`", key.toChars(), aa.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ return pointerToAAValue(pue, aa, aalit, idx);
+}
+
+// signature is V* _d_aaGetY(ref V[K] aa, K key, out bool found)
+private Expression interpret_aaGetY(UnionExp* pue, InterState* istate, Expression aa, Expression key, Expression found)
+{
+ Expression eaa = interpretRegion(aa, istate, CTFEGoal.LValue);
+ if (exceptionOrCantInterpret(eaa))
+ return eaa;
+ Expression ekey = interpretRegion(key, istate);
+ if (exceptionOrCantInterpret(ekey))
+ return ekey;
+ Expression efound = interpretRegion(found, istate, CTFEGoal.LValue);
+ if (exceptionOrCantInterpret(efound))
+ return efound;
+
+ auto ie = ctfeEmplaceExp!IndexExp(aa.loc, aa, key); // any BinExp for location in assignToLvalue
+ Expression evalaa = interpretRegion(eaa, istate);
+ auto aalit = evalaa.isAssocArrayLiteralExp();
+ if (!aalit)
+ {
+ auto keysx = new Expressions();
+ auto valuesx = new Expressions();
+ aalit = ctfeEmplaceExp!AssocArrayLiteralExp(aa.loc, keysx, valuesx);
+ aalit.type = aa.type;
+ aalit.ownedByCtfe = OwnedBy.ctfe;
+ Interpreter.assignToLvalue(ie, eaa, aalit, istate);
+ }
+ size_t idx;
+ auto result = findKeyInAA(aa.loc, aalit, ekey, &idx);
+ if (found)
+ Interpreter.assignToLvalue(ie, efound, IntegerExp.createBool(result !is null), istate);
+ if (!result)
+ {
+ aalit.keys.push(ekey);
+ result = copyLiteral(aa.type.nextOf().defaultInitLiteral(aa.loc)).copy();
+ idx = aalit.values.length;
+ aalit.values.push(result);
+ }
+ return pointerToAAValue(pue, aa, aalit, idx);
+}
+
+private Expression pointerToAAValue(UnionExp* pue, Expression aa, AssocArrayLiteralExp aalit, size_t idx)
+{
+ auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, aa.type.nextOf().arrayOf(), aalit.values);
+ arr.ownedByCtfe = aalit.ownedByCtfe;
+ arr.aaLiteral = aalit;
+ auto len = ctfeEmplaceExp!(IntegerExp)(aa.loc, idx, Type.tsize_t);
+ auto idxexp = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, len);
+ idxexp.type = arr.type.nextOf();
+ emplaceExp!(AddrExp)(pue, aa.loc, idxexp);
+ pue.exp().type = idxexp.type.pointerTo();
+ return pue.exp();
+}
+
// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expression aa, Expression deleg)
{
if (wantRefValue)
{
Type t = evalue.type;
- evalue = ctfeEmplaceExp!IndexExp(deleg.loc, ae, ekey);
+ auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, t.arrayOf(), ae.values);
+ arr.ownedByCtfe = ae.ownedByCtfe;
+ auto idx = ctfeEmplaceExp!(IntegerExp)(aa.loc, i, Type.tsize_t);
+ evalue = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, idx);
evalue.type = t;
}
args[numParams - 1] = evalue;
}
if (!pthis)
{
- if (nargs == 1 || nargs == 3)
+ if (nargs >= 1 && nargs <= 3)
{
Expression firstarg = (*arguments)[0];
if (auto firstAAtype = firstarg.type.toBasetype().isTypeAArray())
const id = fd.ident;
if (nargs == 1)
{
- if (id == Id.aaLen)
+ if (id == Id._d_aaLen)
return interpret_length(pue, istate, firstarg);
if (fd.toParent2().ident == Id.object)
return interpret_dup(pue, istate, firstarg);
}
}
+ else if (nargs == 2)
+ {
+ if (id == Id._d_aaGetRvalueX)
+ return interpret_aaGetRvalueX(pue, istate, firstarg, (*arguments)[1]);
+ if (id == Id._d_aaIn)
+ return interpret_aaIn(pue, istate, firstarg, (*arguments)[1]);
+ if (id == Id._d_aaDel)
+ return interpret_aaDel(pue, istate, firstarg, (*arguments)[1]);
+ if (id == Id._d_aaEqual)
+ return interpret_aaEqual(pue, istate, firstarg, (*arguments)[1]);
+ if (id == Id._d_aaApply)
+ return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]);
+ if (id == Id._d_aaApply2)
+ return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]);
+ }
else // (nargs == 3)
{
- if (id == Id._aaApply)
- return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
- if (id == Id._aaApply2)
- return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
+ if (id == Id._d_aaGetY)
+ return interpret_aaGetY(pue, istate, firstarg, (*arguments)[1], (*arguments)[2]);
}
}
}
import dmd.doc;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, importAll, load;
+import dmd.dsymbolsem : dsymbolSemantic;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
-import dmd.expressionsem;
import dmd.file_manager;
import dmd.func;
import dmd.globals;
-import dmd.gluelayer;
import dmd.id;
import dmd.identifier;
import dmd.location;
SearchOptFlags searchCacheFlags; // cached flags
bool insearch;
+ bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd?
+
/**
* A root module is one that will be compiled all the way to
* object code. This field holds the root module that caused
else if (!FileName.equalsExt(srcfilename, mars_ext) &&
!FileName.equalsExt(srcfilename, hdr_ext) &&
!FileName.equalsExt(srcfilename, c_ext) &&
+ !FileName.equalsExt(srcfilename, h_ext) &&
!FileName.equalsExt(srcfilename, i_ext) &&
!FileName.equalsExt(srcfilename, dd_ext))
{
if (doHdrGen)
hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext);
- this.edition = Edition.legacy;
+ this.edition = Edition.min;
}
extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
}
- static const(char)* find(const(char)* filename)
- {
- ImportPathInfo pathThatFoundThis; // is this needed? In fact is this function needed still???
- return find(filename.toDString, pathThatFoundThis).ptr;
- }
-
extern (D) static const(char)[] find(const(char)[] filename, out ImportPathInfo pathThatFoundThis)
{
ptrdiff_t whichPathFoundThis;
auto m = new Module(loc, filename, ident, 0, 0);
// TODO: apply import path information (pathInfo) on to module
+ m.isExplicitlyOutOfBinary = importPathThatFindUs.isOutOfBinary;
if (!m.read(loc))
return null;
const(ubyte)[] srctext;
if (global.preprocess &&
- FileName.equalsExt(srcfile.toString(), c_ext) &&
+ (FileName.equalsExt(srcfile.toString(), c_ext) ||
+ FileName.equalsExt(srcfile.toString(), h_ext)) &&
FileName.exists(srcfile.toString()))
{
/* Apply C preprocessor to the .c file, returning the contents
DsymbolTable dst;
Package ppack = null;
- /* If it has the extension ".c", it is a "C" file.
+ /* If it has the extension ".c" or ".h", it is a "C" file.
* If it has the extension ".i", it is a preprocessed "C" file.
*/
- if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext))
+ if (FileName.equalsExt(arg, c_ext) ||
+ FileName.equalsExt(arg, h_ext) ||
+ FileName.equalsExt(arg, i_ext))
{
filetype = FileType.c;
return needmoduleinfo || global.params.cov;
}
- /*******************************************
- * Print deprecation warning if we're deprecated, when
- * this module is imported from scope sc.
- *
- * Params:
- * sc = the scope into which we are imported
- * loc = the location of the import statement
- */
- void checkImportDeprecation(Loc loc, Scope* sc)
- {
- if (md && md.isdeprecated && !sc.isDeprecated)
- {
- Expression msg = md.msg;
- if (StringExp se = msg ? msg.toStringExp() : null)
- {
- const slice = se.peekString();
- if (slice.length)
- {
- deprecation(loc, "%s `%s` is deprecated - %.*s", kind, toPrettyChars, cast(int)slice.length, slice.ptr);
- return;
- }
- }
- deprecation(loc, "%s `%s` is deprecated", kind, toPrettyChars);
- }
- }
-
override bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all)
{
if (insearch) // don't follow import cycles
}
// Back end
- int doppelganger; // sub-module
- Symbol* cov; // private uint[] __coverage;
+ void* cov; // private uint[] __coverage;
uint[] covb; // bit array of valid code line numbers
- Symbol* sictor; // module order independent constructor
- Symbol* sctor; // module constructor
- Symbol* sdtor; // module destructor
- Symbol* ssharedctor; // module shared constructor
- Symbol* sshareddtor; // module shared destructor
- Symbol* stest; // module unit test
- Symbol* sfilename; // symbol for filename
+ void* sfilename; // symbol for filename
+ bool hasCDtor; // this module has a (shared) module constructor or destructor and we are codegenning it
uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
_escapetable = new Escape();
return _escapetable;
}
-
- /****************************
- * A Singleton that loads core.stdc.config
- * Returns:
- * Module of core.stdc.config, null if couldn't find it
- */
- extern (D) static Module loadCoreStdcConfig()
- {
- __gshared Module core_stdc_config;
- auto pkgids = new Identifier[2];
- pkgids[0] = Id.core;
- pkgids[1] = Id.stdc;
- return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
- }
-
- /****************************
- * A Singleton that loads core.atomic
- * Returns:
- * Module of core.atomic, null if couldn't find it
- */
- extern (D) static Module loadCoreAtomic()
- {
- __gshared Module core_atomic;
- auto pkgids = new Identifier[1];
- pkgids[0] = Id.core;
- return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
- }
-
- /****************************
- * A Singleton that loads std.math
- * Returns:
- * Module of std.math, null if couldn't find it
- */
- extern (D) static Module loadStdMath()
- {
- __gshared Module std_math;
- auto pkgids = new Identifier[1];
- pkgids[0] = Id.std;
- return loadModuleFromLibrary(std_math, pkgids, Id.math);
- }
-
- /**********************************
- * Load a Module from the library.
- * Params:
- * mod = cached return value of this call
- * pkgids = package identifiers
- * modid = module id
- * Returns:
- * Module loaded, null if cannot load it
- */
- extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
- {
- if (mod)
- return mod;
-
- auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
- // Module.load will call fatal() if there's no module available.
- // Gag the error here, pushing the error handling to the caller.
- const errors = global.startGagging();
- imp.load(null);
- if (imp.mod)
- {
- imp.mod.importAll(null);
- imp.mod.dsymbolSemantic(null);
- }
- global.endGagging(errors);
- mod = imp.mod;
- return mod;
- }
}
/***********************************************************
dbuf.writeUTF8(u);
}
else
- dbuf.writeByte(u);
+ dbuf.writeByte(cast(ubyte)u);
}
dbuf.writeByte(0); //add null terminator
return dbuf.extractSlice();
dbuf.writeUTF8(u);
}
else
- dbuf.writeByte(u);
+ dbuf.writeByte(cast(ubyte)u);
}
dbuf.writeByte(0); //add a terminating null byte
return dbuf.extractSlice();
{
Scope sc;
sc.eSink = global.errorSink;
- auto parameters = new Parameters();
Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
- parameters.push(p);
+ auto parameters = new Parameters(p);
Type tret = null;
TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
ensure = 3, // out contract
}
-private extern (D) struct BitFields
+/// Bitfield for settable/copyable flags, see `copyFlagsFrom`, `resetAllFlags`
+private extern (D) struct FlagBitFields
{
bool ctor; /// constructor type
bool noAccessCheck; /// don't do access checks
bool ctfeBlock; /// inside a `if (__ctfe)` block
}
+private extern (D) struct NonFlagBitFields
+{
+ ubyte intypeof; /// in typeof(exp)
+ bool nofree; /// true if shouldn't free it
+ bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
+ bool inDefaultArg; /// true if inside a default argument (where __FILE__, etc are evaluated at the call site)
+ bool explicitVisibility; /// set if in an explicit visibility attribute
+}
/// State of -preview switches
///
/// By making them part of a Scope, we reduce reliance on dmd.globals,
ForeachStatement fes; /// if nested function for ForeachStatement, this is it
Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
Dsymbol inunion; /// != null if processing members of a union
- bool nofree; /// true if shouldn't free it
- bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
- bool inDefaultArg; /// true if inside a default argument (where __FILE__, etc are evaluated at the call site)
- int intypeof; /// in typeof(exp)
VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init
ErrorSink eSink; /// sink for error messages
/// visibility for class members
Visibility visibility = Visibility(Visibility.Kind.public_);
- int explicitVisibility; /// set if in an explicit visibility attribute
STC stc; /// storage class
DeprecatedDeclaration depdecl; /// customized deprecation message
import dmd.common.bitfields : generateBitFields;
- mixin(generateBitFields!(BitFields, ushort));
-
+ mixin(generateBitFields!(FlagBitFields, ushort));
+ private ushort bitFields2;
+ mixin(generateBitFields!(NonFlagBitFields, ushort, "bitFields2"));
Previews previews;
// user defined attributes
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : search, setFieldOffset;
import dmd.dtemplate;
-import dmd.errors;
import dmd.expression;
import dmd.func;
-import dmd.funcsem;
-import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.opover;
import dmd.target;
import dmd.tokens;
-import dmd.typesem : isZeroInit, merge, size, hasPointers;
import dmd.typinf;
import dmd.visitor;
-/***************************************
- * Search sd for a member function of the form:
- * `extern (D) string toString();`
- * Params:
- * sd = struct declaration to search
- * Returns:
- * FuncDeclaration of `toString()` if found, `null` if not
- */
-FuncDeclaration search_toString(StructDeclaration sd)
-{
- Dsymbol s = search_function(sd, Id.tostring);
- FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
- if (fd)
- {
- __gshared TypeFunction tftostring;
- if (!tftostring)
- {
- tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
- tftostring = tftostring.merge().toTypeFunction();
- }
- fd = fd.overloadExactMatch(tftostring);
- }
- return fd;
-}
-
enum StructFlags : int
{
none = 0x0,
return "struct";
}
- /// Compute cached type properties for `TypeStruct`
- extern(D) final void determineTypeProperties()
- {
- if (computedTypeProperties)
- return;
- foreach (vd; fields)
- {
- import dmd.dsymbolsem : hasPointers;
- if (vd.storage_class & STC.ref_ || vd.hasPointers())
- {
- hasPointerField = true;
- hasUnsafeBitpatterns = true;
- }
-
- if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers())
- hasVoidInitPointers = true;
-
- if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns())
- hasUnsafeBitpatterns = true;
-
- if (!vd._init && vd.type.hasVoidInitPointers())
- hasVoidInitPointers = true;
-
- if (vd.type.hasInvariant())
- hasFieldWithInvariant = true;
- }
- computedTypeProperties = true;
- }
-
- /***************************************
- * Determine if struct is POD (Plain Old Data).
- *
- * POD is defined as:
- * $(OL
- * $(LI not nested)
- * $(LI no postblits, destructors, or assignment operators)
- * $(LI no `ref` fields or fields that are themselves non-POD)
- * )
- * The idea being these are compatible with C structs.
- *
- * Returns:
- * true if struct is POD
- */
- final bool isPOD()
- {
- // If we've already determined whether this struct is POD.
- if (ispod != ThreeState.none)
- return (ispod == ThreeState.yes);
-
- import dmd.clone;
-
- bool hasCpCtorLocal;
- bool hasMoveCtorLocal;
- bool needCopyCtor;
- bool needMoveCtor;
- needCopyOrMoveCtor(this, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
-
- if (enclosing || // is nested
- search(this, loc, Id.postblit) || // has postblit
- search(this, loc, Id.dtor) || // has destructor
- /* This is commented out because otherwise buildkite vibe.d:
- `canCAS!Task` fails to compile
- */
- //hasMoveCtorLocal || // has move constructor
- hasCpCtorLocal) // has copy constructor
- {
- ispod = ThreeState.no;
- return false;
- }
-
- // Recursively check all fields are POD.
- for (size_t i = 0; i < fields.length; i++)
- {
- VarDeclaration v = fields[i];
- if (v.storage_class & STC.ref_)
- {
- ispod = ThreeState.no;
- return false;
- }
-
- if (auto ts = v.type.baseElemOf().isTypeStruct())
- {
- if (!ts.sym.isPOD())
- {
- ispod = ThreeState.no;
- return false;
- }
- }
- }
-
- ispod = ThreeState.yes;
- return true;
- }
-
/***************************************
* Determine if struct has copy construction (copy constructor or postblit)
* Returns:
{
return index < numArgTypes() ? (*argTypes.arguments)[index].type : null;
}
-
-
- /***************************************
- * Verifies whether the struct declaration has a
- * constructor that is not a copy constructor.
- * Optionally, it can check whether the struct
- * declaration has a regular constructor, that
- * is not disabled.
- *
- * Params:
- * ignoreDisabled = true to ignore disabled constructors
- * Returns:
- * true, if the struct has a regular (optionally,
- * not disabled) constructor, false otherwise.
- */
- final bool hasRegularCtor(bool ignoreDisabled = false)
- {
- if (!ctor)
- return false;
-
- bool result;
- overloadApply(ctor, (Dsymbol s)
- {
- if (auto td = s.isTemplateDeclaration())
- {
- if (ignoreDisabled && td.onemember)
- {
- if (auto ctorDecl = td.onemember.isCtorDeclaration())
- {
- if (ctorDecl.storage_class & STC.disable)
- return 0;
- }
- }
- result = true;
- return 1;
- }
- if (auto ctorDecl = s.isCtorDeclaration())
- {
- if (!ctorDecl.isCpCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable)))
- {
- result = true;
- return 1;
- }
- }
- return 0;
- });
- return result;
- }
}
import dmd.attrib;
import dmd.astenums;
import dmd.ast_node;
-import dmd.gluelayer;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dversion;
import dmd.dscope;
import dmd.dstruct;
+import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
+import dmd.expressionsem : getDsymbol;
import dmd.func;
-import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;
}
}
-
-struct Ungag
-{
- uint oldgag;
-
- extern (D) this(uint old) nothrow @safe
- {
- this.oldgag = old;
- }
-
- extern (C++) ~this() nothrow
- {
- global.gag = oldgag;
- }
-}
-
struct Visibility
{
///
{
Identifier ident;
Dsymbol parent;
- Symbol* csym; // symbol for code generator
+ void* csym; // symbol for code generator
Scope* _scope; // !=null means context to use for semantic()
private DsymbolAttributes* atts; /// attached attribute declarations
const Loc loc; // where defined
static bool has2This(Dsymbol s)
{
if (auto f = s.isFuncDeclaration())
- return f.hasDualContext();
+ return f.hasDualContext;
if (auto ad = s.isAggregateDeclaration())
return ad.vthis2 !is null;
return false;
return parent.isSpeculative();
}
- final Ungag ungagSpeculative() const
- {
- const oldgag = global.gag;
- if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration())
- global.gag = 0;
- return Ungag(oldgag);
- }
-
// kludge for template.isSymbol()
override final DYNCAST dyncast() const
{
return "symbol";
}
- /*********************************
- * If this symbol is really an alias for another,
- * return that other.
- * If needed, semantic() is invoked due to resolve forward reference.
- */
- Dsymbol toAlias()
- {
- return this;
- }
-
- /*********************************
- * Resolve recursive tuple expansion in eponymous template.
- */
- Dsymbol toAlias2()
- {
- return toAlias();
- }
-
bool overloadInsert(Dsymbol s)
{
//printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
* Returns:
* SIZE_INVALID when the size cannot be determined
*/
- uinteger_t size(Loc loc)
+ ulong size(Loc loc)
{
.error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars());
return SIZE_INVALID;
class AggregateDeclaration;
class EnumDeclaration;
class ClassDeclaration;
+class StructDeclaration;
class InterfaceDeclaration;
class StructDeclaration;
class UnionDeclaration;
void dsymbolSemantic(Dsymbol *dsym, Scope *sc);
void semantic2(Dsymbol *dsym, Scope *sc);
void semantic3(Dsymbol *dsym, Scope* sc);
+ Dsymbol *toAlias(Dsymbol* s);
+ Dsymbol *toAlias2(Dsymbol* s);
// in iasm.d
void asmSemantic(CAsmDeclaration *ad, Scope *sc);
// in iasmgcc.d
void gccAsmSemantic(CAsmDeclaration *ad, Scope *sc);
+ bool isPOD(StructDeclaration *sd);
}
struct Visibility
virtual Identifier *getIdent();
virtual const char *toPrettyChars(bool QualifyTypes = false);
virtual const char *kind() const;
- virtual Dsymbol *toAlias(); // resolve real symbol
- virtual Dsymbol *toAlias2();
virtual bool overloadInsert(Dsymbol *s);
virtual uinteger_t size(Loc loc);
virtual bool isforwardRef();
return ad;
}
+
+/*********************************
+ * Resolve recursive tuple expansion in eponymous template.
+ */
+Dsymbol toAlias2(Dsymbol s)
+{
+ if (auto ad = s.isAliasDeclaration())
+ {
+ if (ad.inuse)
+ {
+ .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+ return ad;
+ }
+ ad.inuse = 1;
+ Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
+ ad.inuse = 0;
+ return ds;
+ }
+ if (auto td = s.isTupleDeclaration())
+ {
+ //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
+ for (size_t i = 0; i < td.objects.length; i++)
+ {
+ RootObject o = (*td.objects)[i];
+ if (Dsymbol ds = isDsymbol(o))
+ {
+ ds = ds.toAlias2();
+ (*td.objects)[i] = ds;
+ }
+ }
+ return td;
+ }
+ return toAlias(s);
+}
+
+private Dsymbol toAliasImpl(AliasDeclaration ad)
+{
+ static if (0)
+ printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
+ ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
+ assert(ad != ad.aliassym);
+ //static int count; if (++count == 10) *(char*)0=0;
+
+ Dsymbol err()
+ {
+ // Avoid breaking "recursive alias" state during errors gagged
+ if (global.gag)
+ return ad;
+ ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
+ ad.type = Type.terror;
+ return ad.aliassym;
+ }
+ // Reading the AliasDeclaration
+ if (!ad.ignoreRead)
+ ad.wasRead = true; // can never assign to this AliasDeclaration again
+
+ if (ad.inuse == 1 && ad.type && ad._scope)
+ {
+ ad.inuse = 2;
+ const olderrors = global.errors;
+ Dsymbol s = ad.type.toDsymbol(ad._scope);
+ //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
+ if (global.errors != olderrors)
+ return err();
+ if (s)
+ {
+ s = s.toAlias();
+ if (global.errors != olderrors)
+ return err();
+ ad.aliassym = s;
+ ad.inuse = 0;
+ }
+ else
+ {
+ Type t = ad.type.typeSemantic(ad.loc, ad._scope);
+ if (t.ty == Terror)
+ return err();
+ if (global.errors != olderrors)
+ return err();
+ //printf("t = %s\n", t.toChars());
+ ad.inuse = 0;
+ }
+ }
+ if (ad.inuse)
+ {
+ .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+ return err();
+ }
+
+ if (ad.semanticRun >= PASS.semanticdone)
+ {
+ // semantic is already done.
+
+ // Do not see aliassym !is null, because of lambda aliases.
+
+ // Do not see type.deco !is null, even so "alias T = const int;` needs
+ // semantic analysis to take the storage class `const` as type qualifier.
+ }
+ else
+ {
+ // stop AliasAssign tuple building
+ if (ad.aliassym)
+ {
+ if (auto td = ad.aliassym.isTupleDeclaration())
+ {
+ if (td.building)
+ {
+ td.building = false;
+ ad.semanticRun = PASS.semanticdone;
+ return td;
+ }
+ }
+ }
+ if (ad._import && ad._import._scope)
+ {
+ /* If this is an internal alias for selective/renamed import,
+ * load the module first.
+ */
+ ad._import.dsymbolSemantic(null);
+ }
+ if (ad._scope)
+ {
+ aliasSemantic(ad, ad._scope);
+ }
+ }
+
+ ad.inuse = 1;
+ Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
+ ad.inuse = 0;
+ return s;
+}
+
+/*********************************
+ * If this symbol is really an alias for another,
+ * return that other.
+ * If needed, semantic() is invoked due to resolve forward reference.
+ */
+Dsymbol toAlias(Dsymbol s)
+{
+ if (auto ad = s.isAliasDeclaration())
+ {
+ return ad.toAliasImpl();
+ }
+ if (auto imp = s.isImport())
+ {
+ if (imp.aliasId)
+ return imp.mod;
+ return imp;
+ }
+ if (auto vd = s.isVarDeclaration())
+ {
+ //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
+ if ((!vd.type || !vd.type.deco) && vd._scope)
+ dsymbolSemantic(vd, vd._scope);
+
+ assert(vd != vd.aliasTuple);
+ return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+ }
+ // resolve real symbol
+ if (auto ti = s.isTemplateInstance())
+ {
+ static if (LOG)
+ {
+ printf("TemplateInstance.toAlias()\n");
+ }
+ if (!ti.inst)
+ {
+ // Maybe we can resolve it
+ if (ti._scope)
+ {
+ dsymbolSemantic(ti, ti._scope);
+ }
+ if (!ti.inst)
+ {
+ .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
+ ti.errors = true;
+ return ti;
+ }
+ }
+
+ if (ti.inst != ti)
+ return ti.inst.toAlias();
+
+ if (ti.aliasdecl)
+ {
+ return ti.aliasdecl.toAlias();
+ }
+
+ return ti.inst;
+ }
+ return s;
+}
+
const(char)* getMessage(DeprecatedDeclaration dd)
{
if (auto sc = dd._scope)
return true;
}
+/***************************************
+ * Determine if struct is POD (Plain Old Data).
+ *
+ * POD is defined as:
+ * $(OL
+ * $(LI not nested)
+ * $(LI no postblits, destructors, or assignment operators)
+ * $(LI no `ref` fields or fields that are themselves non-POD)
+ * )
+ * The idea being these are compatible with C structs.
+ *
+ * Returns:
+ * true if struct is POD
+ */
+bool isPOD(StructDeclaration sd)
+{
+ // If we've already determined whether this struct is POD.
+ if (sd.ispod != ThreeState.none)
+ return (sd.ispod == ThreeState.yes);
+
+ import dmd.clone;
+
+ bool hasCpCtorLocal;
+ bool hasMoveCtorLocal;
+ bool needCopyCtor;
+ bool needMoveCtor;
+ needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+
+ if (sd.enclosing || // is nested
+ search(sd, sd.loc, Id.postblit) || // has postblit
+ search(sd, sd.loc, Id.dtor) || // has destructor
+ /* This is commented out because otherwise buildkite vibe.d:
+ `canCAS!Task` fails to compile
+ */
+ //hasMoveCtorLocal || // has move constructor
+ hasCpCtorLocal) // has copy constructor
+ {
+ sd.ispod = ThreeState.no;
+ return false;
+ }
+
+ // Recursively check all fields are POD.
+ for (size_t i = 0; i < sd.fields.length; i++)
+ {
+ VarDeclaration v = sd.fields[i];
+ if (v.storage_class & STC.ref_)
+ {
+ sd.ispod = ThreeState.no;
+ return false;
+ }
+
+ if (auto ts = v.type.baseElemOf().isTypeStruct())
+ {
+ if (!ts.sym.isPOD())
+ {
+ sd.ispod = ThreeState.no;
+ return false;
+ }
+ }
+ }
+
+ sd.ispod = ThreeState.yes;
+ return true;
+}
+
/*
If sd has a copy constructor and ctor is an rvalue constructor,
issue an error.
* so resolve it ahead.
*/
{
- int save = sc.intypeof;
+ ubyte save = sc.intypeof;
sc.intypeof = 1; // bypass "need this" error check
e = resolveProperties(sc, e);
sc.intypeof = save;
Module.addDeferredSemantic(s);
}
+struct Ungag
+{
+ uint oldgag;
+
+ extern (D) this(uint old) nothrow @safe
+ {
+ this.oldgag = old;
+ }
+
+ extern (C++) ~this() nothrow
+ {
+ global.gag = oldgag;
+ }
+}
+
+Ungag ungagSpeculative(const Dsymbol s)
+{
+ const oldgag = global.gag;
+ if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
+ global.gag = 0;
+ return Ungag(oldgag);
+}
+
+/*******************************************
+ * Print deprecation warning if we're deprecated, when
+ * this module is imported from scope sc.
+ *
+ * Params:
+ * m = the module
+ * sc = the scope into which we are imported
+ * loc = the location of the import statement
+ */
+private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+{
+ if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+ return;
+
+ Expression msg = m.md.msg;
+ if (StringExp se = msg ? msg.toStringExp() : null)
+ {
+ const slice = se.peekString();
+ if (slice.length)
+ {
+ deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
+ return;
+ }
+ }
+ deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+}
private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
ie = ie.expressionSemantic(sc);
if (nelems > 0 && ie)
{
- auto iexps = new Expressions();
- iexps.push(ie);
+ auto iexps = new Expressions(ie);
auto exps = new Expressions();
for (size_t pos = 0; pos < iexps.length; pos++)
{
exp = ErrorExp.get();
}
}
+ else if (exp.isBitField())
+ {
+ if (dsym.storage_class & STC.autoref)
+ {
+ dsym.storage_class &= ~STC.ref_;
+ constructInit(false);
+ }
+ else
+ {
+ .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ }
else
{
constructInit(false);
if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
{
- .error(dsym.loc, "%s `%s` - bit-field must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
+ .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
}
sc = sc.startCTFE();
if (!dsym.type.isIntegral())
{
// C11 6.7.2.1-5
- error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars());
+ error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
dsym.errors = true;
}
if (!width.isIntegerExp())
{
- error(width.loc, "bit-field width `%s` is not an integer constant", dsym.width.toChars());
+ error(width.loc, "bitfield width `%s` is not an integer constant", dsym.width.toChars());
dsym.errors = true;
}
const uwidth = width.toInteger(); // uwidth is unsigned
if (uwidth == 0 && !dsym.isAnonymous())
{
- error(width.loc, "bit-field `%s` has zero width", dsym.toChars());
+ error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
dsym.errors = true;
}
const sz = dsym.type.size();
const max_width = sz * 8;
if (uwidth > max_width)
{
- error(width.loc, "width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
+ error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
dsym.errors = true;
}
dsym.fieldWidth = cast(uint)uwidth;
/* Run semantic on each argument, place results in tiargs[],
* then find best match template with tiargs
*/
- if (!tm.findTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+ if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
{
if (tm.semanticRun == PASS.initial) // forward reference had occurred
{
auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
- auto sa = new Statements();
Statement s = new ExpStatement(Loc.initial, v);
- sa.push(s);
+ auto sa = new Statements(s);
Expression e;
if (isShared)
}
//
- // For two structures or unions, corresponding bit-fields shall have the same widths.
+ // For two structures or unions, corresponding bitfields shall have the same widths.
//
BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
}
}
+/****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+private void declareParameters(TemplateInstance ti, Scope* sc)
+{
+ TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ //printf("TemplateInstance.declareParameters()\n");
+ foreach (i, o; ti.tdtypes) // initializer for tp
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ //printf("\ttdtypes[%d] = %p\n", i, o);
+ declareParameter(tempdecl, sc, tp, o);
+ }
+}
private extern(C++) class AddMemberVisitor : Visitor
{
alias visit = Visitor.visit;
id == Id._d_arrayappendcTX;
}
+/*****************************************
+ * Append `ti` to the specific module `ti.members[]`
+ */
+private Dsymbols* appendToModuleMember(TemplateInstance ti)
+{
+ Module mi = ti.minst; // instantiated . inserted module
+
+ //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
+ // toPrettyChars(),
+ // enclosing ? enclosing.toPrettyChars() : null,
+ // mi ? mi.toPrettyChars() : null);
+ if (global.params.allInst || !mi || mi.isRoot())
+ {
+ /* If the instantiated module is speculative or root, insert to the
+ * member of a root module. Then:
+ * - semantic3 pass will get called on the instance members.
+ * - codegen pass will get a selection chance to do/skip it (needsCodegen()).
+ */
+ static Dsymbol getStrictEnclosing(TemplateInstance ti)
+ {
+ do
+ {
+ if (ti.enclosing)
+ return ti.enclosing;
+ ti = ti.tempdecl.isInstantiated();
+ } while (ti);
+ return null;
+ }
+
+ Dsymbol enc = getStrictEnclosing(ti);
+ // insert target is made stable by using the module
+ // where tempdecl is declared.
+ mi = (enc ? enc : ti.tempdecl).getModule();
+ if (!mi.isRoot())
+ {
+ if (mi.importedFrom)
+ {
+ mi = mi.importedFrom;
+ assert(mi.isRoot());
+ }
+ else
+ {
+ // This can happen when using the frontend as a library.
+ // Append it to the non-root module.
+ }
+ }
+ }
+ else
+ {
+ /* If the instantiated module is non-root, insert to the member of the
+ * non-root module. Then:
+ * - semantic3 pass won't be called on the instance.
+ * - codegen pass won't reach to the instance.
+ * Unless it is re-appended to a root module later (with changed minst).
+ */
+ }
+ //printf("\t-. mi = %s\n", mi.toPrettyChars());
+
+ if (ti.memberOf) // already appended to some module
+ {
+ assert(mi.isRoot(), "can only re-append to a root module");
+ if (ti.memberOf.isRoot())
+ return null; // no need to move to another root module
+ }
+
+ Dsymbols* a = mi.members;
+ a.push(ti);
+ ti.memberOf = mi;
+ if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+ Module.addDeferredSemantic2(ti);
+ if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+ Module.addDeferredSemantic3(ti);
+ return a;
+}
+
+private void expandMembers(TemplateInstance ti,Scope* sc2)
+{
+ ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+ ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+ if (!ti.aliasdecl)
+ {
+ /* static if's are crucial to evaluating aliasdecl correctly. But
+ * evaluating the if/else bodies may require aliasdecl.
+ * So, evaluate the condition for static if's, but not their if/else bodies.
+ * Then try to set aliasdecl.
+ * Later do the if/else bodies.
+ * https://issues.dlang.org/show_bug.cgi?id=23598
+ * It might be better to do this by attaching a lambda to the StaticIfDeclaration
+ * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
+ */
+ bool done;
+ void staticIfDg(Dsymbol s)
+ {
+ if (done || ti.aliasdecl)
+ return;
+ //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars());
+ if (!s.isStaticIfDeclaration())
+ {
+ //s.dsymbolSemantic(sc2);
+ done = true;
+ return;
+ }
+ auto sid = s.isStaticIfDeclaration();
+ sid.include(sc2);
+ if (ti.members.length)
+ {
+ Dsymbol sa;
+ if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
+ ti.aliasdecl = sa;
+ }
+ done = true;
+ }
+
+ ti.members.foreachDsymbol(&staticIfDg);
+ }
+
+ void symbolDg(Dsymbol s)
+ {
+ //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
+ //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
+ //if (enclosing)
+ // s.parent = sc.parent;
+ //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ s.dsymbolSemantic(sc2);
+ //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ Module.runDeferredSemantic();
+ }
+
+ ti.members.foreachDsymbol(&symbolDg);
+}
+
+private void tryExpandMembers(TemplateInstance ti, Scope* sc2)
+{
+ __gshared int nest;
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+ fatal();
+ }
+
+ ti.expandMembers(sc2);
+
+ nest--;
+}
+
+private void trySemantic3(TemplateInstance ti, Scope* sc2)
+{
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ __gshared int nest;
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+ fatal();
+ }
+
+ semantic3(ti, sc2);
+
+ --nest;
+}
+
void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
{
//printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
if (STC stc = ModToStc(t.mod))
{
//printf("t = %s, stc = x%llx\n", t.toChars(), stc);
- auto s = new Dsymbols();
- s.push(new StorageClassDeclaration(stc, tempinst.members));
+ auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members));
tempinst.members = s;
}
break;
normalRet();
}
+/**********************************************
+ * Find template declaration corresponding to template instance.
+ *
+ * Returns:
+ * false if finding fails.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * any members of this object won't be modified, and repetition call will
+ * reproduce same error.
+ */
+bool findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
+{
+ if (pwithsym)
+ *pwithsym = null;
+
+ if (ti.havetempdecl)
+ return true;
+
+ //printf("TemplateInstance.findTempDecl() %s\n", toChars());
+ if (!ti.tempdecl)
+ {
+ /* Given:
+ * foo!( ... )
+ * figure out which TemplateDeclaration foo refers to.
+ */
+ Identifier id = ti.name;
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(ti.loc, id, scopesym);
+ if (!s)
+ {
+ s = sc.search_correct(id);
+ if (s)
+ .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
+ else
+ .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), id.toChars());
+ return false;
+ }
+ static if (LOG)
+ {
+ printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
+ if (s.parent)
+ printf("s.parent = '%s'\n", s.parent.toChars());
+ }
+ if (pwithsym)
+ *pwithsym = scopesym.isWithScopeSymbol();
+
+ /* We might have found an alias within a template when
+ * we really want the template.
+ */
+ TemplateInstance ti2;
+ if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
+ {
+ if (ti2.tempdecl && ti2.tempdecl.ident == id)
+ {
+ /* This is so that one can refer to the enclosing
+ * template, even if it has the same name as a member
+ * of the template, if it has a !(arguments)
+ */
+ TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration();
+ assert(td);
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ s = td;
+ }
+ }
+
+ // The template might originate from a selective import which implies that
+ // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
+ // This is the last place where we see the deprecated alias because it is
+ // stripped below, so check if the selective import was deprecated.
+ // See https://issues.dlang.org/show_bug.cgi?id=20840.
+ if (s.isAliasDeclaration())
+ s.checkDeprecated(ti.loc, sc);
+
+ if (!ti.updateTempDecl(sc, s))
+ {
+ return false;
+ }
+ }
+ assert(ti.tempdecl);
+
+ // Look for forward references
+ auto tovers = ti.tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.initial)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ auto ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.initial)
+ {
+ .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`",
+ ti.kind, ti.toPrettyChars(), ti.toChars(), td.toChars());
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+}
+
+private bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
+{
+ // Follow qualifications to find the TemplateDeclaration
+ if (!tm.tempdecl)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ tm.tqual.resolve(tm.loc, sc, e, t, s);
+ if (!s)
+ {
+ .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
+ return false;
+ }
+ s = s.toAlias();
+ tm.tempdecl = s.isTemplateDeclaration();
+ OverloadSet os = s.isOverloadSet();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (os)
+ {
+ Dsymbol ds = null;
+ foreach (i, sym; os.a)
+ {
+ Dsymbol s2 = sym.isTemplateDeclaration();
+ if (s2)
+ {
+ if (ds)
+ {
+ tm.tempdecl = os;
+ break;
+ }
+ ds = s2;
+ }
+ }
+ }
+ if (!tm.tempdecl)
+ {
+ .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
+ tm.toPrettyChars, s.toChars(), s.kind());
+ return false;
+ }
+ }
+ assert(tm.tempdecl);
+
+ // Look for forward references
+ auto tovers = tm.tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.initial)
+ {
+ if (td._scope)
+ td.dsymbolSemantic(td._scope);
+ else
+ {
+ tm.semanticRun = PASS.initial;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+}
+
/********************
* Perform semantic on AliasAssignment.
* Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
if (td)
aliassym.aliassym = td;
aliassym.semanticRun = PASS.semanticdone;
- if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
+ if (!TemplateInstance_semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
{
tempinst.errors = true;
return null;
return td;
}
+/*********************************
+ * Iterate this dsymbol or members of this scoped dsymbol, then
+ * call `fp` with the found symbol and `params`.
+ * Params:
+ * symbol = the dsymbol or parent of members to call fp on
+ * fp = function pointer to process the iterated symbol.
+ * If it returns nonzero, the iteration will be aborted.
+ * ctx = context parameter passed to fp.
+ * Returns:
+ * nonzero if the iteration is aborted by the return value of fp,
+ * or 0 if it's completed.
+ */
+int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx)
+{
+ if (auto nd = symbol.isNspace())
+ {
+ return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+ }
+ if (auto ad = symbol.isAttribDeclaration())
+ {
+ return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+ }
+ if (auto tm = symbol.isTemplateMixin())
+ {
+ if (tm._scope) // if fwd reference
+ dsymbolSemantic(tm, null); // try to resolve it
+
+ return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+ }
+
+ return fp(symbol, ctx);
+}
+
/***************************************
* 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()
return true;
}
+/****************************
+ * A Singleton that loads core.stdc.config
+ * Returns:
+ * Module of core.stdc.config, null if couldn't find it
+ */
+Module loadCoreStdcConfig()
+{
+ __gshared Module core_stdc_config;
+ auto pkgids = new Identifier[2];
+ pkgids[0] = Id.core;
+ pkgids[1] = Id.stdc;
+ return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
+}
+
+/****************************
+ * A Singleton that loads core.atomic
+ * Returns:
+ * Module of core.atomic, null if couldn't find it
+ */
+private Module loadCoreAtomic()
+{
+ __gshared Module core_atomic;
+ auto pkgids = new Identifier[1];
+ pkgids[0] = Id.core;
+ return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
+}
+
+/****************************
+ * A Singleton that loads std.math
+ * Returns:
+ * Module of std.math, null if couldn't find it
+ */
+Module loadStdMath()
+{
+ __gshared Module std_math;
+ auto pkgids = new Identifier[1];
+ pkgids[0] = Id.std;
+ return loadModuleFromLibrary(std_math, pkgids, Id.math);
+}
+
+/**********************************
+ * Load a Module from the library.
+ * Params:
+ * mod = cached return value of this call
+ * pkgids = package identifiers
+ * modid = module id
+ * Returns:
+ * Module loaded, null if cannot load it
+ */
+extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
+{
+ if (mod)
+ return mod;
+
+ auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
+ // Module.load will call fatal() if there's no module available.
+ // Gag the error here, pushing the error handling to the caller.
+ const errors = global.startGagging();
+ imp.load(null);
+ if (imp.mod)
+ {
+ imp.mod.importAll(null);
+ imp.mod.dsymbolSemantic(null);
+ }
+ global.endGagging(errors);
+ mod = imp.mod;
+ return mod;
+}
+
/// Do an atomic operation (currently tailored to [shared] static ctors|dtors) needs
private CallExp doAtomicOp (string op, Identifier var, Expression arg)
{
assert(op == "-=" || op == "+=");
- Module mod = Module.loadCoreAtomic();
+ Module mod = loadCoreAtomic();
if (!mod)
return null; // core.atomic couldn't be loaded
{
//printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags);
if (ident != Id.dollar)
- return setResult(null);
+ return visit(cast(ScopeDsymbol)ass);
VarDeclaration* pvar;
Expression ce;
{
assert(0);
}
- auto tiargs = new Objects();
Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t);
edim = edim.expressionSemantic(ass._scope);
- tiargs.push(edim);
+ auto tiargs = new Objects(edim);
e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
}
else
const style = target.c.bitFieldStyle;
if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang)
- assert(0, "unsupported bit-field style");
+ assert(0, "unsupported bitfield style");
const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS;
const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd);
}
else if (!isMicrosoftStyle)
{
- // If the bit-field spans more units of alignment than its type
+ // If the bitfield spans more units of alignment than its type
// and is at the alignment boundary, start a new field at the
// next alignment boundary. This affects when offsetof reports
// a higher number and bitoffsetof starts at zero again.
isunion);
// Add to the anon fields the base offset of this anonymous aggregate
- //printf("anon fields, anonoffset = %d\n", anonoffset);
- foreach (const i; fieldstart .. ad.fields.length)
+ //printf("anon fields, anonoffset = %d\n", anond.anonoffset);
+ if (anond.anonoffset)
+ anond.decl.foreachDsymbol( (s) => s.adjustBaseOffset(anond.anonoffset) );
+ }
+ }
+}
+
+// Adds `offset` as the new base offset of all field members in `d`.
+private void adjustBaseOffset(Dsymbol d, uint offset)
+{
+ switch (d.dsym)
+ {
+ case DSYM.nspace:
+ auto ns = cast(Nspace)d;
+ ns.members.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+ break;
+
+ case DSYM.templateMixin:
+ auto tm = cast(TemplateMixin)d;
+ tm.members.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+ break;
+
+ case DSYM.anonDeclaration:
+ auto ad = cast(AnonDeclaration)d;
+ if (ad.decl)
+ ad.decl.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+ break;
+
+ case DSYM.bitFieldDeclaration:
+ auto bfd = cast(BitFieldDeclaration)d;
+ bfd.offset += offset;
+ //printf("\t%s %d : %d\n", bfd.toChars(), bfd.offset, bfd.bitOffset);
+ break;
+
+ default:
+ if (auto vd = d.isVarDeclaration())
{
- VarDeclaration v = ad.fields[i];
- //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset);
- v.offset += anond.anonoffset;
+ if (vd.aliasTuple)
+ vd.aliasTuple.foreachVar( s => s.adjustBaseOffset(offset) );
+ else if (vd.isField())
+ {
+ vd.offset += offset;
+ //printf("\t%s %d\n", vd.toChars(), vd.offset);
+ }
}
- }
+ else if (auto atd = d.isAttribDeclaration())
+ {
+ atd.include(null).foreachDsymbol( s => s.adjustBaseOffset(offset) );
+ }
+ break;
}
}
const sz = vd.type.size();
assert(sz != SIZE_INVALID);
- if (!sz)
- return null;
if (vd.type.toBasetype().ty == Tstruct)
{
}
}
+/***************************************
+ * Calculate `ad.field[i].overlapped` and `overlapUnsafe`, and check that all of explicit
+ * field initializers have unique memory space on instance.
+ * Returns:
+ * true if any errors happen.
+ */
+private bool checkOverlappedFields(AggregateDeclaration ad)
+{
+ //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
+ assert(ad.sizeok == Sizeok.done);
+ size_t nfields = ad.fields.length;
+ if (ad.isNested())
+ {
+ auto cd = ad.isClassDeclaration();
+ if (!cd || !cd.baseClass || !cd.baseClass.isNested())
+ nfields--;
+ if (ad.vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
+ nfields--;
+ }
+ bool errors = false;
+
+ // Fill in missing any elements with default initializers
+ foreach (i; 0 .. nfields)
+ {
+ auto vd = ad.fields[i];
+ if (vd.errors)
+ {
+ errors = true;
+ continue;
+ }
+
+ const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
+
+ // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
+ foreach (j; 0 .. nfields)
+ {
+ if (i == j)
+ continue;
+ auto v2 = ad.fields[j];
+ if (v2.errors)
+ {
+ errors = true;
+ continue;
+ }
+ if (!vd.isOverlappedWith(v2))
+ continue;
+
+ // vd and v2 are overlapping.
+ vd.overlapped = true;
+ v2.overlapped = true;
+
+ if (!MODimplicitConv(vd.type.mod, v2.type.mod))
+ v2.overlapUnsafe = true;
+ if (!MODimplicitConv(v2.type.mod, vd.type.mod))
+ vd.overlapUnsafe = true;
+
+ if (i > j)
+ continue;
+
+ if (!v2._init)
+ continue;
+
+ if (v2._init.isVoidInitializer())
+ continue;
+
+ if (vd._init && !vdIsVoidInit && v2._init)
+ {
+ .error(ad.loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ else if (v2._init && i < j)
+ {
+ .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
+ v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars());
+ errors = true;
+ }
+ }
+ }
+ return errors;
+}
+
private extern(C++) class FinalizeSizeVisitor : Visitor
{
alias visit = Visitor.visit;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include, hasStaticCtorOrDtor, oneMembers;
+import dmd.dsymbolsem : aliasSemantic, oneMembers, toAlias;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
-import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties, checkValue;
import dmd.func;
-import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.root.array;
import dmd.common.outbuffer;
import dmd.rootobject;
-import dmd.semantic3 : semantic3;
-import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter;
+import dmd.templatesem : getExpression, TemplateInstance_semanticTiargs;
import dmd.tokens;
-import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol,
- addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf,
- unqualify, aliasthisOf, castMod, substWildTo, addMod;
+import dmd.typesem : typeSemantic, isBaseOf, resolveNamedArgs;
import dmd.visitor;
-import dmd.templateparamsem;
-
//debug = FindExistingInstance; // print debug stats of findExistingInstance
private enum LOG = false;
}
-/***********************************
- * If oarg represents a Dsymbol, return that Dsymbol
- * Params:
- * oarg = argument to check
- * Returns:
- * Dsymbol if a symbol, null if not
- */
-Dsymbol getDsymbol(RootObject oarg)
-{
- //printf("getDsymbol()\n");
- //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
- if (auto ea = isExpression(oarg))
- {
- // Try to convert Expression to symbol
- if (auto ve = ea.isVarExp())
- return ve.var;
- if (auto fe = ea.isFuncExp())
- return fe.td ? fe.td : fe.fd;
- if (auto te = ea.isTemplateExp())
- return te.td;
- if (auto te = ea.isScopeExp())
- return te.sds;
- return null;
- }
- // Try to convert Type to symbol
- if (auto ta = isType(oarg))
- return ta.toDsymbol(null);
- return isDsymbol(oarg); // if already a symbol
-}
-
-
-private Expression getValue(ref Dsymbol s)
-{
- if (s)
- {
- if (VarDeclaration v = s.isVarDeclaration())
- {
- if (v.storage_class & STC.manifest)
- return v.getConstInitializer();
- }
- }
- return null;
-}
-
-/***********************
- * Try to get value from manifest constant
- */
-private Expression getValue(Expression e)
-{
- if (!e)
- return null;
- if (auto ve = e.isVarExp())
- {
- if (auto v = ve.var.isVarDeclaration())
- {
- if (v.storage_class & STC.manifest)
- {
- e = v.getConstInitializer();
- }
- }
- }
- return e;
-}
-
-private Expression getExpression(RootObject o)
-{
- auto s = isDsymbol(o);
- return s ? .getValue(s) : .getValue(isExpression(o));
-}
-
/******************************
* See if two objects match
* Params:
* o2 = second object
* Returns: true if they match
*/
-private bool match(RootObject o1, RootObject o2)
+bool match(RootObject o1, RootObject o2)
{
enum log = false;
return visibility;
}
- /****************************
- * Destructively get the error message from the last constraint evaluation
- * Params:
- * tip = tip to show after printing all overloads
- */
- const(char)* getConstraintEvalError(ref const(char)* tip)
- {
- import dmd.staticcond;
-
- // there will be a full tree view in verbose mode, and more compact list in the usual
- const full = global.params.v.verbose;
- uint count;
- const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count);
- scope (exit)
- {
- lastConstraint = null;
- lastConstraintTiargs = null;
- lastConstraintNegs.setDim(0);
- }
- if (!msg)
- return null;
-
- OutBuffer buf;
-
- assert(parameters && lastConstraintTiargs);
- if (parameters.length > 0)
- {
- formatParamsWithTiargs(*parameters, *lastConstraintTiargs, isVariadic() !is null, buf);
- buf.writenl();
- }
- if (!full)
- {
- // choosing singular/plural
- const s = (count == 1) ?
- " must satisfy the following constraint:" :
- " must satisfy one of the following constraints:";
- buf.writestring(s);
- buf.writenl();
- // the constraints
- buf.writeByte('`');
- buf.writestring(msg);
- buf.writeByte('`');
- }
- else
- {
- buf.writestring(" whose parameters have the following constraints:");
- buf.writenl();
- const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
- buf.writestring(sep);
- buf.writenl();
- // the constraints
- buf.writeByte('`');
- buf.writestring(msg);
- buf.writeByte('`');
- buf.writestring(sep);
- tip = "not satisfied constraints are marked with `>`";
- }
- return buf.extractChars();
- }
-
debug (FindExistingInstance)
{
__gshared uint nFound, nNotFound, nAdded, nRemoved;
argexps.push(e);
tparams.push(tparam);
}
-
- MATCH matchAll(Type tt)
- {
- MATCH match = MATCH.exact;
- foreach (j, e; argexps)
- {
- assert(e);
- if (e == emptyArrayElement)
- continue;
-
- Type t = tt.addMod(tparams[j].mod).substWildTo(MODFlags.const_);
-
- MATCH m = e.implicitConvTo(t);
- if (match > m)
- match = m;
- if (match == MATCH.nomatch)
- break;
- }
- return match;
- }
}
-
/* ======================== Type ============================================ */
-/****
- * Given an identifier, figure out which TemplateParameter it is.
- * Return IDX_NOTFOUND if not found.
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ * iStart = Start index of tparams to limit the tested parameters. If it's
+ * nonzero, tparams[0..iStart] will be excluded from the test target.
*/
-private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
{
- for (size_t i = 0; i < parameters.length; i++)
- {
- TemplateParameter tp = (*parameters)[i];
- if (tp.ident.equals(id))
- return i;
- }
- return IDX_NOTFOUND;
+ return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
}
-size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ */
+bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
{
- if (TypeIdentifier tident = tparam.isTypeIdentifier())
+ bool visitVector(TypeVector t)
{
- //printf("\ttident = '%s'\n", tident.toChars());
- return templateIdentifierLookup(tident.ident, parameters);
+ return t.basetype.reliesOnTemplateParameters(tparams);
}
- return IDX_NOTFOUND;
-}
-
-private auto X(T, U)(T m, U n)
-{
- return (m << 4) | n;
-}
-
-ubyte deduceWildHelper(Type t, Type* at, Type tparam)
-{
- if ((tparam.mod & MODFlags.wild) == 0)
- return 0;
- *at = null;
+ bool visitAArray(TypeAArray t)
+ {
+ return t.next.reliesOnTemplateParameters(tparams) ||
+ t.index.reliesOnTemplateParameters(tparams);
+ }
- switch (X(tparam.mod, t.mod))
+ bool visitFunction(TypeFunction t)
{
- case X(MODFlags.wild, 0):
- case X(MODFlags.wild, MODFlags.const_):
- case X(MODFlags.wild, MODFlags.shared_):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.wild, MODFlags.immutable_):
- case X(MODFlags.wildconst, 0):
- case X(MODFlags.wildconst, MODFlags.const_):
- case X(MODFlags.wildconst, MODFlags.shared_):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.wildconst, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
- {
- ubyte wm = (t.mod & ~MODFlags.shared_);
- if (wm == 0)
- wm = MODFlags.mutable;
- ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_);
- *at = t.unqualify(m);
- return wm;
- }
- case X(MODFlags.wild, MODFlags.wild):
- case X(MODFlags.wild, MODFlags.wildconst):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.wildconst, MODFlags.wild):
- case X(MODFlags.wildconst, MODFlags.wildconst):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ foreach (i, fparam; t.parameterList)
{
- *at = t.unqualify(tparam.mod & t.mod);
- return MODFlags.wild;
+ if (fparam.type.reliesOnTemplateParameters(tparams))
+ return true;
}
- default:
- return 0;
+ return t.next.reliesOnTemplateParameters(tparams);
}
-}
-
-/**
- * Returns the common type of the 2 types.
- */
-private Type rawTypeMerge(Type t1, Type t2)
-{
- if (t1.equals(t2))
- return t1;
- if (t1.equivalent(t2))
- return t1.castMod(MODmerge(t1.mod, t2.mod));
-
- auto t1b = t1.toBasetype();
- auto t2b = t2.toBasetype();
- if (t1b.equals(t2b))
- return t1b;
- if (t1b.equivalent(t2b))
- return t1b.castMod(MODmerge(t1b.mod, t2b.mod));
-
- auto ty = implicitConvCommonTy(t1b.ty, t2b.ty);
- if (ty != Terror)
- return Type.basic[ty];
-
- return null;
-}
-MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
-{
- // 9*9 == 81 cases
- switch (X(tparam.mod, t.mod))
+ bool visitIdentifier(TypeIdentifier t)
{
- case X(0, 0):
- case X(0, MODFlags.const_):
- case X(0, MODFlags.wild):
- case X(0, MODFlags.wildconst):
- case X(0, MODFlags.shared_):
- case X(0, MODFlags.shared_ | MODFlags.const_):
- case X(0, MODFlags.shared_ | MODFlags.wild):
- case X(0, MODFlags.shared_ | MODFlags.wildconst):
- case X(0, MODFlags.immutable_):
- // foo(U) T => T
- // foo(U) const(T) => const(T)
- // foo(U) inout(T) => inout(T)
- // foo(U) inout(const(T)) => inout(const(T))
- // foo(U) shared(T) => shared(T)
- // foo(U) shared(const(T)) => shared(const(T))
- // foo(U) shared(inout(T)) => shared(inout(T))
- // foo(U) shared(inout(const(T))) => shared(inout(const(T)))
- // foo(U) immutable(T) => immutable(T)
- {
- at = t;
- return MATCH.exact;
- }
- case X(MODFlags.const_, MODFlags.const_):
- case X(MODFlags.wild, MODFlags.wild):
- case X(MODFlags.wildconst, MODFlags.wildconst):
- case X(MODFlags.shared_, MODFlags.shared_):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.immutable_, MODFlags.immutable_):
- // foo(const(U)) const(T) => T
- // foo(inout(U)) inout(T) => T
- // foo(inout(const(U))) inout(const(T)) => T
- // foo(shared(U)) shared(T) => T
- // foo(shared(const(U))) shared(const(T)) => T
- // foo(shared(inout(U))) shared(inout(T)) => T
- // foo(shared(inout(const(U)))) shared(inout(const(T))) => T
- // foo(immutable(U)) immutable(T) => T
- {
- at = t.mutableOf().unSharedOf();
- return MATCH.exact;
- }
- case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
- // foo(const(U)) shared(const(T)) => shared(T)
- // foo(inout(U)) shared(inout(T)) => shared(T)
- // foo(inout(const(U))) shared(inout(const(T))) => shared(T)
- {
- at = t.mutableOf();
- return MATCH.exact;
- }
- case X(MODFlags.const_, 0):
- case X(MODFlags.const_, MODFlags.wild):
- case X(MODFlags.const_, MODFlags.wildconst):
- case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.const_, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_):
- // foo(const(U)) T => T
- // foo(const(U)) inout(T) => T
- // foo(const(U)) inout(const(T)) => T
- // foo(const(U)) shared(inout(T)) => shared(T)
- // foo(const(U)) shared(inout(const(T))) => shared(T)
- // foo(const(U)) immutable(T) => T
- // foo(shared(const(U))) immutable(T) => T
- {
- at = t.mutableOf();
- return MATCH.constant;
- }
- case X(MODFlags.const_, MODFlags.shared_):
- // foo(const(U)) shared(T) => shared(T)
- {
- at = t;
- return MATCH.constant;
- }
- case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst):
- // foo(shared(U)) shared(const(T)) => const(T)
- // foo(shared(U)) shared(inout(T)) => inout(T)
- // foo(shared(U)) shared(inout(const(T))) => inout(const(T))
- {
- at = t.unSharedOf();
- return MATCH.exact;
- }
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_):
- // foo(shared(const(U))) shared(T) => T
- {
- at = t.unSharedOf();
- return MATCH.constant;
- }
- case X(MODFlags.wildconst, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
- // foo(inout(const(U))) immutable(T) => T
- // foo(shared(const(U))) shared(inout(const(T))) => T
- // foo(shared(inout(const(U)))) immutable(T) => T
- // foo(shared(inout(const(U)))) shared(inout(T)) => T
+ foreach (tp; tparams)
{
- at = t.unSharedOf().mutableOf();
- return MATCH.constant;
+ if (tp.ident.equals(t.ident))
+ return true;
}
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
- // foo(shared(const(U))) shared(inout(T)) => T
+ return false;
+ }
+
+ bool visitInstance(TypeInstance t)
+ {
+ foreach (tp; tparams)
{
- at = t.unSharedOf().mutableOf();
- return MATCH.constant;
+ if (t.tempinst.name == tp.ident)
+ return true;
}
- case X(MODFlags.wild, 0):
- case X(MODFlags.wild, MODFlags.const_):
- case X(MODFlags.wild, MODFlags.wildconst):
- case X(MODFlags.wild, MODFlags.immutable_):
- case X(MODFlags.wild, MODFlags.shared_):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.wildconst, 0):
- case X(MODFlags.wildconst, MODFlags.const_):
- case X(MODFlags.wildconst, MODFlags.wild):
- case X(MODFlags.wildconst, MODFlags.shared_):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.shared_, 0):
- case X(MODFlags.shared_, MODFlags.const_):
- case X(MODFlags.shared_, MODFlags.wild):
- case X(MODFlags.shared_, MODFlags.wildconst):
- case X(MODFlags.shared_, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.const_, 0):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wild, 0):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wildconst, 0):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
- case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.immutable_, 0):
- case X(MODFlags.immutable_, MODFlags.const_):
- case X(MODFlags.immutable_, MODFlags.wild):
- case X(MODFlags.immutable_, MODFlags.wildconst):
- case X(MODFlags.immutable_, MODFlags.shared_):
- case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_):
- case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
- case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst):
- // foo(inout(U)) T => nomatch
- // foo(inout(U)) const(T) => nomatch
- // foo(inout(U)) inout(const(T)) => nomatch
- // foo(inout(U)) immutable(T) => nomatch
- // foo(inout(U)) shared(T) => nomatch
- // foo(inout(U)) shared(const(T)) => nomatch
- // foo(inout(U)) shared(inout(const(T))) => nomatch
- // foo(inout(const(U))) T => nomatch
- // foo(inout(const(U))) const(T) => nomatch
- // foo(inout(const(U))) inout(T) => nomatch
- // foo(inout(const(U))) shared(T) => nomatch
- // foo(inout(const(U))) shared(const(T)) => nomatch
- // foo(inout(const(U))) shared(inout(T)) => nomatch
- // foo(shared(U)) T => nomatch
- // foo(shared(U)) const(T) => nomatch
- // foo(shared(U)) inout(T) => nomatch
- // foo(shared(U)) inout(const(T)) => nomatch
- // foo(shared(U)) immutable(T) => nomatch
- // foo(shared(const(U))) T => nomatch
- // foo(shared(const(U))) const(T) => nomatch
- // foo(shared(const(U))) inout(T) => nomatch
- // foo(shared(const(U))) inout(const(T)) => nomatch
- // foo(shared(inout(U))) T => nomatch
- // foo(shared(inout(U))) const(T) => nomatch
- // foo(shared(inout(U))) inout(T) => nomatch
- // foo(shared(inout(U))) inout(const(T)) => nomatch
- // foo(shared(inout(U))) immutable(T) => nomatch
- // foo(shared(inout(U))) shared(T) => nomatch
- // foo(shared(inout(U))) shared(const(T)) => nomatch
- // foo(shared(inout(U))) shared(inout(const(T))) => nomatch
- // foo(shared(inout(const(U)))) T => nomatch
- // foo(shared(inout(const(U)))) const(T) => nomatch
- // foo(shared(inout(const(U)))) inout(T) => nomatch
- // foo(shared(inout(const(U)))) inout(const(T)) => nomatch
- // foo(shared(inout(const(U)))) shared(T) => nomatch
- // foo(shared(inout(const(U)))) shared(const(T)) => nomatch
- // foo(immutable(U)) T => nomatch
- // foo(immutable(U)) const(T) => nomatch
- // foo(immutable(U)) inout(T) => nomatch
- // foo(immutable(U)) inout(const(T)) => nomatch
- // foo(immutable(U)) shared(T) => nomatch
- // foo(immutable(U)) shared(const(T)) => nomatch
- // foo(immutable(U)) shared(inout(T)) => nomatch
- // foo(immutable(U)) shared(inout(const(T))) => nomatch
- return MATCH.nomatch;
- default:
- assert(0);
+ if (t.tempinst.tiargs)
+ foreach (arg; *t.tempinst.tiargs)
+ {
+ if (Type ta = isType(arg))
+ {
+ if (ta.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool visitTypeof(TypeTypeof t)
+ {
+ //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
+ return t.exp.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ foreach (arg; *t.arguments)
+ {
+ if (arg.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (!t)
+ return false;
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: return visitVector(tb.isTypeVector());
+ case Taarray: return visitAArray(tb.isTypeAArray());
+ case Tfunction: return visitFunction(tb.isTypeFunction());
+ case Tident: return visitIdentifier(tb.isTypeIdentifier());
+ case Tinstance: return visitInstance(tb.isTypeInstance());
+ case Ttypeof: return visitTypeof(tb.isTypeTypeof());
+ case Ttuple: return visitTuple(tb.isTypeTuple());
+ case Tenum: return false;
+ default: return tb.nextOf().reliesOnTemplateParameters(tparams);
}
}
-__gshared Expression emptyArrayElement = null;
-
-/* These form the heart of template argument deduction.
- * Given 'this' being the type argument to the template instance,
- * it is matched against the template declaration parameter specialization
- * 'tparam' to determine the type to be used for the parameter.
- * Example:
- * template Foo(T:T*) // template declaration
- * Foo!(int*) // template instantiation
- * Input:
- * this = int*
- * tparam = T*
- * parameters = [ T:T* ] // Array of TemplateParameter's
- * Output:
- * dedtypes = [ int ] // Array of Expression/Type's
+/***********************************************************
+ * Check whether the expression representation relies on one or more the template parameters.
+ * Params:
+ * e = expression to test
+ * tparams = Template parameters.
+ * Returns:
+ * true if it does
*/
-MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
{
- extern (C++) final class DeduceType : Visitor
+ extern (C++) final class ReliesOnTemplateParameters : Visitor
{
alias visit = Visitor.visit;
public:
- MATCH result;
+ TemplateParameter[] tparams;
+ bool result;
- extern (D) this() @safe
+ extern (D) this(TemplateParameter[] tparams) @safe
{
- result = MATCH.nomatch;
+ this.tparams = tparams;
}
- override void visit(Type t)
+ override void visit(Expression e)
{
- if (!tparam)
- goto Lnomatch;
-
- if (t == tparam)
- goto Lexact;
+ //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
+ }
- if (tparam.ty == Tident)
+ override void visit(IdentifierExp e)
+ {
+ //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (tp; tparams)
{
- // Determine which parameter tparam is
- size_t i = templateParameterLookup(tparam, ¶meters);
- if (i == IDX_NOTFOUND)
+ if (e.ident == tp.ident)
{
- if (!sc)
- goto Lnomatch;
-
- /* Need a loc to go with the semantic routine.
- */
- Loc loc;
- if (parameters.length)
- {
- TemplateParameter tp = parameters[0];
- loc = tp.loc;
- }
-
- /* BUG: what if tparam is a template instance, that
- * has as an argument another Tident?
- */
- tparam = tparam.typeSemantic(loc, sc);
- assert(tparam.ty != Tident);
- result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
+ result = true;
return;
}
+ }
+ }
- TemplateParameter tp = parameters[i];
-
- TypeIdentifier tident = tparam.isTypeIdentifier();
- if (tident.idents.length > 0)
+ override void visit(TupleExp e)
+ {
+ //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.exps)
+ {
+ foreach (ea; *e.exps)
{
- //printf("matching %s to %s\n", tparam.toChars(), t.toChars());
- Dsymbol s = t.toDsymbol(sc);
- for (size_t j = tident.idents.length; j-- > 0;)
- {
- RootObject id = tident.idents[j];
- if (id.dyncast() == DYNCAST.identifier)
- {
- if (!s || !s.parent)
- goto Lnomatch;
- Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id);
- if (!s2)
- goto Lnomatch;
- s2 = s2.toAlias();
- //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars());
- if (s != s2)
- {
- if (Type tx = s2.getType())
- {
- if (s != tx.toDsymbol(sc))
- goto Lnomatch;
- }
- else
- goto Lnomatch;
- }
- s = s.parent;
- }
- else
- goto Lnomatch;
- }
- //printf("[e] s = %s\n", s?s.toChars():"(null)");
- if (tp.isTemplateTypeParameter())
- {
- Type tt = s.getType();
- if (!tt)
- goto Lnomatch;
- Type at = cast(Type)dedtypes[i];
- if (at && at.ty == Tnone)
- at = (cast(TypeDeduced)at).tded;
- if (!at || tt.equals(at))
- {
- dedtypes[i] = tt;
- goto Lexact;
- }
- }
- if (tp.isTemplateAliasParameter())
- {
- Dsymbol s2 = cast(Dsymbol)dedtypes[i];
- if (!s2 || s == s2)
- {
- dedtypes[i] = s;
- goto Lexact;
- }
- }
- goto Lnomatch;
+ ea.accept(this);
+ if (result)
+ return;
}
+ }
+ }
- // Found the corresponding parameter tp
- /+
- https://issues.dlang.org/show_bug.cgi?id=23578
- To pattern match:
- static if (is(S!int == S!av, alias av))
-
- We eventually need to deduce `int` (Tint32 [0]) and `av` (Tident).
- Previously this would not get pattern matched at all, but now we check if the
- template parameter `av` came from.
-
- This note has been left to serve as a hint for further explorers into
- how IsExp matching works.
- +/
- if (auto ta = tp.isTemplateAliasParameter())
- {
- dedtypes[i] = t;
- goto Lexact;
- }
- // (23578) - ensure previous behaviour for non-alias template params
- if (!tp.isTemplateTypeParameter())
+ override void visit(ArrayLiteralExp e)
+ {
+ //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (el; *e.elements)
{
- goto Lnomatch;
+ el.accept(this);
+ if (result)
+ return;
}
+ }
+ }
- Type at = cast(Type)dedtypes[i];
- Type tt;
- if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
- {
- // type vs (none)
- if (!at)
- {
- dedtypes[i] = tt;
- *wm |= wx;
- result = MATCH.constant;
- return;
- }
-
- // type vs expressions
- if (at.ty == Tnone)
- {
- auto xt = cast(TypeDeduced)at;
- result = xt.matchAll(tt);
- if (result > MATCH.nomatch)
- {
- dedtypes[i] = tt;
- if (result > MATCH.constant)
- result = MATCH.constant; // limit level for inout matches
- }
- return;
- }
-
- // type vs type
- if (tt.equals(at))
- {
- dedtypes[i] = tt; // Prefer current type match
- goto Lconst;
- }
- if (tt.implicitConvTo(at.constOf()))
- {
- dedtypes[i] = at.constOf().mutableOf();
- *wm |= MODFlags.const_;
- goto Lconst;
- }
- if (at.implicitConvTo(tt.constOf()))
- {
- dedtypes[i] = tt.constOf().mutableOf();
- *wm |= MODFlags.const_;
- goto Lconst;
- }
- goto Lnomatch;
- }
- else if (MATCH m = deduceTypeHelper(t, tt, tparam))
- {
- // type vs (none)
- if (!at)
- {
- dedtypes[i] = tt;
- result = m;
- return;
- }
-
- // type vs expressions
- if (at.ty == Tnone)
- {
- auto xt = cast(TypeDeduced)at;
- result = xt.matchAll(tt);
- if (result > MATCH.nomatch)
- {
- dedtypes[i] = tt;
- }
- return;
- }
-
- // type vs type
- if (tt.equals(at))
- {
- goto Lexact;
- }
- if (tt.ty == Tclass && at.ty == Tclass)
- {
- result = tt.implicitConvTo(at);
- return;
- }
- if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant)
- {
- goto Lexact;
- }
- }
- goto Lnomatch;
- }
-
- if (tparam.ty == Ttypeof)
- {
- /* Need a loc to go with the semantic routine.
- */
- Loc loc;
- if (parameters.length)
- {
- TemplateParameter tp = parameters[0];
- loc = tp.loc;
- }
-
- tparam = tparam.typeSemantic(loc, sc);
- }
- if (t.ty != tparam.ty)
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (ek; *e.keys)
{
- if (Dsymbol sym = t.toDsymbol(sc))
- {
- if (sym.isforwardRef() && !tparam.deco)
- goto Lnomatch;
- }
-
- MATCH m = t.implicitConvTo(tparam);
- if (m == MATCH.nomatch && !ignoreAliasThis)
- {
- if (auto tc = t.isTypeClass())
- {
- if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT))
- {
- if (auto ato = t.aliasthisOf())
- {
- tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT);
- m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
- tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
- }
- }
- }
- else if (auto ts = t.isTypeStruct())
- {
- if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT))
- {
- if (auto ato = t.aliasthisOf())
- {
- ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT);
- m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
- ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
- }
- }
- }
- }
- result = m;
- return;
+ ek.accept(this);
+ if (result)
+ return;
}
-
- if (t.nextOf())
+ foreach (ev; *e.values)
{
- if (tparam.deco && !tparam.hasWild())
- {
- result = t.implicitConvTo(tparam);
+ ev.accept(this);
+ if (result)
return;
- }
-
- Type tpn = tparam.nextOf();
- if (wm && t.ty == Taarray && tparam.isWild())
- {
- // https://issues.dlang.org/show_bug.cgi?id=12403
- // In IFTI, stop inout matching on transitive part of AA types.
- tpn = tpn.substWildTo(MODFlags.mutable);
- }
-
- result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm);
- return;
}
-
- Lexact:
- result = MATCH.exact;
- return;
-
- Lnomatch:
- result = MATCH.nomatch;
- return;
-
- Lconst:
- result = MATCH.constant;
}
- override void visit(TypeVector t)
+ override void visit(StructLiteralExp e)
{
- if (auto tp = tparam.isTypeVector())
+ //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
{
- result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
- return;
+ foreach (ea; *e.elements)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
}
- visit(cast(Type)t);
}
- override void visit(TypeDArray t)
+ override void visit(TypeExp e)
{
- visit(cast(Type)t);
+ //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.type.reliesOnTemplateParameters(tparams);
}
- override void visit(TypeSArray t)
+ override void visit(NewExp e)
{
- // Extra check that array dimensions must match
- if (!tparam)
- {
- visit(cast(Type)t);
- return;
- }
-
- if (tparam.ty == Tarray)
- {
- MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
- result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
- return;
- }
-
- TemplateParameter tp = null;
- Expression edim = null;
- size_t i;
- if (auto tsa = tparam.isTypeSArray())
+ //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.placement)
+ e.placement.accept(this);
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ result = e.newtype.reliesOnTemplateParameters(tparams);
+ if (!result && e.arguments)
{
- if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter)
+ foreach (ea; *e.arguments)
{
- Identifier id = tsa.dim.isVarExp().var.ident;
- i = templateIdentifierLookup(id, ¶meters);
- assert(i != IDX_NOTFOUND);
- tp = parameters[i];
+ ea.accept(this);
+ if (result)
+ return;
}
- else
- edim = tsa.dim;
}
- else if (auto taa = tparam.isTypeAArray())
- {
- i = templateParameterLookup(taa.index, ¶meters);
- if (i != IDX_NOTFOUND)
- tp = parameters[i];
- else
- {
- Loc loc;
- // The "type" (it hasn't been resolved yet) of the function parameter
- // does not have a location but the parameter it is related to does,
- // so we use that for the resolution (better error message).
- if (inferStart < parameters.length)
- {
- TemplateParameter loctp = parameters[inferStart];
- loc = loctp.loc;
- }
+ }
- Expression e;
- Type tx;
- Dsymbol s;
- taa.index.resolve(loc, sc, e, tx, s);
- edim = s ? getValue(s) : getValue(e);
- }
- }
- if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) ||
- (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger())
- )
- {
- result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
- return;
- }
+ override void visit(NewAnonClassExp e)
+ {
+ //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
- visit(cast(Type)t);
+ override void visit(TypeidExp e)
+ {
+ //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (auto ea = isExpression(e.obj))
+ ea.accept(this);
+ else if (auto ta = isType(e.obj))
+ result = ta.reliesOnTemplateParameters(tparams);
}
- override void visit(TypeAArray t)
+ override void visit(TraitsExp e)
{
- // Extra check that index type must match
- if (tparam && tparam.ty == Taarray)
+ //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.args)
{
- TypeAArray tp = tparam.isTypeAArray();
- if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+ foreach (oa; *e.args)
{
- result = MATCH.nomatch;
- return;
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
}
}
- visit(cast(Type)t);
}
- override void visit(TypeFunction t)
+ override void visit(IsExp e)
{
- // Extra check that function characteristics must match
- if (!tparam)
- return visit(cast(Type)t);
-
- auto tp = tparam.isTypeFunction();
- if (!tp)
- {
- visit(cast(Type)t);
- return;
- }
+ //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.targ.reliesOnTemplateParameters(tparams);
+ }
- if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
- {
- result = MATCH.nomatch;
- return;
- }
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ }
- foreach (fparam; *tp.parameterList.parameters)
+ override void visit(DotTemplateInstanceExp e)
+ {
+ //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.ti.tiargs)
{
- // https://issues.dlang.org/show_bug.cgi?id=2579
- // Apply function parameter storage classes to parameter types
- fparam.type = fparam.type.addStorageClass(fparam.storageClass);
- fparam.storageClass &= ~STC.TYPECTOR;
-
- // https://issues.dlang.org/show_bug.cgi?id=15243
- // Resolve parameter type if it's not related with template parameters
- if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length]))
+ foreach (oa; *e.ti.tiargs)
{
- auto tx = fparam.type.typeSemantic(Loc.initial, sc);
- if (tx.ty == Terror)
- {
- result = MATCH.nomatch;
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
return;
- }
- fparam.type = tx;
}
}
+ }
- const size_t nfargs = t.parameterList.length;
- size_t nfparams = tp.parameterList.length;
-
- /* See if tuple match
- */
- if (nfparams > 0 && nfargs >= nfparams - 1)
+ override void visit(CallExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.arguments)
{
- /* See if 'A' of the template parameter matches 'A'
- * of the type of the last function parameter.
- */
- Parameter fparam = tp.parameterList[nfparams - 1];
- assert(fparam);
- assert(fparam.type);
- if (fparam.type.ty != Tident)
- goto L1;
- TypeIdentifier tid = fparam.type.isTypeIdentifier();
- if (tid.idents.length)
- goto L1;
-
- /* Look through parameters to find tuple matching tid.ident
- */
- size_t tupi = 0;
- for (; 1; tupi++)
- {
- if (tupi == parameters.length)
- goto L1;
- TemplateParameter tx = parameters[tupi];
- TemplateTupleParameter tup = tx.isTemplateTupleParameter();
- if (tup && tup.ident.equals(tid.ident))
- break;
- }
-
- /* The types of the function arguments [nfparams - 1 .. nfargs]
- * now form the tuple argument.
- */
- size_t tuple_dim = nfargs - (nfparams - 1);
-
- /* See if existing tuple, and whether it matches or not
- */
- RootObject o = dedtypes[tupi];
- if (o)
+ foreach (ea; *e.arguments)
{
- // Existing deduced argument must be a tuple, and must match
- Tuple tup = isTuple(o);
- if (!tup || tup.objects.length != tuple_dim)
- {
- result = MATCH.nomatch;
+ ea.accept(this);
+ if (result)
return;
- }
- for (size_t i = 0; i < tuple_dim; i++)
- {
- Parameter arg = t.parameterList[nfparams - 1 + i];
- if (!arg.type.equals(tup.objects[i]))
- {
- result = MATCH.nomatch;
- return;
- }
- }
- }
- else
- {
- // Create new tuple
- auto tup = new Tuple(tuple_dim);
- for (size_t i = 0; i < tuple_dim; i++)
- {
- Parameter arg = t.parameterList[nfparams - 1 + i];
- tup.objects[i] = arg.type;
- }
- dedtypes[tupi] = tup;
}
- nfparams--; // don't consider the last parameter for type deduction
- goto L2;
- }
-
- L1:
- if (nfargs != nfparams)
- {
- result = MATCH.nomatch;
- return;
}
- L2:
- assert(nfparams <= tp.parameterList.length);
- foreach (i, ap; tp.parameterList)
- {
- if (i == nfparams)
- break;
-
- Parameter a = t.parameterList[i];
+ }
- if (!a.isCovariant(t.isRef, ap) ||
- !deduceType(a.type, sc, ap.type, parameters, dedtypes))
- {
- result = MATCH.nomatch;
- return;
- }
- }
+ override void visit(CastExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ // e.to can be null for cast() with no type
+ if (!result && e.to)
+ result = e.to.reliesOnTemplateParameters(tparams);
+ }
- visit(cast(Type)t);
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.lwr)
+ e.lwr.accept(this);
+ if (!result && e.upr)
+ e.upr.accept(this);
}
- override void visit(TypeIdentifier t)
+ override void visit(IntervalExp e)
{
- // Extra check
- if (tparam && tparam.ty == Tident)
- {
- TypeIdentifier tp = tparam.isTypeIdentifier();
- for (size_t i = 0; i < t.idents.length; i++)
- {
- RootObject id1 = t.idents[i];
- RootObject id2 = tp.idents[i];
- if (!id1.equals(id2))
- {
- result = MATCH.nomatch;
- return;
- }
- }
- }
- visit(cast(Type)t);
+ //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.lwr.accept(this);
+ if (!result)
+ e.upr.accept(this);
}
- override void visit(TypeInstance t)
+ override void visit(ArrayExp e)
{
- // Extra check
- if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl)
+ //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(e.isUnaExp());
+ if (!result && e.arguments)
{
- visit(cast(Type)t);
- return;
+ foreach (ea; *e.arguments)
+ ea.accept(this);
}
+ }
- TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
-
- TypeInstance tp = tparam.isTypeInstance();
+ override void visit(BinExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ if (!result)
+ e.e2.accept(this);
+ }
- //printf("tempinst.tempdecl = %p\n", tempdecl);
- //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl);
- if (!tp.tempinst.tempdecl)
- {
- //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars());
-
- /* Handle case of:
- * template Foo(T : sa!(T), alias sa)
- */
- size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters);
- if (i == IDX_NOTFOUND)
- {
- /* Didn't find it as a parameter identifier. Try looking
- * it up and seeing if is an alias.
- * https://issues.dlang.org/show_bug.cgi?id=1454
- */
- auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name);
- Type tx;
- Expression e;
- Dsymbol s;
- tid.resolve(tp.loc, sc, e, tx, s);
- if (tx)
- {
- s = tx.toDsymbol(sc);
- if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null)
- {
- // https://issues.dlang.org/show_bug.cgi?id=14290
- // Try to match with ti.tempecl,
- // only when ti is an enclosing instance.
- Dsymbol p = sc.parent;
- while (p && p != ti)
- p = p.parent;
- if (p)
- s = ti.tempdecl;
- }
- }
- if (s)
- {
- s = s.toAlias();
- TemplateDeclaration td = s.isTemplateDeclaration();
- if (td)
- {
- if (td.overroot)
- td = td.overroot;
- for (; td; td = td.overnext)
- {
- if (td == tempdecl)
- goto L2;
- }
- }
- }
- goto Lnomatch;
- }
+ override void visit(CondExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.econd.accept(this);
+ if (!result)
+ visit(e.isBinExp());
+ }
+ }
- TemplateParameter tpx = parameters[i];
- if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null))
- goto Lnomatch;
- }
- else if (tempdecl != tp.tempinst.tempdecl)
- goto Lnomatch;
+ scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+ e.accept(v);
+ return v.result;
+}
- L2:
- if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes))
- goto Lnomatch;
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateParameter
+ */
+extern (C++) class TemplateParameter : ASTNode
+{
+ Loc loc;
+ Identifier ident;
- visit(cast(Type)t);
- return;
+ /* True if this is a part of precedent parameter specialization pattern.
+ *
+ * template A(T : X!TL, alias X, TL...) {}
+ * // X and TL are dependent template parameter
+ *
+ * A dependent template parameter should return MATCH.exact in matchArg()
+ * to respect the match level of the corresponding precedent parameter.
+ */
+ bool dependent;
- Lnomatch:
- //printf("no match\n");
- result = MATCH.nomatch;
- }
+ /* ======================== TemplateParameter =============================== */
+ extern (D) this(Loc loc, Identifier ident) @safe
+ {
+ this.loc = loc;
+ this.ident = ident;
+ }
- override void visit(TypeStruct t)
- {
- /* If this struct is a template struct, and we're matching
- * it against a template instance, convert the struct type
- * to a template instance, too, and try again.
- */
- TemplateInstance ti = t.sym.parent.isTemplateInstance();
+ TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return null;
+ }
- if (tparam && tparam.ty == Tinstance)
- {
- if (ti && ti.toAlias() == t.sym)
- {
- auto tx = new TypeInstance(Loc.initial, ti);
- auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
- // if we have a no match we still need to check alias this
- if (m != MATCH.nomatch)
- {
- result = m;
- return;
- }
- }
+ TemplateValueParameter isTemplateValueParameter()
+ {
+ return null;
+ }
- /* Match things like:
- * S!(T).foo
- */
- TypeInstance tpi = tparam.isTypeInstance();
- if (tpi.idents.length)
- {
- RootObject id = tpi.idents[tpi.idents.length - 1];
- if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
- {
- Type tparent = t.sym.parent.getType();
- if (tparent)
- {
- /* Slice off the .foo in S!(T).foo
- */
- tpi.idents.length--;
- result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
- tpi.idents.length++;
- return;
- }
- }
- }
- }
+ TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return null;
+ }
- // Extra check
- if (tparam && tparam.ty == Tstruct)
- {
- TypeStruct tp = tparam.isTypeStruct();
+ TemplateThisParameter isTemplateThisParameter()
+ {
+ return null;
+ }
- //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
- if (wm && t.deduceWild(tparam, false))
- {
- result = MATCH.constant;
- return;
- }
- result = t.implicitConvTo(tp);
- return;
- }
- visit(cast(Type)t);
- }
+ TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return null;
+ }
- override void visit(TypeEnum t)
- {
- // Extra check
- if (tparam && tparam.ty == Tenum)
- {
- TypeEnum tp = tparam.isTypeEnum();
- if (t.sym == tp.sym)
- visit(cast(Type)t);
- else
- result = MATCH.nomatch;
- return;
- }
- Type tb = t.toBasetype();
- if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray)
- {
- result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
- if (result == MATCH.exact)
- result = MATCH.convert;
- return;
- }
- visit(cast(Type)t);
- }
+ abstract TemplateParameter syntaxCopy();
- override void visit(TypeClass t)
- {
- //printf("TypeClass.deduceType(this = %s)\n", t.toChars());
+ abstract bool declareParameter(Scope* sc);
- /* If this class is a template class, and we're matching
- * it against a template instance, convert the class type
- * to a template instance, too, and try again.
- */
- TemplateInstance ti = t.sym.parent.isTemplateInstance();
+ abstract void print(RootObject oarg, RootObject oded);
- if (tparam && tparam.ty == Tinstance)
- {
- if (ti && ti.toAlias() == t.sym)
- {
- auto tx = new TypeInstance(Loc.initial, ti);
- MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
- // Even if the match fails, there is still a chance it could match
- // a base class.
- if (m != MATCH.nomatch)
- {
- result = m;
- return;
- }
- }
+ abstract RootObject specialization();
- /* Match things like:
- * S!(T).foo
- */
- TypeInstance tpi = tparam.isTypeInstance();
- if (tpi.idents.length)
- {
- RootObject id = tpi.idents[tpi.idents.length - 1];
- if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
- {
- Type tparent = t.sym.parent.getType();
- if (tparent)
- {
- /* Slice off the .foo in S!(T).foo
- */
- tpi.idents.length--;
- result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
- tpi.idents.length++;
- return;
- }
- }
- }
+ abstract bool hasDefaultArg();
- // If it matches exactly or via implicit conversion, we're done
- visit(cast(Type)t);
- if (result != MATCH.nomatch)
- return;
+ override const(char)* toChars() const
+ {
+ return this.ident.toChars();
+ }
- /* There is still a chance to match via implicit conversion to
- * a base class or interface. Because there could be more than one such
- * match, we need to check them all.
- */
+ override DYNCAST dyncast() const
+ {
+ return DYNCAST.templateparameter;
+ }
- int numBaseClassMatches = 0; // Have we found an interface match?
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
- // Our best guess at dedtypes
- auto best = new Objects(dedtypes.length);
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateTypeParameter
+ * Syntax:
+ * ident : specType = defaultType
+ */
+extern (C++) class TemplateTypeParameter : TemplateParameter
+{
+ Type specType; // if !=null, this is the type specialization
+ Type defaultType;
- ClassDeclaration s = t.sym;
- while (s && s.baseclasses.length > 0)
- {
- // Test the base class
- deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
+ extern (D) __gshared Type tdummy = null;
- // Test the interfaces inherited by the base class
- foreach (b; s.interfaces)
- {
- deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
- }
- s = (*s.baseclasses)[0].sym;
- }
+ extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.defaultType = defaultType;
+ }
- if (numBaseClassMatches == 0)
- {
- result = MATCH.nomatch;
- return;
- }
+ override final TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return this;
+ }
- // If we got at least one match, copy the known types into dedtypes
- memcpy(dedtypes.tdata(), best.tdata(), best.length * (void*).sizeof);
- result = MATCH.convert;
- return;
- }
+ override TemplateTypeParameter syntaxCopy()
+ {
+ return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
- // Extra check
- if (tparam && tparam.ty == Tclass)
- {
- TypeClass tp = tparam.isTypeClass();
+ override final bool declareParameter(Scope* sc)
+ {
+ //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
- //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
- if (wm && t.deduceWild(tparam, false))
- {
- result = MATCH.constant;
- return;
- }
- result = t.implicitConvTo(tp);
- return;
- }
- visit(cast(Type)t);
- }
+ override final void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
- override void visit(Expression e)
- {
- //printf("Expression.deduceType(e = %s)\n", e.toChars());
- size_t i = templateParameterLookup(tparam, ¶meters);
- if (i == IDX_NOTFOUND || tparam.isTypeIdentifier().idents.length > 0)
- {
- if (e == emptyArrayElement && tparam.ty == Tarray)
- {
- Type tn = (cast(TypeNext)tparam).next;
- result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
- return;
- }
- e.type.accept(this);
- return;
- }
+ Type t = isType(oarg);
+ Type ta = isType(oded);
+ assert(ta);
- TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter();
- if (!tp)
- return; // nomatch
+ if (specType)
+ printf("\tSpecialization: %s\n", specType.toChars());
+ if (defaultType)
+ printf("\tDefault: %s\n", defaultType.toChars());
+ printf("\tParameter: %s\n", t ? t.toChars() : "NULL");
+ printf("\tDeduced Type: %s\n", ta.toChars());
+ }
- if (e == emptyArrayElement)
- {
- if (dedtypes[i])
- {
- result = MATCH.exact;
- return;
- }
- if (tp.defaultType)
- {
- tp.defaultType.accept(this);
- return;
- }
- }
+ override final RootObject specialization()
+ {
+ return specType;
+ }
- /* Returns `true` if `t` is a reference type, or an array of reference types
- */
- bool isTopRef(Type t)
- {
- auto tb = t.baseElemOf();
- return tb.ty == Tclass ||
- tb.ty == Taarray ||
- tb.ty == Tstruct && tb.hasPointers();
- }
+ override final bool hasDefaultArg()
+ {
+ return defaultType !is null;
+ }
- Type at = cast(Type)dedtypes[i];
- Type tt;
- if (ubyte wx = deduceWildHelper(e.type, &tt, tparam))
- {
- *wm |= wx;
- result = MATCH.constant;
- }
- else if (MATCH m = deduceTypeHelper(e.type, tt, tparam))
- {
- result = m;
- }
- else if (!isTopRef(e.type))
- {
- /* https://issues.dlang.org/show_bug.cgi?id=15653
- * In IFTI, recognize top-qualifier conversions
- * through the value copy, e.g.
- * int --> immutable(int)
- * immutable(string[]) --> immutable(string)[]
- */
- tt = e.type.mutableOf();
- result = MATCH.convert;
- }
- else
- return; // nomatch
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
- // expression vs (none)
- if (!at)
- {
- dedtypes[i] = new TypeDeduced(tt, e, tparam);
- return;
- }
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateThisParameter
+ * Syntax:
+ * this ident : specType = defaultType
+ */
+extern (C++) final class TemplateThisParameter : TemplateTypeParameter
+{
+ extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
+ {
+ super(loc, ident, specType, defaultType);
+ }
- TypeDeduced xt = null;
- if (at.ty == Tnone)
- {
- xt = cast(TypeDeduced)at;
- at = xt.tded;
- }
+ override TemplateThisParameter isTemplateThisParameter()
+ {
+ return this;
+ }
- // From previous matched expressions to current deduced type
- MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+ override TemplateThisParameter syntaxCopy()
+ {
+ return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
- // From current expressions to previous deduced type
- Type pt = at.addMod(tparam.mod);
- if (*wm)
- pt = pt.substWildTo(*wm);
- MATCH match2 = e.implicitConvTo(pt);
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
- if (match1 > MATCH.nomatch && match2 > MATCH.nomatch)
- {
- if (at.implicitConvTo(tt) == MATCH.nomatch)
- match1 = MATCH.nomatch; // Prefer at
- else if (tt.implicitConvTo(at) == MATCH.nomatch)
- match2 = MATCH.nomatch; // Prefer tt
- else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod)
- {
- if (!tt.isMutable() && !at.isMutable())
- tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod));
- else if (tt.isMutable())
- {
- if (at.mod == 0) // Prefer unshared
- match1 = MATCH.nomatch;
- else
- match2 = MATCH.nomatch;
- }
- else if (at.isMutable())
- {
- if (tt.mod == 0) // Prefer unshared
- match2 = MATCH.nomatch;
- else
- match1 = MATCH.nomatch;
- }
- //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars());
- }
- else
- {
- match1 = MATCH.nomatch;
- match2 = MATCH.nomatch;
- }
- }
- if (match1 > MATCH.nomatch)
- {
- // Prefer current match: tt
- if (xt)
- xt.update(tt, e, tparam);
- else
- dedtypes[i] = tt;
- result = match1;
- return;
- }
- if (match2 > MATCH.nomatch)
- {
- // Prefer previous match: (*dedtypes)[i]
- if (xt)
- xt.update(e, tparam);
- result = match2;
- return;
- }
-
- /* Deduce common type
- */
- if (Type t = rawTypeMerge(at, tt))
- {
- if (xt)
- xt.update(t, e, tparam);
- else
- dedtypes[i] = t;
-
- pt = tt.addMod(tparam.mod);
- if (*wm)
- pt = pt.substWildTo(*wm);
- result = e.implicitConvTo(pt);
- return;
- }
-
- result = MATCH.nomatch;
- }
-
- MATCH deduceEmptyArrayElement()
- {
- if (!emptyArrayElement)
- {
- emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy
- emptyArrayElement.type = Type.tvoid;
- }
- assert(tparam.ty == Tarray);
-
- Type tn = (cast(TypeNext)tparam).next;
- return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
- }
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateValueParameter
+ * Syntax:
+ * valType ident : specValue = defaultValue
+ */
+extern (C++) final class TemplateValueParameter : TemplateParameter
+{
+ Type valType;
+ Expression specValue;
+ Expression defaultValue;
- override void visit(NullExp e)
- {
- if (tparam.ty == Tarray && e.type.ty == Tnull)
- {
- // tparam:T[] <- e:null (void[])
- result = deduceEmptyArrayElement();
- return;
- }
- visit(cast(Expression)e);
- }
+ extern (D) __gshared Expression[void*] edummies;
- override void visit(StringExp e)
- {
- Type taai;
- if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
- {
- // Consider compile-time known boundaries
- e.type.nextOf().sarrayOf(e.len).accept(this);
- return;
- }
- visit(cast(Expression)e);
- }
+ extern (D) this(Loc loc, Identifier ident, Type valType,
+ Expression specValue, Expression defaultValue) @safe
+ {
+ super(loc, ident);
+ this.valType = valType;
+ this.specValue = specValue;
+ this.defaultValue = defaultValue;
+ }
- override void visit(ArrayLiteralExp e)
- {
- // https://issues.dlang.org/show_bug.cgi?id=20092
- if (e.elements && e.elements.length && e.type.toBasetype().nextOf().ty == Tvoid)
- {
- result = deduceEmptyArrayElement();
- return;
- }
- if ((!e.elements || !e.elements.length) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray)
- {
- // tparam:T[] <- e:[] (void[])
- result = deduceEmptyArrayElement();
- return;
- }
+ override TemplateValueParameter isTemplateValueParameter()
+ {
+ return this;
+ }
- if (tparam.ty == Tarray && e.elements && e.elements.length)
- {
- Type tn = (cast(TypeDArray)tparam).next;
- result = MATCH.exact;
- if (e.basis)
- {
- MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm);
- if (m < result)
- result = m;
- }
- foreach (el; *e.elements)
- {
- if (result == MATCH.nomatch)
- break;
- if (!el)
- continue;
- MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
- if (m < result)
- result = m;
- }
- return;
- }
+ override TemplateValueParameter syntaxCopy()
+ {
+ return new TemplateValueParameter(loc, ident,
+ valType.syntaxCopy(),
+ specValue ? specValue.syntaxCopy() : null,
+ defaultValue ? defaultValue.syntaxCopy() : null);
+ }
- Type taai;
- if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
- {
- // Consider compile-time known boundaries
- e.type.nextOf().sarrayOf(e.elements.length).accept(this);
- return;
- }
- visit(cast(Expression)e);
- }
+ override bool declareParameter(Scope* sc)
+ {
+ /*
+ Do type semantic earlier.
- override void visit(AssocArrayLiteralExp e)
- {
- if (tparam.ty == Taarray && e.keys && e.keys.length)
- {
- TypeAArray taa = cast(TypeAArray)tparam;
- result = MATCH.exact;
- foreach (i, key; *e.keys)
- {
- MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm);
- if (m1 < result)
- result = m1;
- if (result == MATCH.nomatch)
- break;
- MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm);
- if (m2 < result)
- result = m2;
- if (result == MATCH.nomatch)
- break;
- }
- return;
- }
- visit(cast(Expression)e);
- }
+ This means for certain erroneous value parameters
+ their "type" can be known earlier and thus a better
+ error message given.
- override void visit(FuncExp e)
- {
- //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars());
- if (e.td)
- {
- Type to = tparam;
- if (!to.nextOf())
- return;
- auto tof = to.nextOf().isTypeFunction();
- if (!tof)
- return;
+ For example:
+ `template test(x* x) {}`
+ now yields "undefined identifier" rather than the opaque
+ "variable `x` is used as a type".
+ */
+ if (valType)
+ valType = valType.typeSemantic(loc, sc);
+ auto v = new VarDeclaration(loc, valType, ident, null);
+ v.storage_class = STC.templateparameter;
+ return sc.insert(v) !is null;
+ }
- // Parameter types inference from 'tof'
- assert(e.td._scope);
- TypeFunction tf = e.fd.type.isTypeFunction();
- //printf("\ttof = %s\n", tof.toChars());
- //printf("\ttf = %s\n", tf.toChars());
- const dim = tf.parameterList.length;
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+ Expression ea = isExpression(oded);
+ if (specValue)
+ printf("\tSpecialization: %s\n", specValue.toChars());
+ printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL");
+ }
- if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
- return;
+ override RootObject specialization()
+ {
+ return specValue;
+ }
- auto tiargs = new Objects();
- tiargs.reserve(e.td.parameters.length);
+ override bool hasDefaultArg()
+ {
+ return defaultValue !is null;
+ }
- foreach (tp; *e.td.parameters)
- {
- size_t u = 0;
- foreach (i, p; tf.parameterList)
- {
- if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
- break;
- ++u;
- }
- assert(u < dim);
- Parameter pto = tof.parameterList[u];
- if (!pto)
- break;
- Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774
- if (reliesOnTemplateParameters(t, parameters[inferStart .. parameters.length]))
- return;
- t = t.typeSemantic(e.loc, sc);
- if (t.ty == Terror)
- return;
- tiargs.push(t);
- }
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
- // Set target of return type inference
- if (!tf.next && tof.next)
- e.fd.treq = tparam;
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateAliasParameter
+ * Syntax:
+ * specType ident : specAlias = defaultAlias
+ */
+extern (C++) final class TemplateAliasParameter : TemplateParameter
+{
+ Type specType;
+ RootObject specAlias;
+ RootObject defaultAlias;
- auto ti = new TemplateInstance(e.loc, e.td, tiargs);
- Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+ extern (D) __gshared Dsymbol sdummy = null;
- // Reset inference target for the later re-semantic
- e.fd.treq = null;
+ extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.specAlias = specAlias;
+ this.defaultAlias = defaultAlias;
+ }
- if (ex.op == EXP.error)
- return;
- if (ex.op != EXP.function_)
- return;
- visit(ex.type);
- return;
- }
+ override TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return this;
+ }
- Type t = e.type;
+ override TemplateAliasParameter syntaxCopy()
+ {
+ return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
+ }
- if (t.ty == Tdelegate && tparam.ty == Tpointer)
- return;
+ override bool declareParameter(Scope* sc)
+ {
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
- // Allow conversion from implicit function pointer to delegate
- if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate)
- {
- TypeFunction tf = t.nextOf().isTypeFunction();
- t = (new TypeDelegate(tf)).merge();
- }
- //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars());
- visit(t);
- }
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+ Dsymbol sa = isDsymbol(oded);
+ assert(sa);
+ printf("\tParameter alias: %s\n", sa.toChars());
+ }
- override void visit(SliceExp e)
- {
- Type taai;
- if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
- {
- // Consider compile-time known boundaries
- if (Type tsa = toStaticArrayType(e))
- {
- tsa.accept(this);
- if (result > MATCH.convert)
- result = MATCH.convert; // match with implicit conversion at most
- return;
- }
- }
- visit(cast(Expression)e);
- }
+ override RootObject specialization()
+ {
+ return specAlias;
+ }
- override void visit(CommaExp e)
- {
- e.e2.accept(this);
- }
+ override bool hasDefaultArg()
+ {
+ return defaultAlias !is null;
}
- scope DeduceType v = new DeduceType();
- if (Type t = isType(o))
- t.accept(v);
- else if (Expression e = isExpression(o))
+ override void accept(Visitor v)
{
- assert(wm);
- e.accept(v);
+ v.visit(this);
}
- else
- assert(0);
- return v.result;
}
-
-/* Helper for TypeClass.deduceType().
- * Classes can match with implicit conversion to a base class or interface.
- * This is complicated, because there may be more than one base class which
- * matches. In such cases, one or more parameters remain ambiguous.
- * For example,
- *
- * interface I(X, Y) {}
- * class C : I(uint, double), I(char, double) {}
- * C x;
- * foo(T, U)( I!(T, U) x)
- *
- * deduces that U is double, but T remains ambiguous (could be char or uint).
- *
- * Given a baseclass b, and initial deduced types 'dedtypes', this function
- * tries to match tparam with b, and also tries all base interfaces of b.
- * If a match occurs, numBaseClassMatches is incremented, and the new deduced
- * types are ANDed with the current 'best' estimate for dedtypes.
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateSequenceParameter
+ * Syntax:
+ * ident ...
*/
-private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches)
+extern (C++) final class TemplateTupleParameter : TemplateParameter
{
- if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null)
+ extern (D) this(Loc loc, Identifier ident) @safe
{
- // Make a temporary copy of dedtypes so we don't destroy it
- auto tmpdedtypes = new Objects(dedtypes.length);
- memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.length * (void*).sizeof);
+ super(loc, ident);
+ }
- auto t = new TypeInstance(Loc.initial, parti);
- MATCH m = deduceType(t, sc, tparam, parameters, *tmpdedtypes);
- if (m > MATCH.nomatch)
- {
- // If this is the first ever match, it becomes our best estimate
- if (numBaseClassMatches == 0)
- memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.length * (void*).sizeof);
- else
- for (size_t k = 0; k < tmpdedtypes.length; ++k)
- {
- // If we've found more than one possible type for a parameter,
- // mark it as unknown.
- if ((*tmpdedtypes)[k] != best[k])
- best[k] = dedtypes[k];
- }
- ++numBaseClassMatches;
- }
+ override TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return this;
}
- // Now recursively test the inherited interfaces
- foreach (ref bi; b.baseInterfaces)
+ override TemplateTupleParameter syntaxCopy()
{
- deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ return new TemplateTupleParameter(loc, ident);
}
-}
-/********************
- * Match template `parameters` to the target template instance.
- * Example:
- * struct Temp(U, int Z) {}
- * void foo(T)(Temp!(T, 3));
- * foo(Temp!(int, 3)());
- * Input:
- * sc = context
- * parameters = template params of foo -> [T]
- * tiargs = <Temp!(int, 3)>.tiargs -> [int, 3]
- * tdtypes = <Temp!(int, 3)>.tdtypes -> [int, 3]
- * tempdecl = <struct Temp!(T, int Z)> -> [T, Z]
- * tp = <Temp!(T, 3)>
- * Output:
- * dedtypes = deduced params of `foo(Temp!(int, 3)())` -> [int]
- */
-private bool resolveTemplateInstantiation(Scope* sc, TemplateParameters* parameters, Objects* tiargs, Objects* tdtypes, TemplateDeclaration tempdecl, TypeInstance tp, Objects* dedtypes)
-{
- for (size_t i = 0; 1; i++)
+ override bool declareParameter(Scope* sc)
{
- //printf("\ttest: tempinst.tiargs[%zu]\n", i);
- RootObject o1 = null;
- if (i < tiargs.length)
- o1 = (*tiargs)[i];
- else if (i < tdtypes.length && i < tp.tempinst.tiargs.length)
- {
- // Pick up default arg
- o1 = (*tdtypes)[i];
- }
- else if (i >= tp.tempinst.tiargs.length)
- break;
- //printf("\ttest: o1 = %s\n", o1.toChars());
- if (i >= tp.tempinst.tiargs.length)
- {
- size_t dim = tempdecl.parameters.length - (tempdecl.isVariadic() ? 1 : 0);
- while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg()))
- {
- i++;
- }
- if (i >= dim)
- break; // match if all remained parameters are dependent
- return false;
- }
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
- RootObject o2 = (*tp.tempinst.tiargs)[i];
- Type t2 = isType(o2);
- //printf("\ttest: o2 = %s\n", o2.toChars());
- size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.length - 1)
- ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
- if (j != IDX_NOTFOUND && j == parameters.length - 1 &&
- (*parameters)[j].isTemplateTupleParameter())
- {
- /* Given:
- * struct A(B...) {}
- * alias A!(int, float) X;
- * static if (is(X Y == A!(Z), Z...)) {}
- * deduce that Z is a tuple(int, float)
- */
-
- /* Create tuple from remaining args
- */
- size_t vtdim = (tempdecl.isVariadic() ? tiargs.length : tdtypes.length) - i;
- auto vt = new Tuple(vtdim);
- for (size_t k = 0; k < vtdim; k++)
- {
- RootObject o;
- if (k < tiargs.length)
- o = (*tiargs)[i + k];
- else // Pick up default arg
- o = (*tdtypes)[i + k];
- vt.objects[k] = o;
- }
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s... [", ident.toChars());
+ Tuple v = isTuple(oded);
+ assert(v);
- Tuple v = cast(Tuple)(*dedtypes)[j];
- if (v)
- {
- if (!match(v, vt))
- return false;
- }
- else
- (*dedtypes)[j] = vt;
- break;
- }
- else if (!o1)
- break;
-
- Type t1 = isType(o1);
- Dsymbol s1 = isDsymbol(o1);
- Dsymbol s2 = isDsymbol(o2);
- Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
- Expression e2 = isExpression(o2);
- version (none)
+ //printf("|%d| ", v.objects.length);
+ foreach (i, o; v.objects)
{
- Tuple v1 = isTuple(o1);
- Tuple v2 = isTuple(o2);
- if (t1)
- printf("t1 = %s\n", t1.toChars());
- if (t2)
- printf("t2 = %s\n", t2.toChars());
- if (e1)
- printf("e1 = %s\n", e1.toChars());
- if (e2)
- printf("e2 = %s\n", e2.toChars());
- if (s1)
- printf("s1 = %s\n", s1.toChars());
- if (s2)
- printf("s2 = %s\n", s2.toChars());
- if (v1)
- printf("v1 = %s\n", v1.toChars());
- if (v2)
- printf("v2 = %s\n", v2.toChars());
- }
+ if (i)
+ printf(", ");
- if (t1 && t2)
- {
- if (!deduceType(t1, sc, t2, *parameters, *dedtypes))
- return false;
- }
- else if (e1 && e2)
- {
- Le:
- e1 = e1.ctfeInterpret();
+ Dsymbol sa = isDsymbol(o);
+ if (sa)
+ printf("alias: %s", sa.toChars());
+ Type ta = isType(o);
+ if (ta)
+ printf("type: %s", ta.toChars());
+ Expression ea = isExpression(o);
+ if (ea)
+ printf("exp: %s", ea.toChars());
- /* If it is one of the template parameters for this template,
- * we should not attempt to interpret it. It already has a value.
- */
- if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter))
- {
- /*
- * (T:Number!(e2), int e2)
- */
- j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters);
- if (j != IDX_NOTFOUND)
- goto L1;
- // The template parameter was not from this template
- // (it may be from a parent template, for example)
- }
+ assert(!isTuple(o)); // no nested Tuple arguments
+ }
+ printf("]\n");
+ }
- e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417
- e2 = e2.ctfeInterpret();
+ override RootObject specialization()
+ {
+ return null;
+ }
- //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty);
- //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty);
- if (!e1.equals(e2))
- {
- if (!e2.implicitConvTo(e1.type))
- return false;
+ override bool hasDefaultArg()
+ {
+ return false;
+ }
- e2 = e2.implicitCastTo(sc, e1.type);
- e2 = e2.ctfeInterpret();
- if (!e1.equals(e2))
- return false;
- }
- }
- else if (e1 && t2 && t2.ty == Tident)
- {
- j = templateParameterLookup(t2, parameters);
- L1:
- if (j == IDX_NOTFOUND)
- {
- t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
- if (e2)
- goto Le;
- return false;
- }
- if (!(*parameters)[j].matchArg(sc, e1, j, parameters, *dedtypes, null))
- return false;
- }
- else if (s1 && s2)
- {
- Ls:
- if (!s1.equals(s2))
- return false;
- }
- else if (s1 && t2 && t2.ty == Tident)
- {
- j = templateParameterLookup(t2, parameters);
- if (j == IDX_NOTFOUND)
- {
- t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
- if (s2)
- goto Ls;
- return false;
- }
- if (!(*parameters)[j].matchArg(sc, s1, j, parameters, *dedtypes, null))
- return false;
- }
- else
- return false;
+ override void accept(Visitor v)
+ {
+ v.visit(this);
}
- return true;
}
-
/***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- * t = Tested type, if null, returns false.
- * tparams = Template parameters.
- * iStart = Start index of tparams to limit the tested parameters. If it's
- * nonzero, tparams[0..iStart] will be excluded from the test target.
+ * https://dlang.org/spec/template.html#explicit_tmp_instantiation
+ * Given:
+ * foo!(args) =>
+ * name = foo
+ * tiargs = args
*/
-bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
+extern (C++) class TemplateInstance : ScopeDsymbol
{
- return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
-}
+ Identifier name;
-/***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- * t = Tested type, if null, returns false.
- * tparams = Template parameters.
- */
-bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
-{
- bool visitVector(TypeVector t)
+ // Array of Types/Expressions of template
+ // instance arguments [int*, char, 10*10]
+ Objects* tiargs;
+
+ // Array of Types/Expressions corresponding
+ // to TemplateDeclaration.parameters
+ // [int, char, 100]
+ Objects tdtypes;
+
+ // Modules imported by this template instance
+ Modules importedModules;
+
+ Dsymbol tempdecl; // referenced by foo.bar.abc
+ Dsymbol enclosing; // if referencing local symbols, this is the context
+ Dsymbol aliasdecl; // !=null if instance is an alias for its sole member
+
+ /**
+ If this is not null and it has a value that is not the current object,
+ then this field points to an existing template instance
+ and that object has been duplicated into us.
+
+ If this object is a duplicate,
+ the ``memberOf`` field will be set to a root module (passed on CLI).
+
+ This information is useful to deduplicate analysis that may occur
+ after semantic 3 has completed.
+
+ See_Also: memberOf
+ */
+ TemplateInstance inst;
+
+ ScopeDsymbol argsym; // argument symbol table
+ size_t hash; // cached result of toHash()
+
+ /// For function template, these are the function fnames(name and loc of it) and arguments
+ /// Relevant because different resolutions of `auto ref` parameters
+ /// create different template instances even with the same template arguments
+ Expressions* fargs;
+ ArgumentLabels* fnames;
+
+ TemplateInstances* deferred;
+
+ /**
+ If this is not null then this template instance appears in a root module's members.
+
+ Note: This is not useful for determining duplication status of this template instance.
+ Use the field ``inst`` for determining if a template instance has been duplicated into this object.
+
+ See_Also: inst
+ */
+ Module memberOf;
+
+ // Used to determine the instance needs code generation.
+ // Note that these are inaccurate until semantic analysis phase completed.
+ TemplateInstance tinst; // enclosing template instance
+ TemplateInstance tnext; // non-first instantiated instances
+ Module minst; // the top module that instantiated this instance
+
+ private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
+ ubyte inuse; // for recursive expansion detection
+
+ private enum Flag : uint
{
- return t.basetype.reliesOnTemplateParameters(tparams);
+ semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
+ havetempdecl = semantictiargsdone >> 1,
+ gagged = semantictiargsdone >> 2,
+ available = gagged - 1 // always last flag minus one, 1s for all available bits
}
- bool visitAArray(TypeAArray t)
+ extern(D) final @safe @property pure nothrow @nogc
{
- return t.next.reliesOnTemplateParameters(tparams) ||
- t.index.reliesOnTemplateParameters(tparams);
+ ushort nest() const { return _nest & Flag.available; }
+ void nestUp() { assert(nest() < Flag.available); ++_nest; }
+ void nestDown() { assert(nest() > 0); --_nest; }
+ /// has semanticTiargs() been done?
+ bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
+ void semantictiargsdone(bool x)
+ {
+ if (x) _nest |= Flag.semantictiargsdone;
+ else _nest &= ~Flag.semantictiargsdone;
+ }
+ /// if used second constructor
+ bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
+ void havetempdecl(bool x)
+ {
+ if (x) _nest |= Flag.havetempdecl;
+ else _nest &= ~Flag.havetempdecl;
+ }
+ /// if the instantiation is done with error gagging
+ bool gagged() const { return (_nest & Flag.gagged) != 0; }
+ void gagged(bool x)
+ {
+ if (x) _nest |= Flag.gagged;
+ else _nest &= ~Flag.gagged;
+ }
}
- bool visitFunction(TypeFunction t)
+ extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope
{
- foreach (i, fparam; t.parameterList)
+ super(loc, null);
+ static if (LOG)
{
- if (fparam.type.reliesOnTemplateParameters(tparams))
- return true;
+ printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
}
- return t.next.reliesOnTemplateParameters(tparams);
+ this.dsym = DSYM.templateInstance;
+ this.name = ident;
+ this.tiargs = tiargs;
}
- bool visitIdentifier(TypeIdentifier t)
+ /*****************
+ * This constructor is only called when we figured out which function
+ * template to instantiate.
+ */
+ extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope
{
- foreach (tp; tparams)
+ super(loc, null);
+ static if (LOG)
{
- if (tp.ident.equals(t.ident))
- return true;
+ printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
}
- return false;
+ this.dsym = DSYM.templateInstance;
+ this.name = td.ident;
+ this.tiargs = tiargs;
+ this.tempdecl = td;
+ this.semantictiargsdone = true;
+ this.havetempdecl = true;
+ assert(tempdecl._scope);
}
- bool visitInstance(TypeInstance t)
+ extern (D) static Objects* arraySyntaxCopy(Objects* objs)
{
- foreach (tp; tparams)
+ Objects* a = null;
+ if (objs)
{
- if (t.tempinst.name == tp.ident)
- return true;
+ a = new Objects(objs.length);
+ foreach (i, o; *objs)
+ (*a)[i] = objectSyntaxCopy(o);
}
-
- if (t.tempinst.tiargs)
- foreach (arg; *t.tempinst.tiargs)
- {
- if (Type ta = isType(arg))
- {
- if (ta.reliesOnTemplateParameters(tparams))
- return true;
- }
- }
-
- return false;
+ return a;
}
- bool visitTypeof(TypeTypeof t)
+ override TemplateInstance syntaxCopy(Dsymbol s)
{
- //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
- return t.exp.reliesOnTemplateParameters(tparams);
+ TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
+ ti.tiargs = arraySyntaxCopy(tiargs);
+ TemplateDeclaration td;
+ if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
+ td.ScopeDsymbol.syntaxCopy(ti);
+ else
+ ScopeDsymbol.syntaxCopy(ti);
+ return ti;
}
- bool visitTuple(TypeTuple t)
+ override const(char)* kind() const
{
- if (t.arguments)
- foreach (arg; *t.arguments)
- {
- if (arg.type.reliesOnTemplateParameters(tparams))
- return true;
- }
+ return "template instance";
+ }
- return false;
- }
-
- if (!t)
- return false;
-
- Type tb = t.toBasetype();
- switch (tb.ty)
- {
- case Tvector: return visitVector(tb.isTypeVector());
- case Taarray: return visitAArray(tb.isTypeAArray());
- case Tfunction: return visitFunction(tb.isTypeFunction());
- case Tident: return visitIdentifier(tb.isTypeIdentifier());
- case Tinstance: return visitInstance(tb.isTypeInstance());
- case Ttypeof: return visitTypeof(tb.isTypeTypeof());
- case Ttuple: return visitTuple(tb.isTypeTuple());
- case Tenum: return false;
- default: return tb.nextOf().reliesOnTemplateParameters(tparams);
- }
-}
-
-/***********************************************************
- * Check whether the expression representation relies on one or more the template parameters.
- * Params:
- * e = expression to test
- * tparams = Template parameters.
- * Returns:
- * true if it does
- */
-private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
-{
- extern (C++) final class ReliesOnTemplateParameters : Visitor
- {
- alias visit = Visitor.visit;
- public:
- TemplateParameter[] tparams;
- bool result;
-
- extern (D) this(TemplateParameter[] tparams) @safe
- {
- this.tparams = tparams;
- }
-
- override void visit(Expression e)
- {
- //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
- }
-
- override void visit(IdentifierExp e)
- {
- //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- foreach (tp; tparams)
- {
- if (e.ident == tp.ident)
- {
- result = true;
- return;
- }
- }
- }
-
- override void visit(TupleExp e)
- {
- //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.exps)
- {
- foreach (ea; *e.exps)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(ArrayLiteralExp e)
- {
- //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.elements)
- {
- foreach (el; *e.elements)
- {
- el.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(AssocArrayLiteralExp e)
- {
- //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- foreach (ek; *e.keys)
- {
- ek.accept(this);
- if (result)
- return;
- }
- foreach (ev; *e.values)
- {
- ev.accept(this);
- if (result)
- return;
- }
- }
-
- override void visit(StructLiteralExp e)
- {
- //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.elements)
- {
- foreach (ea; *e.elements)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(TypeExp e)
- {
- //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = e.type.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(NewExp e)
- {
- //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.placement)
- e.placement.accept(this);
- if (e.thisexp)
- e.thisexp.accept(this);
- result = e.newtype.reliesOnTemplateParameters(tparams);
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(NewAnonClassExp e)
- {
- //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = true;
- }
-
- override void visit(FuncExp e)
- {
- //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = true;
- }
-
- override void visit(TypeidExp e)
- {
- //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (auto ea = isExpression(e.obj))
- ea.accept(this);
- else if (auto ta = isType(e.obj))
- result = ta.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(TraitsExp e)
- {
- //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- if (e.args)
- {
- foreach (oa; *e.args)
- {
- if (auto ea = isExpression(oa))
- ea.accept(this);
- else if (auto ta = isType(oa))
- result = ta.reliesOnTemplateParameters(tparams);
- if (result)
- return;
- }
- }
- }
-
- override void visit(IsExp e)
- {
- //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- result = e.targ.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(UnaExp e)
- {
- //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.e1.accept(this);
- }
-
- override void visit(DotTemplateInstanceExp e)
- {
- //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.ti.tiargs)
- {
- foreach (oa; *e.ti.tiargs)
- {
- if (auto ea = isExpression(oa))
- ea.accept(this);
- else if (auto ta = isType(oa))
- result = ta.reliesOnTemplateParameters(tparams);
- if (result)
- return;
- }
- }
- }
-
- override void visit(CallExp e)
- {
- //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- {
- ea.accept(this);
- if (result)
- return;
- }
- }
- }
-
- override void visit(CastExp e)
- {
- //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- // e.to can be null for cast() with no type
- if (!result && e.to)
- result = e.to.reliesOnTemplateParameters(tparams);
- }
-
- override void visit(SliceExp e)
- {
- //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.lwr)
- e.lwr.accept(this);
- if (!result && e.upr)
- e.upr.accept(this);
- }
-
- override void visit(IntervalExp e)
- {
- //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.lwr.accept(this);
- if (!result)
- e.upr.accept(this);
- }
-
- override void visit(ArrayExp e)
- {
- //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- visit(e.isUnaExp());
- if (!result && e.arguments)
- {
- foreach (ea; *e.arguments)
- ea.accept(this);
- }
- }
-
- override void visit(BinExp e)
- {
- //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.e1.accept(this);
- if (!result)
- e.e2.accept(this);
- }
-
- override void visit(CondExp e)
- {
- //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
- e.econd.accept(this);
- if (!result)
- visit(e.isBinExp());
- }
- }
-
- scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
- e.accept(v);
- return v.result;
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateParameter
- */
-extern (C++) class TemplateParameter : ASTNode
-{
- Loc loc;
- Identifier ident;
-
- /* True if this is a part of precedent parameter specialization pattern.
- *
- * template A(T : X!TL, alias X, TL...) {}
- * // X and TL are dependent template parameter
- *
- * A dependent template parameter should return MATCH.exact in matchArg()
- * to respect the match level of the corresponding precedent parameter.
- */
- bool dependent;
-
- /* ======================== TemplateParameter =============================== */
- extern (D) this(Loc loc, Identifier ident) @safe
- {
- this.loc = loc;
- this.ident = ident;
- }
-
- TemplateTypeParameter isTemplateTypeParameter()
- {
- return null;
- }
-
- TemplateValueParameter isTemplateValueParameter()
- {
- return null;
- }
-
- TemplateAliasParameter isTemplateAliasParameter()
- {
- return null;
- }
-
- TemplateThisParameter isTemplateThisParameter()
- {
- return null;
- }
-
- TemplateTupleParameter isTemplateTupleParameter()
- {
- return null;
- }
-
- abstract TemplateParameter syntaxCopy();
-
- abstract bool declareParameter(Scope* sc);
-
- abstract void print(RootObject oarg, RootObject oded);
-
- abstract RootObject specialization();
-
- abstract RootObject defaultArg(Loc instLoc, Scope* sc);
-
- abstract bool hasDefaultArg();
-
- override const(char)* toChars() const
- {
- return this.ident.toChars();
- }
-
- override DYNCAST dyncast() const
- {
- return DYNCAST.templateparameter;
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateTypeParameter
- * Syntax:
- * ident : specType = defaultType
- */
-extern (C++) class TemplateTypeParameter : TemplateParameter
-{
- Type specType; // if !=null, this is the type specialization
- Type defaultType;
-
- extern (D) __gshared Type tdummy = null;
-
- extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
- {
- super(loc, ident);
- this.specType = specType;
- this.defaultType = defaultType;
- }
-
- override final TemplateTypeParameter isTemplateTypeParameter()
- {
- return this;
- }
-
- override TemplateTypeParameter syntaxCopy()
- {
- return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
- }
-
- override final bool declareParameter(Scope* sc)
- {
- //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
- override final void print(RootObject oarg, RootObject oded)
- {
- printf(" %s\n", ident.toChars());
-
- Type t = isType(oarg);
- Type ta = isType(oded);
- assert(ta);
-
- if (specType)
- printf("\tSpecialization: %s\n", specType.toChars());
- if (defaultType)
- printf("\tDefault: %s\n", defaultType.toChars());
- printf("\tParameter: %s\n", t ? t.toChars() : "NULL");
- printf("\tDeduced Type: %s\n", ta.toChars());
- }
-
- override final RootObject specialization()
- {
- return specType;
- }
-
- override final RootObject defaultArg(Loc instLoc, Scope* sc)
- {
- Type t = defaultType;
- if (t)
- {
- t = t.syntaxCopy();
- t = t.typeSemantic(loc, sc); // use the parameter loc
- }
- return t;
- }
-
- override final bool hasDefaultArg()
- {
- return defaultType !is null;
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateThisParameter
- * Syntax:
- * this ident : specType = defaultType
- */
-extern (C++) final class TemplateThisParameter : TemplateTypeParameter
-{
- extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
- {
- super(loc, ident, specType, defaultType);
- }
-
- override TemplateThisParameter isTemplateThisParameter()
- {
- return this;
- }
-
- override TemplateThisParameter syntaxCopy()
- {
- return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateValueParameter
- * Syntax:
- * valType ident : specValue = defaultValue
- */
-extern (C++) final class TemplateValueParameter : TemplateParameter
-{
- Type valType;
- Expression specValue;
- Expression defaultValue;
-
- extern (D) __gshared Expression[void*] edummies;
-
- extern (D) this(Loc loc, Identifier ident, Type valType,
- Expression specValue, Expression defaultValue) @safe
- {
- super(loc, ident);
- this.valType = valType;
- this.specValue = specValue;
- this.defaultValue = defaultValue;
- }
-
- override TemplateValueParameter isTemplateValueParameter()
- {
- return this;
- }
-
- override TemplateValueParameter syntaxCopy()
- {
- return new TemplateValueParameter(loc, ident,
- valType.syntaxCopy(),
- specValue ? specValue.syntaxCopy() : null,
- defaultValue ? defaultValue.syntaxCopy() : null);
- }
-
- override bool declareParameter(Scope* sc)
- {
- /*
- Do type semantic earlier.
-
- This means for certain erroneous value parameters
- their "type" can be known earlier and thus a better
- error message given.
-
- For example:
- `template test(x* x) {}`
- now yields "undefined identifier" rather than the opaque
- "variable `x` is used as a type".
- */
- if (valType)
- valType = valType.typeSemantic(loc, sc);
- auto v = new VarDeclaration(loc, valType, ident, null);
- v.storage_class = STC.templateparameter;
- return sc.insert(v) !is null;
- }
-
- override void print(RootObject oarg, RootObject oded)
- {
- printf(" %s\n", ident.toChars());
- Expression ea = isExpression(oded);
- if (specValue)
- printf("\tSpecialization: %s\n", specValue.toChars());
- printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL");
- }
-
- override RootObject specialization()
- {
- return specValue;
- }
-
- override RootObject defaultArg(Loc instLoc, Scope* sc)
- {
- Expression e = defaultValue;
- if (!e)
- return null;
-
- e = e.syntaxCopy();
- Scope* sc2 = sc.push();
- sc2.inDefaultArg = true;
- e = e.expressionSemantic(sc2);
- sc2.pop();
- if (e is null)
- return null;
- if (auto te = e.isTemplateExp())
- {
- assert(sc && sc.tinst);
- if (te.td == sc.tinst.tempdecl)
- {
- // defaultValue is a reference to its template declaration
- // i.e: `template T(int arg = T)`
- // Raise error now before calling resolveProperties otherwise we'll
- // start looping on the expansion of the template instance.
- auto td = sc.tinst.tempdecl;
- .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars);
- return ErrorExp.get();
- }
- }
- if ((e = resolveProperties(sc, e)) is null)
- return null;
- e = e.resolveLoc(instLoc, sc); // use the instantiated loc
- e = e.optimize(WANTvalue);
-
- return e;
- }
-
- override bool hasDefaultArg()
- {
- return defaultValue !is null;
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateAliasParameter
- * Syntax:
- * specType ident : specAlias = defaultAlias
- */
-extern (C++) final class TemplateAliasParameter : TemplateParameter
-{
- Type specType;
- RootObject specAlias;
- RootObject defaultAlias;
-
- extern (D) __gshared Dsymbol sdummy = null;
-
- extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe
- {
- super(loc, ident);
- this.specType = specType;
- this.specAlias = specAlias;
- this.defaultAlias = defaultAlias;
- }
-
- override TemplateAliasParameter isTemplateAliasParameter()
- {
- return this;
- }
-
- override TemplateAliasParameter syntaxCopy()
- {
- return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
- }
-
- override bool declareParameter(Scope* sc)
- {
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
- override void print(RootObject oarg, RootObject oded)
- {
- printf(" %s\n", ident.toChars());
- Dsymbol sa = isDsymbol(oded);
- assert(sa);
- printf("\tParameter alias: %s\n", sa.toChars());
- }
-
- override RootObject specialization()
- {
- return specAlias;
- }
-
- override RootObject defaultArg(Loc instLoc, Scope* sc)
- {
- RootObject da = defaultAlias;
- if (auto ta = isType(defaultAlias))
- {
- switch (ta.ty)
- {
- // If the default arg is a template, instantiate for each type
- case Tinstance :
- // same if the default arg is a mixin, traits, typeof
- // since the content might rely on a previous parameter
- // (https://issues.dlang.org/show_bug.cgi?id=23686)
- case Tmixin, Ttypeof, Ttraits :
- da = ta.syntaxCopy();
- break;
- default:
- }
- }
-
- RootObject o = aliasParameterSemantic(loc, sc, da, null); // use the parameter loc
- return o;
- }
-
- override bool hasDefaultArg()
- {
- return defaultAlias !is null;
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateSequenceParameter
- * Syntax:
- * ident ...
- */
-extern (C++) final class TemplateTupleParameter : TemplateParameter
-{
- extern (D) this(Loc loc, Identifier ident) @safe
- {
- super(loc, ident);
- }
-
- override TemplateTupleParameter isTemplateTupleParameter()
- {
- return this;
- }
-
- override TemplateTupleParameter syntaxCopy()
- {
- return new TemplateTupleParameter(loc, ident);
- }
-
- override bool declareParameter(Scope* sc)
- {
- auto ti = new TypeIdentifier(loc, ident);
- Declaration ad = new AliasDeclaration(loc, ident, ti);
- return sc.insert(ad) !is null;
- }
-
- override void print(RootObject oarg, RootObject oded)
- {
- printf(" %s... [", ident.toChars());
- Tuple v = isTuple(oded);
- assert(v);
-
- //printf("|%d| ", v.objects.length);
- foreach (i, o; v.objects)
- {
- if (i)
- printf(", ");
-
- Dsymbol sa = isDsymbol(o);
- if (sa)
- printf("alias: %s", sa.toChars());
- Type ta = isType(o);
- if (ta)
- printf("type: %s", ta.toChars());
- Expression ea = isExpression(o);
- if (ea)
- printf("exp: %s", ea.toChars());
-
- assert(!isTuple(o)); // no nested Tuple arguments
- }
- printf("]\n");
- }
-
- override RootObject specialization()
- {
- return null;
- }
-
- override RootObject defaultArg(Loc instLoc, Scope* sc)
- {
- return null;
- }
-
- override bool hasDefaultArg()
- {
- return false;
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#explicit_tmp_instantiation
- * Given:
- * foo!(args) =>
- * name = foo
- * tiargs = args
- */
-extern (C++) class TemplateInstance : ScopeDsymbol
-{
- Identifier name;
-
- // Array of Types/Expressions of template
- // instance arguments [int*, char, 10*10]
- Objects* tiargs;
-
- // Array of Types/Expressions corresponding
- // to TemplateDeclaration.parameters
- // [int, char, 100]
- Objects tdtypes;
-
- // Modules imported by this template instance
- Modules importedModules;
-
- Dsymbol tempdecl; // referenced by foo.bar.abc
- Dsymbol enclosing; // if referencing local symbols, this is the context
- Dsymbol aliasdecl; // !=null if instance is an alias for its sole member
-
- /**
- If this is not null and it has a value that is not the current object,
- then this field points to an existing template instance
- and that object has been duplicated into us.
-
- If this object is a duplicate,
- the ``memberOf`` field will be set to a root module (passed on CLI).
-
- This information is useful to deduplicate analysis that may occur
- after semantic 3 has completed.
-
- See_Also: memberOf
- */
- TemplateInstance inst;
-
- ScopeDsymbol argsym; // argument symbol table
- size_t hash; // cached result of toHash()
-
- /// For function template, these are the function fnames(name and loc of it) and arguments
- /// Relevant because different resolutions of `auto ref` parameters
- /// create different template instances even with the same template arguments
- Expressions* fargs;
- ArgumentLabels* fnames;
-
- TemplateInstances* deferred;
-
- /**
- If this is not null then this template instance appears in a root module's members.
-
- Note: This is not useful for determining duplication status of this template instance.
- Use the field ``inst`` for determining if a template instance has been duplicated into this object.
-
- See_Also: inst
- */
- Module memberOf;
-
- // Used to determine the instance needs code generation.
- // Note that these are inaccurate until semantic analysis phase completed.
- TemplateInstance tinst; // enclosing template instance
- TemplateInstance tnext; // non-first instantiated instances
- Module minst; // the top module that instantiated this instance
-
- private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
- ubyte inuse; // for recursive expansion detection
-
- private enum Flag : uint
- {
- semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
- havetempdecl = semantictiargsdone >> 1,
- gagged = semantictiargsdone >> 2,
- available = gagged - 1 // always last flag minus one, 1s for all available bits
- }
-
- extern(D) final @safe @property pure nothrow @nogc
- {
- ushort nest() const { return _nest & Flag.available; }
- void nestUp() { assert(nest() < Flag.available); ++_nest; }
- void nestDown() { assert(nest() > 0); --_nest; }
- /// has semanticTiargs() been done?
- bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
- void semantictiargsdone(bool x)
- {
- if (x) _nest |= Flag.semantictiargsdone;
- else _nest &= ~Flag.semantictiargsdone;
- }
- /// if used second constructor
- bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
- void havetempdecl(bool x)
- {
- if (x) _nest |= Flag.havetempdecl;
- else _nest &= ~Flag.havetempdecl;
- }
- /// if the instantiation is done with error gagging
- bool gagged() const { return (_nest & Flag.gagged) != 0; }
- void gagged(bool x)
- {
- if (x) _nest |= Flag.gagged;
- else _nest &= ~Flag.gagged;
- }
- }
-
- extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope
- {
- super(loc, null);
- static if (LOG)
- {
- printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
- }
- this.dsym = DSYM.templateInstance;
- this.name = ident;
- this.tiargs = tiargs;
- }
-
- /*****************
- * This constructor is only called when we figured out which function
- * template to instantiate.
- */
- extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope
- {
- super(loc, null);
- static if (LOG)
- {
- printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
- }
- this.dsym = DSYM.templateInstance;
- this.name = td.ident;
- this.tiargs = tiargs;
- this.tempdecl = td;
- this.semantictiargsdone = true;
- this.havetempdecl = true;
- assert(tempdecl._scope);
- }
-
- extern (D) static Objects* arraySyntaxCopy(Objects* objs)
- {
- Objects* a = null;
- if (objs)
- {
- a = new Objects(objs.length);
- foreach (i, o; *objs)
- (*a)[i] = objectSyntaxCopy(o);
- }
- return a;
- }
-
- override TemplateInstance syntaxCopy(Dsymbol s)
- {
- TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
- ti.tiargs = arraySyntaxCopy(tiargs);
- TemplateDeclaration td;
- if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
- td.ScopeDsymbol.syntaxCopy(ti);
- else
- ScopeDsymbol.syntaxCopy(ti);
- return ti;
- }
-
- // resolve real symbol
- override final Dsymbol toAlias()
- {
- static if (LOG)
- {
- printf("TemplateInstance.toAlias()\n");
- }
- if (!inst)
- {
- // Maybe we can resolve it
- if (_scope)
- {
- dsymbolSemantic(this, _scope);
- }
- if (!inst)
- {
- .error(loc, "%s `%s` cannot resolve forward reference", kind, toPrettyChars);
- errors = true;
- return this;
- }
- }
-
- if (inst != this)
- return inst.toAlias();
-
- if (aliasdecl)
- {
- return aliasdecl.toAlias();
- }
-
- return inst;
- }
-
- override const(char)* kind() const
- {
- return "template instance";
- }
-
- override final const(char)* toPrettyCharsHelper()
- {
- OutBuffer buf;
- toCBufferInstance(this, buf, true);
- return buf.extractChars();
- }
-
- /**************************************
- * Given an error instantiating the TemplateInstance,
- * give the nested TemplateInstance instantiations that got
- * us here. Those are a list threaded into the nested scopes.
- * Params:
- * cl = classification of this trace as printing either errors or deprecations
- * max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit)
- */
- extern(D) final void printInstantiationTrace(Classification cl = Classification.error,
- const(uint) max_shown = global.params.v.errorSupplementCount())
- {
- if (global.gag)
- return;
-
- // Print full trace for verbose mode, otherwise only short traces
- const(char)* format = "instantiated from here: `%s`";
-
- // This returns a function pointer
- scope printFn = () {
- final switch (cl)
- {
- case Classification.error:
- return &errorSupplemental;
- case Classification.deprecation:
- return &deprecationSupplemental;
- case Classification.gagged, Classification.tip, Classification.warning:
- assert(0);
- }
- }();
-
- // determine instantiation depth and number of recursive instantiations
- int n_instantiations = 1;
- int n_totalrecursions = 0;
- for (TemplateInstance cur = this; cur; cur = cur.tinst)
- {
- ++n_instantiations;
- // Set error here as we don't want it to depend on the number of
- // entries that are being printed.
- if (cl == Classification.error ||
- (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) ||
- (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
- cur.errors = true;
-
- // If two instantiations use the same declaration, they are recursive.
- // (this works even if they are instantiated from different places in the
- // same template).
- // In principle, we could also check for multiple-template recursion, but it's
- // probably not worthwhile.
- if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
- ++n_totalrecursions;
- }
-
- if (n_instantiations <= max_shown)
- {
- for (TemplateInstance cur = this; cur; cur = cur.tinst)
- printFn(cur.loc, format, cur.toErrMsg());
- }
- else if (n_instantiations - n_totalrecursions <= max_shown)
- {
- // By collapsing recursive instantiations into a single line,
- // we can stay under the limit.
- int recursionDepth = 0;
- for (TemplateInstance cur = this; cur; cur = cur.tinst)
- {
- if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
- {
- ++recursionDepth;
- }
- else
- {
- if (recursionDepth)
- printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
- else
- printFn(cur.loc, format, cur.toChars());
- recursionDepth = 0;
- }
- }
- }
- else
- {
- // Even after collapsing the recursions, the depth is too deep.
- // Just display the first few and last few instantiations.
- uint i = 0;
- for (TemplateInstance cur = this; cur; cur = cur.tinst)
- {
- if (i == max_shown / 2)
- printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
-
- if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
- printFn(cur.loc, format, cur.toChars());
- ++i;
- }
- }
- }
-
- /*************************************
- * Lazily generate identifier for template instance.
- * This is because 75% of the ident's are never needed.
- */
- override final Identifier getIdent()
- {
- if (!ident && inst && !errors)
- ident = genIdent(tiargs); // need an identifier for name mangling purposes.
- return ident;
- }
-
- /*************************************
- * Compare proposed template instantiation with existing template instantiation.
- * Note that this is not commutative because of the auto ref check.
- * Params:
- * ti = existing template instantiation
- * Returns:
- * true for match
- */
- final bool equalsx(TemplateInstance ti)
- {
- //printf("this = %p, ti = %p\n", this, ti);
- assert(tdtypes.length == ti.tdtypes.length);
-
- // Nesting must match
- if (enclosing != ti.enclosing)
- {
- //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
- return false;
- }
- //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
-
- if (!arrayObjectMatch(tdtypes, ti.tdtypes))
- return false;
-
- /* Template functions may have different instantiations based on
- * "auto ref" parameters.
- */
- auto fd = ti.toAlias().isFuncDeclaration();
- if (!fd)
- return true;
- if (fd.errors)
- return true;
-
- auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
- ArgumentList(this.fargs, this.fnames), null);
-
- // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
- // In that case, equalsx returns true to prevent endless template instantiations
- // However, it can also mean the function was explicitly instantiated
- // without function arguments: fail_compilation/fail14669
- // Hence the following check:
- if (this.fargs && !resolvedArgs)
- return true;
-
- Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
-
- auto fparameters = fd.getParameterList();
- size_t nfparams = fparameters.length; // Num function parameters
- for (size_t j = 0; j < nfparams; j++)
- {
- Parameter fparam = fparameters[j];
- if (!(fparam.storageClass & STC.autoref) ) // if "auto ref"
- continue;
-
- Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
- // resolveNamedArgs strips trailing nulls / default params
- // when it doesn't anymore, the ternary can be replaced with:
- // assert(j < resolvedArgs.length);
- if (!farg)
- farg = fparam.defaultArg;
- if (!farg)
- return false;
- if (farg.isLvalue())
- {
- if (!(fparam.storageClass & STC.ref_))
- return false; // auto ref's don't match
- }
- else
- {
- if (fparam.storageClass & STC.ref_)
- return false; // auto ref's don't match
- }
- }
- return true;
- }
-
- extern (D) final size_t toHash()
- {
- if (!hash)
- {
- hash = cast(size_t)cast(void*)enclosing;
- hash += arrayObjectHash(tdtypes);
- hash += hash == 0;
- }
- return hash;
- }
-
- /**
- Returns: true if the instances' innards are discardable.
-
- The idea of this function is to see if the template instantiation
- can be 100% replaced with its eponymous member. All other members
- can be discarded, even in the compiler to free memory (for example,
- the template could be expanded in a region allocator, deemed trivial,
- the end result copied back out independently and the entire region freed),
- and can be elided entirely from the binary.
-
- The current implementation affects code that generally looks like:
-
- ---
- template foo(args...) {
- some_basic_type_or_string helper() { .... }
- enum foo = helper();
- }
- ---
-
- since it was the easiest starting point of implementation but it can and
- should be expanded more later.
- */
- final bool isDiscardable()
- {
- if (aliasdecl is null)
- return false;
-
- auto v = aliasdecl.isVarDeclaration();
- if (v is null)
- return false;
-
- if (!(v.storage_class & STC.manifest))
- return false;
-
- // Currently only doing basic types here because it is the easiest proof-of-concept
- // implementation with minimal risk of side effects, but it could likely be
- // expanded to any type that already exists outside this particular instance.
- if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null)))
- return false;
-
- // Static ctors and dtors, even in an eponymous enum template, are still run,
- // so if any of them are in here, we'd better not assume it is trivial lest
- // we break useful code
- foreach(member; *members)
- {
- if(member.hasStaticCtorOrDtor())
- return false;
- if(member.isStaticDtorDeclaration())
- return false;
- if(member.isStaticCtorDeclaration())
- return false;
- }
-
- // but if it passes through this gauntlet... it should be fine. D code will
- // see only the eponymous member, outside stuff can never access it, even through
- // reflection; the outside world ought to be none the wiser. Even dmd should be
- // able to simply free the memory of everything except the final result.
-
- return true;
- }
-
-
- /***********************************************
- * Returns true if this is not instantiated in non-root module, and
- * is a part of non-speculative instantiatiation.
- *
- * Note: minst does not stabilize until semantic analysis is completed,
- * so don't call this function during semantic analysis to return precise result.
- */
- final bool needsCodegen()
- {
- //printf("needsCodegen() %s\n", toChars());
-
- // minst is finalized after the 1st invocation.
- // tnext is only needed for the 1st invocation and
- // cleared for further invocations.
- TemplateInstance tnext = this.tnext;
- TemplateInstance tinst = this.tinst;
- this.tnext = null;
-
- // Don't do codegen if the instance has errors,
- // is a dummy instance (see evaluateConstraint),
- // or is determined to be discardable.
- if (errors || inst is null || inst.isDiscardable())
- {
- minst = null; // mark as speculative
- return false;
- }
-
- // This should only be called on the primary instantiation.
- assert(this is inst);
-
- if (global.params.allInst)
- {
- // Do codegen if there is an instantiation from a root module, to maximize link-ability.
- static ThreeState needsCodegenAllInst(TemplateInstance tithis, TemplateInstance tinst)
- {
- // Do codegen if `this` is instantiated from a root module.
- if (tithis.minst && tithis.minst.isRoot())
- return ThreeState.yes;
-
- // Do codegen if the ancestor needs it.
- if (tinst && tinst.inst && tinst.inst.needsCodegen())
- {
- tithis.minst = tinst.inst.minst; // cache result
- assert(tithis.minst);
- assert(tithis.minst.isRoot());
- return ThreeState.yes;
- }
- return ThreeState.none;
- }
-
- if (const needsCodegen = needsCodegenAllInst(this, tinst))
- return needsCodegen == ThreeState.yes ? true : false;
-
- // Do codegen if a sibling needs it.
- for (; tnext; tnext = tnext.tnext)
- {
- const needsCodegen = needsCodegenAllInst(tnext, tnext.tinst);
- if (needsCodegen == ThreeState.yes)
- {
- minst = tnext.minst; // cache result
- assert(minst);
- assert(minst.isRoot());
- return true;
- }
- else if (!minst && tnext.minst)
- {
- minst = tnext.minst; // cache result from non-speculative sibling
- // continue searching
- }
- else if (needsCodegen != ThreeState.none)
- break;
- }
-
- // Elide codegen because there's no instantiation from any root modules.
- return false;
- }
-
- // Prefer instantiations from non-root modules, to minimize object code size.
-
- /* If a TemplateInstance is ever instantiated from a non-root module,
- * we do not have to generate code for it,
- * because it will be generated when the non-root module is compiled.
- *
- * But, if the non-root 'minst' imports any root modules, it might still need codegen.
- *
- * The problem is if A imports B, and B imports A, and both A
- * and B instantiate the same template, does the compilation of A
- * or the compilation of B do the actual instantiation?
- *
- * See https://issues.dlang.org/show_bug.cgi?id=2500.
- *
- * => Elide codegen if there is at least one instantiation from a non-root module
- * which doesn't import any root modules.
- */
- static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst)
- {
- // If the ancestor isn't speculative,
- // 1. do codegen if the ancestor needs it
- // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree)
- if (tinst && tinst.inst)
- {
- tinst = tinst.inst;
- const needsCodegen = tinst.needsCodegen(); // sets tinst.minst
- if (tinst.minst) // not speculative
- {
- tithis.minst = tinst.minst; // cache result
- return needsCodegen ? ThreeState.yes : ThreeState.no;
- }
- }
-
- // Elide codegen if `this` doesn't need it.
- if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports())
- return ThreeState.no;
-
- return ThreeState.none;
- }
-
- if (const needsCodegen = needsCodegenRootOnly(this, tinst))
- return needsCodegen == ThreeState.yes ? true : false;
-
- // Elide codegen if a (non-speculative) sibling doesn't need it.
- for (; tnext; tnext = tnext.tnext)
- {
- const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst
- if (tnext.minst) // not speculative
- {
- if (needsCodegen == ThreeState.no)
- {
- minst = tnext.minst; // cache result
- assert(!minst.isRoot() && !minst.rootImports());
- return false;
- }
- else if (!minst)
- {
- minst = tnext.minst; // cache result from non-speculative sibling
- // continue searching
- }
- else if (needsCodegen != ThreeState.none)
- break;
- }
- }
-
- // Unless `this` is still speculative (=> all further siblings speculative too),
- // do codegen because we found no guaranteed-codegen'd non-root instantiation.
- return minst !is null;
- }
-
- /**********************************************
- * Find template declaration corresponding to template instance.
- *
- * Returns:
- * false if finding fails.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * any members of this object won't be modified, and repetition call will
- * reproduce same error.
- */
- extern (D) final bool findTempDecl(Scope* sc, WithScopeSymbol* pwithsym)
+ override final const(char)* toPrettyCharsHelper()
{
- if (pwithsym)
- *pwithsym = null;
-
- if (havetempdecl)
- return true;
-
- //printf("TemplateInstance.findTempDecl() %s\n", toChars());
- if (!tempdecl)
- {
- /* Given:
- * foo!( ... )
- * figure out which TemplateDeclaration foo refers to.
- */
- Identifier id = name;
- Dsymbol scopesym;
- Dsymbol s = sc.search(loc, id, scopesym);
- if (!s)
- {
- s = sc.search_correct(id);
- if (s)
- .error(loc, "%s `%s` template `%s` is not defined, did you mean %s?", kind, toPrettyChars, id.toChars(), s.toChars());
- else
- .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars());
- return false;
- }
- static if (LOG)
- {
- printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
- if (s.parent)
- printf("s.parent = '%s'\n", s.parent.toChars());
- }
- if (pwithsym)
- *pwithsym = scopesym.isWithScopeSymbol();
-
- /* We might have found an alias within a template when
- * we really want the template.
- */
- TemplateInstance ti;
- if (s.parent && (ti = s.parent.isTemplateInstance()) !is null)
- {
- if (ti.tempdecl && ti.tempdecl.ident == id)
- {
- /* This is so that one can refer to the enclosing
- * template, even if it has the same name as a member
- * of the template, if it has a !(arguments)
- */
- TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
- assert(td);
- if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td.overroot; // then get the start
- s = td;
- }
- }
-
- // The template might originate from a selective import which implies that
- // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
- // This is the last place where we see the deprecated alias because it is
- // stripped below, so check if the selective import was deprecated.
- // See https://issues.dlang.org/show_bug.cgi?id=20840.
- if (s.isAliasDeclaration())
- s.checkDeprecated(this.loc, sc);
-
- if (!updateTempDecl(sc, s))
- {
- return false;
- }
- }
- assert(tempdecl);
-
- // Look for forward references
- auto tovers = tempdecl.isOverloadSet();
- foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
- {
- Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
- int r = overloadApply(dstart, (Dsymbol s)
- {
- auto td = s.isTemplateDeclaration();
- if (!td)
- return 0;
-
- if (td.semanticRun == PASS.initial)
- {
- if (td._scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- Ungag ungag = td.ungagSpeculative();
- td.dsymbolSemantic(td._scope);
- }
- if (td.semanticRun == PASS.initial)
- {
- .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars,
- toChars(), td.toChars());
- return 1;
- }
- }
- return 0;
- });
- if (r)
- return false;
- }
- return true;
+ OutBuffer buf;
+ toCBufferInstance(this, buf, true);
+ return buf.extractChars();
}
- /**********************************************
- * Confirm s is a valid template, then store it.
- * Input:
- * sc
- * s candidate symbol of template. It may be:
- * TemplateDeclaration
- * FuncDeclaration with findTemplateDeclRoot() != NULL
- * OverloadSet which contains candidates
- * Returns:
- * true if updating succeeds.
+ /**************************************
+ * Given an error instantiating the TemplateInstance,
+ * give the nested TemplateInstance instantiations that got
+ * us here. Those are a list threaded into the nested scopes.
+ * Params:
+ * cl = classification of this trace as printing either errors or deprecations
+ * max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit)
*/
- extern (D) final bool updateTempDecl(Scope* sc, Dsymbol s)
+ extern(D) final void printInstantiationTrace(Classification cl = Classification.error,
+ const(uint) max_shown = global.params.v.errorSupplementCount())
{
- if (!s)
- return tempdecl !is null;
-
- Identifier id = name;
- s = s.toAlias();
-
- /* If an OverloadSet, look for a unique member that is a template declaration
- */
- if (OverloadSet os = s.isOverloadSet())
- {
- s = null;
- foreach (s2; os.a)
- {
- if (FuncDeclaration f = s2.isFuncDeclaration())
- s2 = f.findTemplateDeclRoot();
- else
- s2 = s2.isTemplateDeclaration();
- if (s2)
- {
- if (s)
- {
- tempdecl = os;
- return true;
- }
- s = s2;
- }
- }
- if (!s)
- {
- .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars());
- return false;
- }
- }
-
- if (OverDeclaration od = s.isOverDeclaration())
- {
- tempdecl = od; // TODO: more strict check
- return true;
- }
-
- /* It should be a TemplateDeclaration, not some other symbol
- */
- if (FuncDeclaration f = s.isFuncDeclaration())
- tempdecl = f.findTemplateDeclRoot();
- else
- tempdecl = s.isTemplateDeclaration();
-
- // We're done
- if (tempdecl)
- return true;
+ if (global.gag)
+ return;
- // Error already issued, just return `false`
- if (!s.parent && global.errors)
- return false;
+ // Print full trace for verbose mode, otherwise only short traces
+ const(char)* format = "instantiated from here: `%s`";
- if (!s.parent && s.getType())
- {
- Dsymbol s2 = s.getType().toDsymbol(sc);
- if (!s2)
- {
- .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind());
- return false;
- }
- // because s can be the alias created for a TemplateParameter
- const AliasDeclaration ad = s.isAliasDeclaration();
- version (none)
+ // This returns a function pointer
+ scope printFn = () {
+ final switch (cl)
{
- if (ad && ad.isAliasedTemplateParameter())
- printf("`%s` is an alias created from a template parameter\n", s.toChars());
+ case Classification.error:
+ return &errorSupplemental;
+ case Classification.deprecation:
+ return &deprecationSupplemental;
+ case Classification.gagged, Classification.tip, Classification.warning:
+ assert(0);
}
- if (!ad || !ad.isAliasedTemplateParameter())
- s = s2;
- }
-
- TemplateInstance ti = s.parent ? s.parent.isTemplateInstance() : null;
-
- /* This avoids the VarDeclaration.toAlias() which runs semantic() too soon
- */
- static bool matchId(TemplateInstance ti, Identifier id)
- {
- if (ti.aliasdecl && ti.aliasdecl.isVarDeclaration())
- return ti.aliasdecl.isVarDeclaration().ident == id;
- return ti.toAlias().ident == id;
- }
-
- if (ti && (ti.name == s.ident || matchId(ti, s.ident)) && ti.tempdecl)
- {
- /* This is so that one can refer to the enclosing
- * template, even if it has the same name as a member
- * of the template, if it has a !(arguments)
- */
- TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
- assert(td);
- if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td.overroot; // then get the start
- tempdecl = td;
- return true;
- }
- else
- {
- .error(loc, "%s `%s` `%s` is not a template declaration, it is a %s", kind, toPrettyChars, id.toChars(), s.kind());
- return false;
- }
- }
-
- /**********************************
- * Run semantic of tiargs as arguments of template.
- * Input:
- * loc
- * sc
- * 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(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null)
- {
- // Run semantic on each argument, place results in tiargs[]
- //printf("+TemplateInstance.semanticTiargs()\n");
- if (!tiargs)
- return true;
- bool err = false;
-
- // The arguments are not treated as part of a default argument,
- // because they are evaluated at compile time.
- const inCondition = sc.condition;
- sc = sc.push();
- sc.inDefaultArg = false;
-
- // https://issues.dlang.org/show_bug.cgi?id=24699
- sc.condition = inCondition;
+ }();
- for (size_t j = 0; j < tiargs.length; j++)
+ // determine instantiation depth and number of recursive instantiations
+ int n_instantiations = 1;
+ int n_totalrecursions = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
{
- RootObject o = (*tiargs)[j];
- Type ta = isType(o);
- Expression ea = isExpression(o);
- Dsymbol sa = isDsymbol(o);
-
- //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
- if (ta)
- {
- //printf("type %s\n", ta.toChars());
-
- // It might really be an Expression or an Alias
- ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0);
- if (ea)
- goto Lexpr;
- if (sa)
- goto Ldsym;
- if (ta is null)
- {
- assert(global.errors);
- ta = Type.terror;
- }
-
- Ltype:
- if (TypeTuple tt = ta.isTypeTuple())
- {
- // Expand tuple
- size_t dim = tt.arguments.length;
- tiargs.remove(j);
- if (dim)
- {
- tiargs.reserve(dim);
- foreach (i, arg; *tt.arguments)
- {
- if (flags & 2 && (arg.storageClass & STC.parameter))
- tiargs.insert(j + i, arg);
- else
- tiargs.insert(j + i, arg.type);
- }
- }
- j--;
- continue;
- }
- if (ta.ty == Terror)
- {
- err = true;
- continue;
- }
- (*tiargs)[j] = ta.merge2();
- }
- else if (ea)
- {
- Lexpr:
- //printf("+[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
- if (flags & 1) // only used by __traits
- {
- ea = ea.expressionSemantic(sc);
-
- // must not interpret the args, excepting template parameters
- if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter))
- {
- ea = ea.optimize(WANTvalue);
- }
- }
- else
- {
- sc = sc.startCTFE();
- ea = ea.expressionSemantic(sc);
- sc = sc.endCTFE();
-
- if (auto varExp = ea.isVarExp())
- {
- /* If the parameter is a function that is not called
- * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
- * then it is a dsymbol, not the return value of `func()`
- */
- Declaration vd = varExp.var;
- if (auto fd = vd.isFuncDeclaration())
- {
- sa = fd;
- goto Ldsym;
- }
- /* Otherwise skip substituting a const var with
- * its initializer. The problem is the initializer won't
- * match with an 'alias' parameter. Instead, do the
- * const substitution in TemplateValueParameter.matchArg().
- */
- }
- else if (definitelyValueParameter(ea))
- {
- if (ea.checkValue()) // check void expression
- ea = ErrorExp.get();
- const olderrs = global.errors;
- ea = ea.ctfeInterpret();
- if (global.errors != olderrs)
- ea = ErrorExp.get();
- }
- }
- //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
- if (TupleExp te = ea.isTupleExp())
- {
- // Expand tuple
- size_t dim = te.exps.length;
- tiargs.remove(j);
- if (dim)
- {
- tiargs.reserve(dim);
- foreach (i, exp; *te.exps)
- tiargs.insert(j + i, exp);
- }
- j--;
- continue;
- }
- if (ea.op == EXP.error)
- {
- err = true;
- continue;
- }
- (*tiargs)[j] = ea;
+ ++n_instantiations;
+ // Set error here as we don't want it to depend on the number of
+ // entries that are being printed.
+ if (cl == Classification.error ||
+ (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) ||
+ (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
+ cur.errors = true;
- if (ea.op == EXP.type)
- {
- ta = ea.type;
- goto Ltype;
- }
- if (ea.op == EXP.scope_)
- {
- sa = ea.isScopeExp().sds;
- goto Ldsym;
- }
- if (FuncExp fe = ea.isFuncExp())
- {
- /* A function literal, that is passed to template and
- * already semanticed as function pointer, never requires
- * outer frame. So convert it to global function is valid.
- */
- if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer)
- {
- // change to non-nested
- fe.fd.tok = TOK.function_;
- fe.fd.vthis = null;
- }
- else if (fe.td)
- {
- /* If template argument is a template lambda,
- * get template declaration itself. */
- //sa = fe.td;
- //goto Ldsym;
- }
- }
- if (ea.op == EXP.dotVariable && !(flags & 1))
- {
- // translate expression to dsymbol.
- sa = ea.isDotVarExp().var;
- goto Ldsym;
- }
- if (auto te = ea.isTemplateExp())
- {
- sa = te.td;
- goto Ldsym;
- }
- if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1))
- {
- // translate expression to dsymbol.
- sa = ea.isDotTemplateExp().td;
- goto Ldsym;
- }
- if (auto de = ea.isDotExp())
- {
- if (auto se = de.e2.isScopeExp())
- {
- sa = se.sds;
- goto Ldsym;
- }
- }
- }
- else if (sa)
- {
- Ldsym:
- //printf("dsym %s %s\n", sa.kind(), sa.toChars());
- if (sa.errors)
- {
- err = true;
- continue;
- }
+ // If two instantiations use the same declaration, they are recursive.
+ // (this works even if they are instantiated from different places in the
+ // same template).
+ // In principle, we could also check for multiple-template recursion, but it's
+ // probably not worthwhile.
+ if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
+ ++n_totalrecursions;
+ }
- 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);
- j--;
- continue;
- }
- if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration())
+ if (n_instantiations <= max_shown)
+ {
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ printFn(cur.loc, format, cur.toErrMsg());
+ }
+ else if (n_instantiations - n_totalrecursions <= max_shown)
+ {
+ // By collapsing recursive instantiations into a single line,
+ // we can stay under the limit.
+ int recursionDepth = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
{
- FuncDeclaration f = fa.toAliasFunc();
- if (!fa.hasOverloads && f.isUnique())
- {
- // Strip FuncAlias only when the aliased function
- // does not have any overloads.
- sa = f;
- }
+ ++recursionDepth;
}
- (*tiargs)[j] = sa;
-
- TemplateDeclaration td = sa.isTemplateDeclaration();
- if (td && td.semanticRun == PASS.initial && td.literal)
+ else
{
- td.dsymbolSemantic(sc);
+ if (recursionDepth)
+ printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
+ else
+ printFn(cur.loc, format, cur.toChars());
+ recursionDepth = 0;
}
- FuncDeclaration fd = sa.isFuncDeclaration();
- if (fd)
- functionSemantic(fd);
- }
- else if (isParameter(o))
- {
- }
- else
- {
- assert(0);
}
- //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
}
- sc.pop();
- version (none)
+ else
{
- printf("-TemplateInstance.semanticTiargs()\n");
- for (size_t j = 0; j < tiargs.length; j++)
+ // Even after collapsing the recursions, the depth is too deep.
+ // Just display the first few and last few instantiations.
+ uint i = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
{
- RootObject o = (*tiargs)[j];
- Type ta = isType(o);
- Expression ea = isExpression(o);
- Dsymbol sa = isDsymbol(o);
- Tuple va = isTuple(o);
- printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va);
+ if (i == max_shown / 2)
+ printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
+
+ if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
+ printFn(cur.loc, format, cur.toChars());
+ ++i;
}
}
- return !err;
}
- /**********************************
- * Run semantic on the elements of tiargs.
- * Input:
- * sc
- * Returns:
- * false if one or more arguments have errors.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * all elements of tiargs won't be modified.
+ /*************************************
+ * Lazily generate identifier for template instance.
+ * This is because 75% of the ident's are never needed.
*/
- extern (D) final bool semanticTiargs(Scope* sc)
+ override final Identifier getIdent()
{
- //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
- if (semantictiargsdone)
- return true;
- if (semanticTiargs(loc, sc, tiargs, 0))
- {
- // cache the result iff semantic analysis succeeded entirely
- semantictiargsdone = 1;
- return true;
- }
- return false;
+ if (!ident && inst && !errors)
+ ident = genIdent(tiargs); // need an identifier for name mangling purposes.
+ return ident;
}
- /**********************************
- * Find the TemplateDeclaration that matches this TemplateInstance best.
- *
+ /*************************************
+ * Compare proposed template instantiation with existing template instantiation.
+ * Note that this is not commutative because of the auto ref check.
* Params:
- * sc = the scope this TemplateInstance resides in
- * argumentList = function arguments in case of a template function
- *
+ * ti = existing template instantiation
* Returns:
- * `true` if a match was found, `false` otherwise
+ * true for match
*/
- extern (D) final bool findBestMatch(Scope* sc, ArgumentList argumentList)
+ final bool equalsx(TemplateInstance ti)
{
- if (havetempdecl)
- {
- TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
- assert(tempdecl._scope);
- // Deduce tdtypes
- tdtypes.setDim(tempdecl.parameters.length);
- if (!matchWithInstance(sc, tempdecl, this, tdtypes, argumentList, 2))
- {
- .error(loc, "%s `%s` incompatible arguments for template instantiation", kind, toPrettyChars);
- return false;
- }
- // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
- return true;
- }
+ //printf("this = %p, ti = %p\n", this, ti);
+ assert(tdtypes.length == ti.tdtypes.length);
- static if (LOG)
+ // Nesting must match
+ if (enclosing != ti.enclosing)
{
- printf("TemplateInstance.findBestMatch()\n");
+ //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
+ return false;
}
+ //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
- const errs = global.errors;
- TemplateDeclaration td_last = null;
- Objects dedtypes;
+ if (!arrayObjectMatch(tdtypes, ti.tdtypes))
+ return false;
- /* Since there can be multiple TemplateDeclaration's with the same
- * name, look for the best match.
+ /* Template functions may have different instantiations based on
+ * "auto ref" parameters.
*/
- auto tovers = tempdecl.isOverloadSet();
- foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
- {
- TemplateDeclaration td_best;
- TemplateDeclaration td_ambig;
- MATCH m_best = MATCH.nomatch;
-
- Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
- overloadApply(dstart, (Dsymbol s)
- {
- auto td = s.isTemplateDeclaration();
- if (!td)
- return 0;
- if (td == td_best) // skip duplicates
- return 0;
-
- //printf("td = %s\n", td.toPrettyChars());
- // If more arguments than parameters,
- // then this is no match.
- if (td.parameters.length < tiargs.length)
- {
- if (!td.isVariadic())
- return 0;
- }
-
- dedtypes.setDim(td.parameters.length);
- dedtypes.zero();
- assert(td.semanticRun != PASS.initial);
-
- MATCH m = matchWithInstance(sc, td, this, dedtypes, argumentList, 0);
- //printf("matchWithInstance = %d\n", m);
- if (m == MATCH.nomatch) // no match at all
- return 0;
- if (m < m_best) goto Ltd_best;
- if (m > m_best) goto Ltd;
-
- // Disambiguate by picking the most specialized TemplateDeclaration
- {
- MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList);
- MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList);
- //printf("c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto Ltd;
- if (c1 < c2) goto Ltd_best;
- }
+ auto fd = ti.toAlias().isFuncDeclaration();
+ if (!fd)
+ return true;
+ if (fd.errors)
+ return true;
- td_ambig = td;
- return 0;
-
- Ltd_best:
- // td_best is the best match so far
- td_ambig = null;
- return 0;
-
- Ltd:
- // td is the new best match
- td_ambig = null;
- td_best = td;
- m_best = m;
- tdtypes.setDim(dedtypes.length);
- memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.length * (void*).sizeof);
- return 0;
- });
-
- if (td_ambig)
- {
- .error(loc, "%s `%s.%s` matches more than one template declaration:",
- td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars());
- .errorSupplemental(td_best.loc, "`%s`\nand:", td_best.toChars());
- .errorSupplemental(td_ambig.loc, "`%s`", td_ambig.toChars());
- return false;
- }
- if (td_best)
- {
- if (!td_last)
- td_last = td_best;
- else if (td_last != td_best)
- {
- ScopeDsymbol.multiplyDefined(loc, td_last, td_best);
- return false;
- }
- }
- }
+ auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
+ ArgumentList(this.fargs, this.fnames), null);
- if (td_last)
- {
- /* https://issues.dlang.org/show_bug.cgi?id=7469
- * Normalize tiargs by using corresponding deduced
- * template value parameters and tuples for the correct mangling.
- *
- * By doing this before hasNestedArgs, CTFEable local variable will be
- * accepted as a value parameter. For example:
- *
- * void foo() {
- * struct S(int n) {} // non-global template
- * const int num = 1; // CTFEable local variable
- * S!num s; // S!1 is instantiated, not S!num
- * }
- */
- size_t dim = td_last.parameters.length - (td_last.isVariadic() ? 1 : 0);
- for (size_t i = 0; i < dim; i++)
- {
- if (tiargs.length <= i)
- tiargs.push(tdtypes[i]);
- assert(i < tiargs.length);
+ // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
+ // In that case, equalsx returns true to prevent endless template instantiations
+ // However, it can also mean the function was explicitly instantiated
+ // without function arguments: fail_compilation/fail14669
+ // Hence the following check:
+ if (this.fargs && !resolvedArgs)
+ return true;
- auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
- if (!tvp)
- continue;
- assert(tdtypes[i]);
- // tdtypes[i] is already normalized to the required type in matchArg
+ Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
- (*tiargs)[i] = tdtypes[i];
- }
- if (td_last.isVariadic() && tiargs.length == dim && tdtypes[dim])
- {
- Tuple va = isTuple(tdtypes[dim]);
- assert(va);
- tiargs.pushSlice(va.objects[]);
- }
- }
- else if (errors && inst)
- {
- // instantiation was failed with error reporting
- assert(global.errors);
- return false;
- }
- else
+ auto fparameters = fd.getParameterList();
+ size_t nfparams = fparameters.length; // Num function parameters
+ for (size_t j = 0; j < nfparams; j++)
{
- auto tdecl = tempdecl.isTemplateDeclaration();
+ Parameter fparam = fparameters[j];
+ if (!(fparam.storageClass & STC.autoref) ) // if "auto ref"
+ continue;
- if (errs != global.errors)
- errorSupplemental(loc, "while looking for match for `%s`", toChars());
- else if (tdecl && !tdecl.overnext)
+ Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
+ // resolveNamedArgs strips trailing nulls / default params
+ // when it doesn't anymore, the ternary can be replaced with:
+ // assert(j < resolvedArgs.length);
+ if (!farg)
+ farg = fparam.defaultArg;
+ if (!farg)
+ return false;
+ if (farg.isLvalue())
{
- // Only one template, so we can give better error message
- const(char)* msg = "does not match template declaration";
- const(char)* tip;
- OutBuffer buf;
- HdrGenState hgs;
- hgs.skipConstraints = true;
- toCharsMaybeConstraints(tdecl, buf, hgs);
- const tmsg = buf.peekChars();
- const cmsg = tdecl.getConstraintEvalError(tip);
- if (cmsg)
- {
- .error(loc, "%s `%s` %s `%s`\n%s", kind, toPrettyChars, msg, tmsg, cmsg);
- if (tip)
- .tip(tip);
- }
- else
- {
- .error(loc, "%s `%s` %s `%s`", kind, toPrettyChars, msg, tmsg);
-
- if (tdecl.parameters.length == tiargs.length)
- {
- // https://issues.dlang.org/show_bug.cgi?id=7352
- // print additional information, e.g. `foo` is not a type
- foreach (i, param; *tdecl.parameters)
- {
- MATCH match = param.matchArg(loc, sc, tiargs, i, tdecl.parameters, dedtypes, null);
- auto arg = (*tiargs)[i];
- auto sym = arg.isDsymbol;
- auto exp = arg.isExpression;
-
- if (exp)
- exp = exp.optimize(WANTvalue);
-
- if (match == MATCH.nomatch &&
- ((sym && sym.isFuncDeclaration) ||
- (exp && exp.isVarExp)))
- {
- if (param.isTemplateTypeParameter)
- errorSupplemental(loc, "`%s` is not a type", arg.toChars);
- else if (auto tvp = param.isTemplateValueParameter)
- errorSupplemental(loc, "`%s` is not of a value of type `%s`",
- arg.toChars, tvp.valType.toChars);
-
- }
- }
- }
- }
+ if (!(fparam.storageClass & STC.ref_))
+ return false; // auto ref's don't match
}
else
{
- .error(loc, "%s `%s` does not match any template declaration", kind(), toPrettyChars());
- bool found;
- overloadApply(tempdecl, (s){
- if (!found)
- errorSupplemental(loc, "Candidates are:");
- found = true;
- errorSupplemental(s.loc, "%s", s.toChars());
- return 0;
- });
+ if (fparam.storageClass & STC.ref_)
+ return false; // auto ref's don't match
}
- return false;
}
+ return true;
+ }
- /* The best match is td_last
- */
- tempdecl = td_last;
+ extern (D) final size_t toHash()
+ {
+ if (!hash)
+ {
+ hash = cast(size_t)cast(void*)enclosing;
+ hash += arrayObjectHash(tdtypes);
+ hash += hash == 0;
+ }
+ return hash;
+ }
- static if (LOG)
+ /**********************************
+ * Run semantic on the elements of tiargs.
+ * Input:
+ * sc
+ * Returns:
+ * false if one or more arguments have errors.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * all elements of tiargs won't be modified.
+ */
+ extern (D) final bool semanticTiargs(Scope* sc)
+ {
+ //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
+ if (semantictiargsdone)
+ return true;
+ if (TemplateInstance_semanticTiargs(loc, sc, tiargs, 0))
{
- printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+ // cache the result iff semantic analysis succeeded entirely
+ semantictiargsdone = 1;
+ return true;
}
- return (errs == global.errors);
+ return false;
}
/*****************************************
L1:
//printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars());
nested |= 1;
- }
- }
- else if (va)
- {
- nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
- }
- }
- //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
- return nested != 0;
- }
-
- /*****************************************
- * Append 'this' to the specific module members[]
- */
- extern (D) final Dsymbols* appendToModuleMember()
- {
- Module mi = minst; // instantiated . inserted module
-
- //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
- // toPrettyChars(),
- // enclosing ? enclosing.toPrettyChars() : null,
- // mi ? mi.toPrettyChars() : null);
- if (global.params.allInst || !mi || mi.isRoot())
- {
- /* If the instantiated module is speculative or root, insert to the
- * member of a root module. Then:
- * - semantic3 pass will get called on the instance members.
- * - codegen pass will get a selection chance to do/skip it (needsCodegen()).
- */
- static Dsymbol getStrictEnclosing(TemplateInstance ti)
- {
- do
- {
- if (ti.enclosing)
- return ti.enclosing;
- ti = ti.tempdecl.isInstantiated();
- } while (ti);
- return null;
- }
-
- Dsymbol enc = getStrictEnclosing(this);
- // insert target is made stable by using the module
- // where tempdecl is declared.
- mi = (enc ? enc : tempdecl).getModule();
- if (!mi.isRoot())
- {
- if (mi.importedFrom)
- {
- mi = mi.importedFrom;
- assert(mi.isRoot());
- }
- else
- {
- // This can happen when using the frontend as a library.
- // Append it to the non-root module.
- }
- }
- }
- else
- {
- /* If the instantiated module is non-root, insert to the member of the
- * non-root module. Then:
- * - semantic3 pass won't be called on the instance.
- * - codegen pass won't reach to the instance.
- * Unless it is re-appended to a root module later (with changed minst).
- */
- }
- //printf("\t-. mi = %s\n", mi.toPrettyChars());
-
- if (memberOf) // already appended to some module
- {
- assert(mi.isRoot(), "can only re-append to a root module");
- if (memberOf.isRoot())
- return null; // no need to move to another root module
- }
-
- Dsymbols* a = mi.members;
- a.push(this);
- memberOf = mi;
- if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
- Module.addDeferredSemantic2(this);
- if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
- Module.addDeferredSemantic3(this);
- return a;
- }
-
- /****************************************************
- * Declare parameters of template instance, initialize them with the
- * template instance arguments.
- */
- extern (D) final void declareParameters(Scope* sc)
- {
- TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
- assert(tempdecl);
-
- //printf("TemplateInstance.declareParameters()\n");
- foreach (i, o; tdtypes) // initializer for tp
- {
- TemplateParameter tp = (*tempdecl.parameters)[i];
- //printf("\ttdtypes[%d] = %p\n", i, o);
- declareParameter(tempdecl, sc, tp, o);
+ }
+ }
+ else if (va)
+ {
+ nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
+ }
}
+ //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
+ return nested != 0;
}
/****************************************
return Identifier.idPool(buf[]);
}
- extern (D) final void expandMembers(Scope* sc2)
- {
- members.foreachDsymbol( (s) { s.setScope (sc2); } );
-
- members.foreachDsymbol( (s) { s.importAll(sc2); } );
-
- if (!aliasdecl)
- {
- /* static if's are crucial to evaluating aliasdecl correctly. But
- * evaluating the if/else bodies may require aliasdecl.
- * So, evaluate the condition for static if's, but not their if/else bodies.
- * Then try to set aliasdecl.
- * Later do the if/else bodies.
- * https://issues.dlang.org/show_bug.cgi?id=23598
- * It might be better to do this by attaching a lambda to the StaticIfDeclaration
- * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
- */
- bool done;
- void staticIfDg(Dsymbol s)
- {
- if (done || aliasdecl)
- return;
- //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars());
- if (!s.isStaticIfDeclaration())
- {
- //s.dsymbolSemantic(sc2);
- done = true;
- return;
- }
- auto sid = s.isStaticIfDeclaration();
- sid.include(sc2);
- if (members.length)
- {
- Dsymbol sa;
- if (oneMembers(members, sa, tempdecl.ident) && sa)
- aliasdecl = sa;
- }
- done = true;
- }
-
- members.foreachDsymbol(&staticIfDg);
- }
-
- void symbolDg(Dsymbol s)
- {
- //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
- //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
- //if (enclosing)
- // s.parent = sc.parent;
- //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
- s.dsymbolSemantic(sc2);
- //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
- Module.runDeferredSemantic();
- }
-
- members.foreachDsymbol(&symbolDg);
- }
-
- extern (D) final void tryExpandMembers(Scope* sc2)
- {
- __gshared int nest;
- // extracted to a function to allow windows SEH to work without destructors in the same function
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars);
- fatal();
- }
-
- expandMembers(sc2);
-
- nest--;
- }
-
- extern (D) final void trySemantic3(Scope* sc2)
- {
- // extracted to a function to allow windows SEH to work without destructors in the same function
- __gshared int nest;
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars);
- fatal();
- }
-
- semantic3(this, sc2);
-
- --nest;
- }
-
override void accept(Visitor v)
{
v.visit(this);
}
}
-/**************************************
- * IsExpression can evaluate the specified type speculatively, and even if
- * it instantiates any symbols, they are normally unnecessary for the
- * final executable.
- * However, if those symbols leak to the actual code, compiler should remark
- * them as non-speculative to generate their code and link to the final executable.
- */
-void unSpeculative(Scope* sc, RootObject o)
-{
- if (!o)
- return;
-
- if (Tuple tup = isTuple(o))
- {
- foreach (obj; tup.objects)
- {
- unSpeculative(sc, obj);
- }
- return;
- }
-
- Dsymbol s = getDsymbol(o);
- if (!s)
- return;
-
- if (Declaration d = s.isDeclaration())
- {
- if (VarDeclaration vd = d.isVarDeclaration())
- o = vd.type;
- else if (AliasDeclaration ad = d.isAliasDeclaration())
- {
- o = ad.getType();
- if (!o)
- o = ad.toAlias();
- }
- else
- o = d.toAlias();
-
- s = getDsymbol(o);
- if (!s)
- return;
- }
-
- if (TemplateInstance ti = s.isTemplateInstance())
- {
- // If the instance is already non-speculative,
- // or it is leaked to the speculative scope.
- if (ti.minst !is null || sc.minst is null)
- return;
-
- // Remark as non-speculative instance.
- ti.minst = sc.minst;
- if (!ti.tinst)
- ti.tinst = sc.tinst;
-
- unSpeculative(sc, ti.tempdecl);
- }
-
- if (TemplateInstance ti = s.isInstantiated())
- unSpeculative(sc, ti);
-}
-
/**********************************
* Return true if e could be valid only as a template value parameter.
* Return false if it might be an alias or tuple.
return "mixin";
}
- extern (D) bool findTempDecl(Scope* sc)
- {
- // Follow qualifications to find the TemplateDeclaration
- if (!tempdecl)
- {
- Expression e;
- Type t;
- Dsymbol s;
- tqual.resolve(loc, sc, e, t, s);
- if (!s)
- {
- .error(loc, "%s `%s` is not defined", kind, toPrettyChars);
- return false;
- }
- s = s.toAlias();
- tempdecl = s.isTemplateDeclaration();
- OverloadSet os = s.isOverloadSet();
-
- /* If an OverloadSet, look for a unique member that is a template declaration
- */
- if (os)
- {
- Dsymbol ds = null;
- foreach (i, sym; os.a)
- {
- Dsymbol s2 = sym.isTemplateDeclaration();
- if (s2)
- {
- if (ds)
- {
- tempdecl = os;
- break;
- }
- ds = s2;
- }
- }
- }
- if (!tempdecl)
- {
- .error(loc, "%s `%s` - `%s` is a %s, not a template", kind, toPrettyChars, s.toChars(), s.kind());
- return false;
- }
- }
- assert(tempdecl);
-
- // Look for forward references
- auto tovers = tempdecl.isOverloadSet();
- foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
- {
- Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
- int r = overloadApply(dstart, (Dsymbol s)
- {
- auto td = s.isTemplateDeclaration();
- if (!td)
- return 0;
-
- if (td.semanticRun == PASS.initial)
- {
- if (td._scope)
- td.dsymbolSemantic(td._scope);
- else
- {
- semanticRun = PASS.initial;
- return 1;
- }
- }
- return 0;
- });
- if (r)
- return false;
- }
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
}
}
-/*******************************************
- * Match to a particular TemplateParameter.
- * Input:
- * instLoc location that the template is instantiated.
- * tiargs[] actual arguments to template instance
- * i i'th argument
- * parameters[] template parameters
- * dedtypes[] deduced arguments to template instance
- * *psparam set to symbol declared and initialized to dedtypes[i]
- */
-MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
-{
- MATCH matchArgNoMatch()
- {
- if (psparam)
- *psparam = null;
- return MATCH.nomatch;
- }
-
- MATCH matchArgParameter()
- {
- RootObject oarg;
-
- if (i < tiargs.length)
- oarg = (*tiargs)[i];
- else
- {
- // Get default argument instead
- oarg = tp.defaultArg(instLoc, sc);
- if (!oarg)
- {
- assert(i < dedtypes.length);
- // It might have already been deduced
- oarg = dedtypes[i];
- if (!oarg)
- return matchArgNoMatch();
- }
- }
- return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam);
- }
-
- MATCH matchArgTuple(TemplateTupleParameter ttp)
- {
- /* The rest of the actual arguments (tiargs[]) form the match
- * for the variadic parameter.
- */
- assert(i + 1 == dedtypes.length); // must be the last one
- Tuple ovar;
-
- if (Tuple u = isTuple(dedtypes[i]))
- {
- // It has already been deduced
- ovar = u;
- }
- else if (i + 1 == tiargs.length && isTuple((*tiargs)[i]))
- ovar = isTuple((*tiargs)[i]);
- else
- {
- ovar = new Tuple();
- //printf("ovar = %p\n", ovar);
- if (i < tiargs.length)
- {
- //printf("i = %d, tiargs.length = %d\n", i, tiargs.length);
- ovar.objects.setDim(tiargs.length - i);
- foreach (j, ref obj; ovar.objects)
- obj = (*tiargs)[i + j];
- }
- }
- return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam);
- }
-
- if (auto ttp = tp.isTemplateTupleParameter())
- return matchArgTuple(ttp);
-
- return matchArgParameter();
-}
-
-MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
-{
- MATCH matchArgNoMatch()
- {
- //printf("\tm = %d\n", MATCH.nomatch);
- if (psparam)
- *psparam = null;
- return MATCH.nomatch;
- }
-
- MATCH matchArgType(TemplateTypeParameter ttp)
- {
- //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars());
- MATCH m = MATCH.exact;
- Type ta = isType(oarg);
- if (!ta)
- {
- //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
- return matchArgNoMatch();
- }
- //printf("ta is %s\n", ta.toChars());
-
- if (ttp.specType)
- {
- if (!ta || ta == TemplateTypeParameter.tdummy)
- return matchArgNoMatch();
-
- //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars());
- MATCH m2 = deduceType(ta, sc, ttp.specType, *parameters, dedtypes);
- if (m2 == MATCH.nomatch)
- {
- //printf("\tfailed deduceType\n");
- return matchArgNoMatch();
- }
-
- if (m2 < m)
- m = m2;
- if (dedtypes[i])
- {
- Type t = cast(Type)dedtypes[i];
-
- if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357
- return matchArgNoMatch();
-
- /* This is a self-dependent parameter. For example:
- * template X(T : T*) {}
- * template X(T : S!T, alias S) {}
- */
- //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
- ta = t;
- }
- }
- else
- {
- if (dedtypes[i])
- {
- // Must match already deduced type
- Type t = cast(Type)dedtypes[i];
-
- if (!t.equals(ta))
- {
- //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
- return matchArgNoMatch();
- }
- }
- else
- {
- // So that matches with specializations are better
- m = MATCH.convert;
- }
- }
- dedtypes[i] = ta;
-
- if (psparam)
- *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta);
- //printf("\tm = %d\n", m);
- return ttp.dependent ? MATCH.exact : m;
- }
-
- MATCH matchArgValue(TemplateValueParameter tvp)
- {
- //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars());
- MATCH m = MATCH.exact;
-
- Expression ei = isExpression(oarg);
- Type vt;
-
- if (!ei && oarg)
- {
- Dsymbol si = isDsymbol(oarg);
- FuncDeclaration f = si ? si.isFuncDeclaration() : null;
- if (!f || !f.fbody || f.needThis())
- return matchArgNoMatch();
-
- ei = new VarExp(tvp.loc, f);
- ei = ei.expressionSemantic(sc);
-
- /* If a function is really property-like, and then
- * it's CTFEable, ei will be a literal expression.
- */
- const olderrors = global.startGagging();
- ei = resolveProperties(sc, ei);
- ei = ei.ctfeInterpret();
- if (global.endGagging(olderrors) || ei.op == EXP.error)
- return matchArgNoMatch();
-
- /* https://issues.dlang.org/show_bug.cgi?id=14520
- * A property-like function can match to both
- * TemplateAlias and ValueParameter. But for template overloads,
- * it should always prefer alias parameter to be consistent
- * template match result.
- *
- * template X(alias f) { enum X = 1; }
- * template X(int val) { enum X = 2; }
- * int f1() { return 0; } // CTFEable
- * int f2(); // body-less function is not CTFEable
- * enum x1 = X!f1; // should be 1
- * enum x2 = X!f2; // should be 1
- *
- * e.g. The x1 value must be same even if the f1 definition will be moved
- * into di while stripping body code.
- */
- m = MATCH.convert;
- }
-
- if (ei && ei.op == EXP.variable)
- {
- // Resolve const variables that we had skipped earlier
- ei = ei.ctfeInterpret();
- }
-
- //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty);
- vt = tvp.valType.typeSemantic(tvp.loc, sc);
- //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars());
- //printf("vt = %s\n", vt.toChars());
-
- if (ei.type)
- {
- MATCH m2 = ei.implicitConvTo(vt);
- //printf("m: %d\n", m);
- if (m2 < m)
- m = m2;
- if (m == MATCH.nomatch)
- return matchArgNoMatch();
- ei = ei.implicitCastTo(sc, vt);
- ei = ei.ctfeInterpret();
- }
-
- if (tvp.specValue)
- {
- if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies &&
- TemplateValueParameter.edummies[cast(void*)ei.type] == ei))
- return matchArgNoMatch();
-
- Expression e = tvp.specValue;
-
- sc = sc.startCTFE();
- e = e.expressionSemantic(sc);
- e = resolveProperties(sc, e);
- sc = sc.endCTFE();
- e = e.implicitCastTo(sc, vt);
- e = e.ctfeInterpret();
-
- ei = ei.syntaxCopy();
- sc = sc.startCTFE();
- ei = ei.expressionSemantic(sc);
- sc = sc.endCTFE();
- ei = ei.implicitCastTo(sc, vt);
- ei = ei.ctfeInterpret();
- //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars());
- //printf("\te : %s, %s\n", e.toChars(), e.type.toChars());
- if (!ei.equals(e))
- return matchArgNoMatch();
- }
- else
- {
- if (dedtypes[i])
- {
- // Must match already deduced value
- Expression e = cast(Expression)dedtypes[i];
- if (!ei || !ei.equals(e))
- return matchArgNoMatch();
- }
- }
- dedtypes[i] = ei;
-
- if (psparam)
- {
- Initializer _init = new ExpInitializer(tvp.loc, ei);
- Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init);
- sparam.storage_class = STC.manifest;
- *psparam = sparam;
- }
- return tvp.dependent ? MATCH.exact : m;
- }
-
- MATCH matchArgAlias(TemplateAliasParameter tap)
- {
- //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars());
- MATCH m = MATCH.exact;
- Type ta = isType(oarg);
- RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
- Expression ea = isExpression(oarg);
- if (ea)
- {
- if (auto te = ea.isThisExp())
- sa = te.var;
- else if (auto se = ea.isSuperExp())
- sa = se.var;
- else if (auto se = ea.isScopeExp())
- sa = se.sds;
- }
- if (sa)
- {
- if ((cast(Dsymbol)sa).isAggregateDeclaration())
- m = MATCH.convert;
-
- /* specType means the alias must be a declaration with a type
- * that matches specType.
- */
- if (tap.specType)
- {
- tap.specType = typeSemantic(tap.specType, tap.loc, sc);
- Declaration d = (cast(Dsymbol)sa).isDeclaration();
- if (!d)
- return matchArgNoMatch();
- if (!d.type.equals(tap.specType))
- return matchArgNoMatch();
- }
- }
- else
- {
- sa = oarg;
- if (ea)
- {
- if (tap.specType)
- {
- if (!ea.type.equals(tap.specType))
- return matchArgNoMatch();
- }
- }
- else if (ta && ta.ty == Tinstance && !tap.specAlias)
- {
- /* Specialized parameter should be preferred
- * match to the template type parameter.
- * template X(alias a) {} // a == this
- * template X(alias a : B!A, alias B, A...) {} // B!A => ta
- */
- }
- else if (sa && sa == TemplateTypeParameter.tdummy)
- {
- /* https://issues.dlang.org/show_bug.cgi?id=2025
- * Aggregate Types should preferentially
- * match to the template type parameter.
- * template X(alias a) {} // a == this
- * template X(T) {} // T => sa
- */
- }
- else if (ta && ta.ty != Tident)
- {
- /* Match any type that's not a TypeIdentifier to alias parameters,
- * but prefer type parameter.
- * template X(alias a) { } // a == ta
- *
- * TypeIdentifiers are excluded because they might be not yet resolved aliases.
- */
- m = MATCH.convert;
- }
- else
- return matchArgNoMatch();
- }
-
- if (tap.specAlias)
- {
- if (sa == TemplateAliasParameter.sdummy)
- return matchArgNoMatch();
- // check specialization if template arg is a symbol
- Dsymbol sx = isDsymbol(sa);
- if (sa != tap.specAlias && sx)
- {
- Type talias = isType(tap.specAlias);
- if (!talias)
- return matchArgNoMatch();
-
- TemplateInstance ti = sx.isTemplateInstance();
- if (!ti && sx.parent)
- {
- ti = sx.parent.isTemplateInstance();
- if (ti && ti.name != sx.ident)
- return matchArgNoMatch();
- }
- if (!ti)
- return matchArgNoMatch();
-
- Type t = new TypeInstance(Loc.initial, ti);
- MATCH m2 = deduceType(t, sc, talias, *parameters, dedtypes);
- if (m2 == MATCH.nomatch)
- return matchArgNoMatch();
- }
- // check specialization if template arg is a type
- else if (ta)
- {
- if (Type tspec = isType(tap.specAlias))
- {
- MATCH m2 = ta.implicitConvTo(tspec);
- if (m2 == MATCH.nomatch)
- return matchArgNoMatch();
- }
- else
- {
- error(tap.loc, "template parameter specialization for a type must be a type and not `%s`",
- tap.specAlias.toChars());
- return matchArgNoMatch();
- }
- }
- }
- else if (dedtypes[i])
- {
- // Must match already deduced symbol
- RootObject si = dedtypes[i];
- if (!sa || si != sa)
- return matchArgNoMatch();
- }
- dedtypes[i] = sa;
-
- if (psparam)
- {
- if (Dsymbol s = isDsymbol(sa))
- {
- *psparam = new AliasDeclaration(tap.loc, tap.ident, s);
- }
- else if (Type t = isType(sa))
- {
- *psparam = new AliasDeclaration(tap.loc, tap.ident, t);
- }
- else
- {
- assert(ea);
-
- // Declare manifest constant
- Initializer _init = new ExpInitializer(tap.loc, ea);
- auto v = new VarDeclaration(tap.loc, null, tap.ident, _init);
- v.storage_class = STC.manifest;
- v.dsymbolSemantic(sc);
- *psparam = v;
- }
- }
- return tap.dependent ? MATCH.exact : m;
- }
-
- MATCH matchArgTuple(TemplateTupleParameter ttp)
- {
- //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars());
- Tuple ovar = isTuple(oarg);
- if (!ovar)
- return MATCH.nomatch;
- if (dedtypes[i])
- {
- Tuple tup = isTuple(dedtypes[i]);
- if (!tup)
- return MATCH.nomatch;
- if (!match(tup, ovar))
- return MATCH.nomatch;
- }
- dedtypes[i] = ovar;
-
- if (psparam)
- *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects);
- return ttp.dependent ? MATCH.exact : MATCH.convert;
- }
-
- if (auto ttp = tp.isTemplateTypeParameter())
- return matchArgType(ttp);
- if (auto tvp = tp.isTemplateValueParameter())
- return matchArgValue(tvp);
- if (auto tap = tp.isTemplateAliasParameter())
- return matchArgAlias(tap);
- if (auto ttp = tp.isTemplateTupleParameter())
- return matchArgTuple(ttp);
- assert(0);
-}
-
-
/***********************************************
* Collect and print statistics on template instantiations.
*/
{
// Omit redundant declarations - the slot was already
// reserved in the base class
- if (fd.isVirtual() && fd.isIntroducing())
+ if (fd.isVirtual() && fd.isIntroducing)
{
// Hide placeholders because they are not ABI compatible
writeProtection(AST.Visibility.Kind.private_);
auto fd = ad.aliassym.isFuncDeclaration();
- if (fd && (fd.isGenerated() || fd.isDtorDeclaration()))
+ if (fd && (fd.isGenerated || fd.isDtorDeclaration()))
{
// Ignore. It's taken care of while visiting FuncDeclaration
return;
// Print prefix of the base class if this function originates from a superclass
// because alias might be resolved through multiple classes, e.g.
// e.g. for alias visit = typeof(super).visit in the visitors
- if (!fd.isIntroducing())
+ if (!fd.isIntroducing)
printPrefix(ad.toParent().isClassDeclaration().baseClass);
else
printPrefix(pd);
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
-import dmd.globals;
import dmd.identifier;
import dmd.location;
import dmd.common.outbuffer;
bool isSpecial() const;
- Symbol *sinit;
+ void *sinit;
void accept(Visitor *v) override { v->visit(this); }
};
em.dsymbolSemantic(em._scope);
});
- if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot())
+ if (ed.errors)
+ ed.memtype = Type.terror; // avoid infinite recursion in toBaseType
+ else if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot())
semanticTypeInfo(sc, ed.memtype);
//printf("ed.defaultval = %lld\n", ed.defaultval);
void visitThis(ThisExp e)
{
// Special case because `__this2` isn't `ref` internally
- if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
+ if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext)
{
escapeByValue(e, er);
return;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
+import dmd.ctfeexpr : isCtfeReferenceValid;
import dmd.dcast : implicitConvTo;
import dmd.dclass;
import dmd.declaration;
import dmd.dtemplate;
import dmd.errors;
import dmd.errorsink;
+import dmd.expressionsem : getDsymbol;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
import dmd.root.utf;
import dmd.target;
import dmd.tokens;
-import dmd.typesem;
+import dmd.typesem : toHeadMutable, size, mutableOf, unSharedOf;
import dmd.visitor;
enum LOGSEMANTIC = false;
return false;
}
- /******************************
- * Take address of expression.
- */
- final Expression addressOf()
- {
- //printf("Expression::addressOf()\n");
- debug
- {
- assert(op == EXP.error || isLvalue());
- }
- Expression e = new AddrExp(loc, this, type.pointerTo());
- return e;
- }
-
/******************************
* If this is a reference, dereference it.
*/
Expressions* elements;
+ Expression lowering;
+
+ // aaLiteral is set if this is an array of values of an AA literal
+ // only used during CTFE to show the original AA in error messages instead
+ AssocArrayLiteralExp aaLiteral;
+
extern (D) this(Loc loc, Type type, Expressions* elements) @safe
{
super(loc, EXP.arrayLiteral);
{
super(loc, EXP.arrayLiteral);
this.type = type;
- elements = new Expressions();
- elements.push(e);
+ elements = new Expressions(e);
}
extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe
if (ch.op != EXP.int64)
return null;
if (sz == 1)
- buf.writeByte(cast(uint)ch.toInteger());
+ buf.writeByte(cast(ubyte)ch.toInteger());
else if (sz == 2)
buf.writeword(cast(uint)ch.toInteger());
else
Expressions* keys;
Expressions* values;
- /// Lower to core.internal.newaa for static initializaton
- Expression lowering;
+
+ Expression lowering; // call to _d_assocarrayliteralTX()
+ Expression loweringCtfe; // result of interpreting lowering for static initializaton
extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe
{
return exp;
}
- /**************************************
- * Gets expression at offset of type.
- * Returns NULL if not found.
- */
- extern (D) Expression getField(Type type, uint offset)
- {
- //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
- // /*toChars()*/"", type.toChars(), offset);
- Expression e = null;
- int i = getFieldIndex(type, offset);
-
- if (i != -1)
- {
- //printf("\ti = %d\n", i);
- if (i >= sd.nonHiddenFields())
- return null;
-
- assert(i < elements.length);
- e = (*elements)[i];
- if (e)
- {
- //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
-
- /* If type is a static array, and e is an initializer for that array,
- * then the field initializer should be an array literal of e.
- */
- auto tsa = type.isTypeSArray();
- if (tsa && e.type.castMod(0) != type.castMod(0))
- {
- const length = cast(size_t)tsa.dim.toInteger();
- auto z = new Expressions(length);
- foreach (ref q; *z)
- q = e.copy();
- e = new ArrayLiteralExp(loc, type, z);
- }
- else
- {
- e = e.copy();
- e.type = type;
- }
- if (useStaticInit && e.type.needsNested())
- if (auto se = e.isStructLiteralExp())
- {
- se.useStaticInit = true;
- }
- }
- }
- return e;
- }
-
/************************************
* Get index of field.
* Returns -1 if not found.
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
bool isUfcsRewrite; /// the first argument was pushed in here by a UFCS rewrite
VarDeclaration vthis2; // container for multi-context
+ Expression loweredFrom; // set if this is the result of a lowering
/// Puts the `arguments` and `names` into an `ArgumentList` for easily passing them around.
/// The fields are still separate for backwards compatibility
type = t;
}
+ override Optional!bool toBool()
+ {
+ if (isCtfeReferenceValid(e1))
+ return typeof(return)(true);
+ return UnaExp.toBool();
+ }
+
override void accept(Visitor v)
{
v.visit(this);
*/
extern (C++) final class NotExp : UnaExp
{
+ Expression loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)`
+
extern (D) this(Loc loc, Expression e) @safe
{
super(loc, EXP.not, e);
Type to; // type to cast to
ubyte mod = cast(ubyte)~0; // MODxxxxx
bool trusted; // assume cast is safe
+ Expression lowering;
extern (D) this(Loc loc, Expression e, Type t) @safe
{
size_t currentDimension; // for opDollar
VarDeclaration lengthVar;
+ bool modifiable = false; // is this expected to be an lvalue in an AssignExp? propagate to IndexExp
extern (D) this(Loc loc, Expression e1, Expression index = null)
{
extern (C++) final class IndexExp : BinExp
{
VarDeclaration lengthVar;
+ Expression loweredFrom; // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX
bool modifiable = false; // assume it is an rvalue
bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
return true;
}
- extern (D) Expression markSettingAAElem()
- {
- if (e1.type.toBasetype().ty == Taarray)
- {
- Type t2b = e2.type.toBasetype();
- if (t2b.ty == Tarray && t2b.nextOf().isMutable())
- {
- error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars());
- return ErrorExp.get();
- }
- modifiable = true;
-
- if (auto ie = e1.isIndexExp())
- {
- Expression ex = ie.markSettingAAElem();
- if (ex.op == EXP.error)
- return ex;
- assert(ex == e1);
- }
- }
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
extern (D) this(Loc loc, Expression e1, Expression e2)
{
super(loc, EXP.remove, e1, e2);
- type = Type.tbool;
}
override void accept(Visitor v)
*/
extern (C++) final class EqualExp : BinExp
{
+ Expression lowering;
extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe
{
super(loc, op, e1, e2);
d_bool onstack;
Expression *basis;
Expressions *elements;
+ Expression *lowering;
+ AssocArrayLiteralExp* aaLiteral; // set if this is an array of keys/values of an AA literal
static ArrayLiteralExp *create(Loc loc, Expressions *elements);
ArrayLiteralExp *syntaxCopy() override;
Expressions *keys;
Expressions *values;
Expression* lowering;
+ Expression* loweringCtfe;
bool equals(const RootObject * const o) const override;
AssocArrayLiteralExp *syntaxCopy() override;
d_bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code)
d_bool isUfcsRewrite; // the first argument was pushed in here by a UFCS rewrite
VarDeclaration *vthis2; // container for multi-context
+ Expression* loweredFrom; // set if this is the result of a lowering
static CallExp *create(Loc loc, Expression *e, Expressions *exps);
static CallExp *create(Loc loc, Expression *e);
class AddrExp final : public UnaExp
{
public:
+ Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
class NotExp final : public UnaExp
{
public:
+ Expression* loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)`
void accept(Visitor *v) override { v->visit(this); }
};
Type *to; // type to cast to
unsigned char mod; // MODxxxxx
d_bool trusted; // assume cast is safe
+ Expression* lowering;
CastExp *syntaxCopy() override;
bool isLvalue() override;
Expressions *arguments; // Array of Expression's
size_t currentDimension; // for opDollar
VarDeclaration *lengthVar;
+ d_bool modifiable;
ArrayExp *syntaxCopy() override;
bool isLvalue() override;
{
public:
VarDeclaration *lengthVar;
+ Expression* loweredFrom; // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX
d_bool modifiable;
d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
class EqualExp final : public BinExp
{
public:
+ Expression* lowering;
+
void accept(Visitor *v) override { v->visit(this); }
};
-/**
+/***
* Semantic analysis of expressions.
*
* Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
import dmd.ctorflow;
import dmd.dscope;
import dmd.dsymbol;
+import dmd.dsymbolsem;
import dmd.declaration;
import dmd.dclass;
import dmd.dcast;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dstruct;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : findTempDecl;
import dmd.dtemplate;
import dmd.errors;
import dmd.errorsink;
import dmd.sideeffect;
import dmd.safe;
import dmd.target;
-import dmd.templatesem : matchWithInstance;
+import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl;
import dmd.tokens;
import dmd.traits;
import dmd.typesem;
return false;
}
+/***************************************
+ * Verifies whether the struct declaration has a
+ * constructor that is not a copy constructor.
+ * Optionally, it can check whether the struct
+ * declaration has a regular constructor, that
+ * is not disabled.
+ *
+ * Params:
+ * sd = struct declaration
+ * ignoreDisabled = true to ignore disabled constructors
+ * Returns:
+ * true, if the struct has a regular (optionally,
+ * not disabled) constructor, false otherwise.
+ */
+bool hasRegularCtor(StructDeclaration sd, bool ignoreDisabled)
+{
+ if (!sd.ctor)
+ return false;
+
+ bool result;
+ overloadApply(sd.ctor, (Dsymbol s)
+ {
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (ignoreDisabled && td.onemember)
+ {
+ if (auto ctorDecl = td.onemember.isCtorDeclaration())
+ {
+ if (ctorDecl.storage_class & STC.disable)
+ return 0;
+ }
+ }
+ result = true;
+ return 1;
+ }
+ if (auto ctorDecl = s.isCtorDeclaration())
+ {
+ if (!ctorDecl.isCpCtor && !ctorDecl.isMoveCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable)))
+ {
+ result = true;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ return result;
+}
+
/*****************************************
* Determine if `this` is available by walking up the enclosing
* scopes until a function is found.
{
return null;
}
- if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
+ if (!fd.isNested() || fd.isThis() || (fd.hasDualContext && fd.isMember2()))
break;
Dsymbol parent = fd.parent;
fd = parent.isFuncDeclaration();
}
- if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
+ if (!fd.isThis() && !(fd.hasDualContext && fd.isMember2()))
{
return null;
}
return ErrorExp.get();
}
-private Expression reorderSettingAAElem(BinExp exp, Scope* sc)
-{
- BinExp be = exp;
-
- auto ie = be.e1.isIndexExp();
- if (!ie)
- return be;
- if (ie.e1.type.toBasetype().ty != Taarray)
- return be;
-
- /* Fix evaluation order of setting AA element
- * https://issues.dlang.org/show_bug.cgi?id=3825
- * Rewrite:
- * aa[k1][k2][k3] op= val;
- * as:
- * auto ref __aatmp = aa;
- * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
- * auto ref __aaval = val;
- * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment
- */
-
- Expression e0;
- while (1)
- {
- Expression de;
- ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2);
- e0 = Expression.combine(de, e0);
-
- auto ie1 = ie.e1.isIndexExp();
- if (!ie1 ||
- ie1.e1.type.toBasetype().ty != Taarray)
- {
- break;
- }
- ie = ie1;
- }
- assert(ie.e1.type.toBasetype().ty == Taarray);
-
- Expression de;
- ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1);
- e0 = Expression.combine(de, e0);
-
- be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true);
-
- //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars());
- return Expression.combine(e0, be);
-}
-
private Expression checkOpAssignTypes(BinExp binExp, Scope* sc)
{
auto e1 = binExp.e1;
// https://issues.dlang.org/show_bug.cgi?id=12585
// Extract the side effect part if ue.e1 is comma.
- if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
- {
- /* Even if opDollar is needed, 'e1' should be evaluate only once. So
- * Rewrite:
- * e1.opIndex( ... use of $ ... )
- * e1.opSlice( ... use of $ ... )
- * as:
- * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
- * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
- */
- e1 = extractSideEffect(sc, "__dop", e0, e1, false);
- assert(e1.isVarExp());
- e1.isVarExp().var.storage_class |= STC.exptemp; // lifetime limited to expression
- }
+ /* Even if opDollar is needed, 'e1' should be evaluate only once. So
+ * Rewrite:
+ * e1.opIndex( ... use of $ ... )
+ * e1.opSlice( ... use of $ ... )
+ * as:
+ * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
+ * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
+ */
+ e1 = extractSideEffect(sc, "__dop", e0, e1, false);
ue.e1 = e1;
return e0;
}
}
}
+/******************************
+ * Take address of expression.
+ */
+Expression addressOf(Expression e)
+{
+ //printf("Expression::addressOf() %s\n", e.toChars());
+ debug
+ {
+ assert(e.op == EXP.error || e.isLvalue());
+ }
+ return new AddrExp(e.loc, e, e.type.pointerTo());
+}
+
+/**************************************
+ * Gets expression at offset of type.
+ * Returns NULL if not found.
+ */
+Expression getField(StructLiteralExp sle, Type type, uint offset)
+{
+ //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
+ // /*toChars()*/"", type.toChars(), offset);
+ Expression e2 = null;
+ const int i = sle.getFieldIndex(type, offset);
+
+ if (i == -1)
+ return null;
+
+ //printf("\ti = %d\n", i);
+ if (i >= sle.sd.nonHiddenFields())
+ return null;
+
+ assert(i < sle.elements.length);
+ e2 = (*sle.elements)[i];
+ if (!e2)
+ return null;
+
+ //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
+
+ /* If type is a static array, and e is an initializer for that array,
+ * then the field initializer should be an array literal of e.
+ */
+ auto tsa = type.isTypeSArray();
+ if (tsa && e2.type.castMod(0) != type.castMod(0))
+ {
+ const length = cast(size_t)tsa.dim.toInteger();
+ auto z = new Expressions(length);
+ foreach (ref q; *z)
+ q = e2.copy();
+ e2 = new ArrayLiteralExp(sle.loc, type, z);
+ }
+ else
+ {
+ e2 = e2.copy();
+ e2.type = type;
+ }
+ if (sle.useStaticInit && e2.type.needsNested())
+ if (auto se = e2.isStructLiteralExp())
+ {
+ se.useStaticInit = true;
+ }
+
+ return e2;
+}
+
+
+
/**************************************
* Runs semantic on ae.arguments. Declares temporary variables
* if '$' was used.
if (auto ie = e.isIntervalExp())
{
- auto tiargs = new Objects();
Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
edim = edim.expressionSemantic(sc);
- tiargs.push(edim);
+ auto tiargs = new Objects(edim);
- auto fargs = new Expressions(2);
- (*fargs)[0] = ie.lwr;
- (*fargs)[1] = ie.upr;
+ auto fargs = new Expressions(ie.lwr, ie.upr);
const xerrors = global.startGagging();
sc = sc.push();
semanticTypeInfo(sc, taa.index);
- return new RemoveExp(loc, eleft, key);
+ e = new RemoveExp(loc, eleft, key);
+ return e.expressionSemantic(sc);
}
}
else
if (e1.op == EXP.this_)
{
FuncDeclaration f = hasThis(sc);
- if (f && f.hasDualContext())
+ if (f && f.hasDualContext)
{
if (f.followInstantiationContext(ad))
{
scope bool function(DtorDeclaration) check, const string checkName)
{
auto dd = f.isDtorDeclaration();
- if (!dd || !dd.isGenerated())
+ if (!dd || !dd.isGenerated)
return;
// DtorDeclaration without parents should fail at an earlier stage
}
dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
- dd.isGenerated() ? "generated " : "".ptr,
+ dd.isGenerated ? "generated " : "".ptr,
ad.toChars,
cast(int) checkName.length, checkName.ptr);
{
field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
- if (fieldSd.dtor.isGenerated())
+ if (fieldSd.dtor.isGenerated)
fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName);
else
fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here",
// so don't print anything to avoid double error messages.
if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
|| f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
- || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+ || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT
+ || f.ident == Id._d_assocarrayliteralTX || f.ident == Id._d_arrayliteralTX
+ || f.ident == Id._d_aaGetY))
{
error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
* t = struct type, or static array of struct type to check
* loc = error message location
* sc = scope in which attributes are checked
- * Returns: true if there's an error
+ * Returns: true if there is an error
*/
private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
{
return ErrorExp.get();
e2 = resolveProperties(sc, e2);
- Expressions* a = new Expressions();
- a.push(e2);
+ Expressions* a = new Expressions(e2);
for (size_t i = 0; i < os.a.length; i++)
{
return ErrorExp.get();
e2 = resolveProperties(sc, e2);
- Expressions* a = new Expressions();
- a.push(e2);
+ Expressions* a = new Expressions(e2);
FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(a), FuncResolveFlag.quiet);
if (fd && fd.type)
*/
if (fd && fd.inlining == PINLINE.always)
{
- if (sc._module)
+ if (sc.minst)
+ sc.minst.hasAlwaysInlines = true;
+ else if (sc._module)
sc._module.hasAlwaysInlines = true;
if (sc.func)
sc.func.hasAlwaysInlines = true;
int offset;
if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
{
- const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
- const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
+ const(char)* s1 = tret.isNaked ? " mutable" : tret.modToChars();
+ const(char)* s2 = tthis.isNaked ? " mutable" : tthis.modToChars();
.error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
err = true;
}
return pkg;
}
+/**
+ * Performs the lowering of a CastExp to a call to `.object._d_cast`,
+ * populating the `lowering` field of the CastExp.
+ * This is only done for casts between classes/interfaces.
+ *
+ * Params:
+ * cex = the CastExp to lower
+ * sc = the current scope
+ */
+private void lowerCastExp(CastExp cex, Scope* sc)
+{
+ Type t1b = cex.e1.type.toBasetype();
+ Type tob = cex.to.toBasetype();
+
+ if (t1b.ty != Tclass || tob.ty != Tclass)
+ return;
+
+ ClassDeclaration cdfrom = t1b.isClassHandle();
+ ClassDeclaration cdto = tob.isClassHandle();
+
+ int offset;
+ if ((cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME)
+ || cdfrom.classKind == ClassKind.cpp || cdto.classKind == ClassKind.cpp)
+ return;
+
+ Identifier hook = Id._d_cast;
+ if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object))
+ return;
+
+ // Lower to .object._d_cast!(To)(exp.e1)
+ Expression lowering = new IdentifierExp(cex.loc, Id.empty);
+ lowering = new DotIdExp(cex.loc, lowering, Id.object);
+
+ // Unqualify the type being casted to, avoiding multiple instantiations
+ auto unqual_tob = tob.unqualify(MODFlags.wild | MODFlags.const_ |
+ MODFlags.immutable_ | MODFlags.shared_);
+ auto tiargs = new Objects(unqual_tob);
+ lowering = new DotTemplateInstanceExp(cex.loc, lowering, hook, tiargs);
+
+ // Unqualify the type being casted from to avoid multiple instantiations
+ auto unqual_t1b = t1b.unqualify(MODFlags.wild | MODFlags.const_ |
+ MODFlags.immutable_ | MODFlags.shared_);
+ Expression e1c = cex.e1.copy();
+ e1c.type = unqual_t1b;
+ auto arguments = new Expressions(e1c);
+
+ lowering = new CallExp(cex.loc, lowering, arguments);
+
+ cex.lowering = lowering.expressionSemantic(sc);
+}
+
+/**
+ * Visitor to check if a type is suitable for comparison using `memcmp`.
+ * Currently used when lowering `EqualExp`.
+ */
+private extern(C++) final class IsMemcmpableVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+ public:
+ bool result = false;
+
+ override void visit(Type t)
+ {
+ result = t.ty == Tvoid || (t.isScalar() && !t.isFloating());
+ }
+
+ override void visit(TypeStruct ts)
+ {
+ result = false;
+
+ if (ts.sym.hasIdentityEquals)
+ return; // has custom opEquals
+
+ if (!ts.sym.members)
+ {
+ result = true;
+ return;
+ }
+
+ /* We recursively check all variable declaration within the struct.
+ * The recursiveness is needed to handle cases like this:
+ * struct Test {
+ * nothrow:
+ * int[] contents;
+ * }
+ * Here a `StorageClassDeclaration` symbol will be created, which wraps the variable declaration.
+ */
+ static bool visitAllMembers(Dsymbols* members, TypeStruct root, IsMemcmpableVisitor v)
+ {
+ if (members is null)
+ return true;
+
+ foreach (m; *members)
+ {
+ if (auto vd = m.isVarDeclaration())
+ {
+ if (vd.type is null)
+ continue;
+
+ auto tbvd = vd.type.toBasetype();
+ if (tbvd !is root)
+ tbvd.accept(v);
+
+ if (!v.result)
+ return false;
+ }
+ else if (auto ad = m.isAttribDeclaration())
+ {
+ if(!visitAllMembers(ad.decl, root, v))
+ return false;
+ }
+ }
+ return true;
+ }
+ result = visitAllMembers(ts.sym.members, ts, this);
+ }
+
+ override void visit(TypeSArray tsa)
+ {
+ tsa.nextOf().toBasetype().accept(this);
+ }
+}
+
+/*********************************************
+ * In the current function 'sc.func', we are calling 'fd'.
+ * 1. Check to see if the current function can call 'fd' , issue error if not.
+ * 2. If the current function is not the parent of 'fd' , then add
+ * the current function to the list of siblings of 'fd' .
+ * 3. If the current function is a literal, and it's accessing an uplevel scope,
+ * then mark it as a delegate.
+ * Returns true if error occurs.
+ */
+private bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc)
+{
+ //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars());
+ if (auto fld = fd.isFuncLiteralDeclaration())
+ {
+ if (fld.tok == TOK.reserved)
+ {
+ fld.tok = TOK.function_;
+ fld.vthis = null;
+ }
+ }
+ if (!fd.parent || fd.parent == sc.parent)
+ return false;
+ if (fd.ident == Id.require || fd.ident == Id.ensure)
+ return false;
+ if (!fd.isThis() && !fd.isNested())
+ return false;
+ // The current function
+ FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
+ if (!fdthis)
+ return false; // out of function scope
+ Dsymbol p = fd.toParentLocal();
+ Dsymbol p2 = fd.toParent2();
+ // Function literals from fdthis to p must be delegates
+ ensureStaticLinkTo(fdthis, p);
+ if (p != p2)
+ ensureStaticLinkTo(fdthis, p2);
+ if (!fd.isNested())
+ return false;
+
+ // The function that this function is in
+ bool checkEnclosing(FuncDeclaration fdv)
+ {
+ if (!fdv)
+ return false;
+ if (fdv == fdthis)
+ return false;
+ //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
+ //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
+ //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
+ // Add this function to the list of those which called us
+ if (fdthis != fd)
+ {
+ bool found = false;
+ for (size_t i = 0; i < fd.siblingCallers.length; ++i)
+ {
+ if (fd.siblingCallers[i] == fdthis)
+ found = true;
+ }
+ if (!found)
+ {
+ //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
+ if (!sc.intypeof && !sc.traitsCompiles)
+ {
+ fd.siblingCallers.push(fdthis);
+ fd.computedEscapingSiblings = false;
+ }
+ }
+ }
+ const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
+ if (lv == fd.LevelError)
+ return true; // error
+ if (lv == -1)
+ return false; // downlevel call
+ if (lv == 0)
+ return false; // same level call
+ return false; // Uplevel call
+ }
+ if (checkEnclosing(p.isFuncDeclaration()))
+ return true;
+ if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
+ return true;
+ return false;
+}
+
+Expression lowerArrayLiteral(ArrayLiteralExp ale, Scope* sc)
+{
+ const dim = ale.elements ? ale.elements.length : 0;
+
+ Identifier hook = global.params.tracegc ? Id._d_arrayliteralTXTrace : Id._d_arrayliteralTX;
+ if (!verifyHookExist(ale.loc, *sc, hook, "creating array literals"))
+ return null;
+
+ Expression lowering = new IdentifierExp(ale.loc, Id.empty);
+ lowering = new DotIdExp(ale.loc, lowering, Id.object);
+ // Remove `inout`, `const`, `immutable` and `shared` to reduce template instances
+ auto t = ale.type.nextOf().unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_);
+ auto tiargs = new Objects(t);
+ lowering = new DotTemplateInstanceExp(ale.loc, lowering, hook, tiargs);
+
+ auto arguments = new Expressions(new IntegerExp(dim));
+ lowering = new CallExp(ale.loc, lowering, arguments);
+ ale.lowering = lowering.expressionSemantic(sc);
+
+ return ale.lowering;
+}
+
+/***********************************
+ * If oarg represents a Dsymbol, return that Dsymbol
+ * Params:
+ * oarg = argument to check
+ * Returns:
+ * Dsymbol if a symbol, null if not
+ */
+Dsymbol getDsymbol(RootObject oarg)
+{
+ //printf("getDsymbol()\n");
+ //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
+ if (auto ea = isExpression(oarg))
+ {
+ // Try to convert Expression to symbol
+ if (auto ve = ea.isVarExp())
+ return ve.var;
+ if (auto fe = ea.isFuncExp())
+ return fe.td ? fe.td : fe.fd;
+ if (auto te = ea.isTemplateExp())
+ return te.td;
+ if (auto te = ea.isScopeExp())
+ return te.sds;
+ return null;
+ }
+ // Try to convert Type to symbol
+ if (auto ta = isType(oarg))
+ return ta.toDsymbol(null);
+ return isDsymbol(oarg); // if already a symbol
+}
+
+/**************************************
+ * IsExpression can evaluate the specified type speculatively, and even if
+ * it instantiates any symbols, they are normally unnecessary for the
+ * final executable.
+ * However, if those symbols leak to the actual code, compiler should remark
+ * them as non-speculative to generate their code and link to the final executable.
+ */
+private void unSpeculative(Scope* sc, RootObject o)
+{
+ if (!o)
+ return;
+
+ if (Tuple tup = isTuple(o))
+ {
+ foreach (obj; tup.objects)
+ {
+ unSpeculative(sc, obj);
+ }
+ return;
+ }
+
+ Dsymbol s = getDsymbol(o);
+ if (!s)
+ return;
+
+ if (Declaration d = s.isDeclaration())
+ {
+ if (VarDeclaration vd = d.isVarDeclaration())
+ o = vd.type;
+ else if (AliasDeclaration ad = d.isAliasDeclaration())
+ {
+ o = ad.getType();
+ if (!o)
+ o = ad.toAlias();
+ }
+ else
+ o = d.toAlias();
+
+ s = getDsymbol(o);
+ if (!s)
+ return;
+ }
+
+ if (TemplateInstance ti = s.isTemplateInstance())
+ {
+ // If the instance is already non-speculative,
+ // or it is leaked to the speculative scope.
+ if (ti.minst !is null || sc.minst is null)
+ return;
+
+ // Remark as non-speculative instance.
+ ti.minst = sc.minst;
+ if (!ti.tinst)
+ ti.tinst = sc.tinst;
+
+ unSpeculative(sc, ti.tempdecl);
+ }
+
+ if (TemplateInstance ti = s.isInstantiated())
+ unSpeculative(sc, ti);
+}
private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
}
+ if (exp.rvalue)
+ {
+ if (sc.setUnsafe(false, exp.loc, "moving variable `%s` with `__rvalue`", exp))
+ {
+ setError();
+ }
+ }
scope (exit) result.rvalue = exp.rvalue;
Dsymbol scopesym;
Expression makeNonTemplateItem(Identifier which) {
Expression id = new IdentifierExp(e.loc, Id.empty);
id = new DotIdExp(e.loc, id, Id.object);
- auto moduleNameArgs = new Objects();
- moduleNameArgs.push(new StringExp(e.loc, "core.interpolation"));
+ auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation"));
id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs);
id = new DotIdExp(e.loc, id, which);
id = new CallExp(e.loc, id, new Expressions());
Expression makeTemplateItem(Identifier which, string arg) {
Expression id = new IdentifierExp(e.loc, Id.empty);
id = new DotIdExp(e.loc, id, Id.object);
- auto moduleNameArgs = new Objects();
- moduleNameArgs.push(new StringExp(e.loc, "core.interpolation"));
+ auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation"));
id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs);
- auto tiargs = new Objects();
+
auto templateStringArg = new StringExp(e.loc, arg);
// banning those instead of forwarding them
// templateStringArg.postfix = e.postfix; // forward the postfix to these literals
- tiargs.push(templateStringArg);
+ auto tiargs = new Objects(templateStringArg);
id = new DotTemplateInstanceExp(e.loc, id, which, tiargs);
id = new CallExp(e.loc, id, new Expressions());
return id;
}
- auto arguments = new Expressions();
- arguments.push(makeNonTemplateItem(Id.InterpolationHeader));
+ auto arguments = new Expressions(makeNonTemplateItem(Id.InterpolationHeader));
foreach (idx, str; e.interpolatedSet.parts)
{
else
{
arguments.push(makeTemplateItem(Id.InterpolatedExpression, str));
- Expressions* mix = new Expressions();
- mix.push(new StringExp(e.loc, str));
+ Expressions* mix = new Expressions(new StringExp(e.loc, str));
// FIXME: i'd rather not use MixinExp but idk how to do it lol
arguments.push(new MixinExp(e.loc, mix));
}
result = e;
}
+ void tryLowerAALiteral(AssocArrayLiteralExp aaExp)
+ {
+ auto hookId = Id._d_assocarrayliteralTX;
+ if (!verifyHookExist(aaExp.loc, *sc, hookId, "initializing associative arrays", Id.object))
+ return;
+
+ auto aaType = aaExp.type.toBasetype().isTypeAArray();
+ assert(aaType);
+ Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty);
+ hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object);
+ auto keytype = aaType.index.substWildTo(MODFlags.const_);
+ auto valtype = aaType.nextOf().substWildTo(MODFlags.const_);
+ auto tiargs = new Objects(keytype, valtype);
+ hookFunc = new DotTemplateInstanceExp(aaExp.loc, hookFunc, hookId, tiargs);
+ auto ale1 = new ArrayLiteralExp(aaExp.loc, keytype.arrayOf(), aaExp.keys);
+ auto ale2 = new ArrayLiteralExp(aaExp.loc, valtype.arrayOf(), aaExp.values);
+ lowerArrayLiteral(ale1, sc);
+ lowerArrayLiteral(ale2, sc);
+ auto arguments = new Expressions(ale1, ale2);
+
+ Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments);
+ loweredExp = loweredExp.expressionSemantic(sc);
+ loweredExp = resolveProperties(sc, loweredExp);
+ aaExp.lowering = loweredExp;
+
+ semanticTypeInfo(sc, loweredExp.type);
+ }
+
override void visit(AssocArrayLiteralExp e)
{
static if (LOGSEMANTIC)
{
// already done, but we might have missed generating type info
semanticTypeInfo(sc, e.type);
+ if (!e.lowering)
+ tryLowerAALiteral(e);
result = e;
return;
}
e.type = new TypeAArray(tvalue, tkey);
e.type = e.type.typeSemantic(e.loc, sc);
+ tryLowerAALiteral(e);
semanticTypeInfo(sc, e.type);
if (checkAssocArrayLiteralEscape(*sc, e, false))
*/
Expression id = new IdentifierExp(ne.loc, Id.empty);
id = new DotIdExp(ne.loc, id, Id.object);
- auto tiargs = new Objects();
/*
* Remove `inout`, `const`, `immutable` and `shared` to reduce the
* number of generated `_d_newitemT` instances.
*/
auto t = ne.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ |
MODFlags.immutable_ | MODFlags.shared_);
- tiargs.push(t);
+ auto tiargs = new Objects(t);
+ id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs);
+
+ auto arguments = new Expressions();
+ id = new CallExp(ne.loc, id, arguments);
+
+ ne.lowering = id.expressionSemantic(sc);
+ }
+
+ /**
+ * Sets the `lowering` field of a `NewExp` to a call to `_d_newAA` unless
+ * compiling with `-betterC` or within `__traits(compiles)`.
+ *
+ * Params:
+ * ne = the `NewExp` to lower
+ */
+ private void tryLowerToNewAA(NewExp ne)
+ {
+ if (!global.params.useGC || !sc.needsCodegen())
+ return;
+
+ Identifier hook = Id._d_aaNew;
+ if (!verifyHookExist(ne.loc, *sc, hook, "new AA"))
+ return;
+
+ /* Lower the memory allocation and initialization of `new V[K]` to
+ * `_d_newAA!(V[K])()`.
+ */
+ Expression id = new IdentifierExp(ne.loc, Id.empty);
+ id = new DotIdExp(ne.loc, id, Id.object);
+ auto taa = ne.type.isTypeAArray();
+ assert(taa);
+ auto tiargs = new Objects(taa.index, taa.next);
id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs);
auto arguments = new Expressions();
{
return setError();
}
- if (!exp.placement.type.isNaked())
+ if (!exp.placement.type.isNaked)
{
error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type));
return setError();
else
{
// --> new T[](edim)
- exp.arguments = new Expressions();
- exp.arguments.push(edim);
+ exp.arguments = new Expressions(edim);
exp.type = exp.type.arrayOf();
}
}
Expression id = new IdentifierExp(exp.loc, Id.empty);
id = new DotIdExp(exp.loc, id, Id.object);
- auto tiargs = new Objects();
- tiargs.push(exp.newtype);
+ auto tiargs = new Objects(exp.newtype);
id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs);
Expression id = new IdentifierExp(exp.loc, Id.empty);
id = new DotIdExp(exp.loc, id, Id.object);
- auto tiargs = new Objects();
auto t = exp.newtype.unqualify(MODFlags.wild); // remove `inout`
- tiargs.push(t);
+ auto tiargs = new Objects(t);
id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
auto arguments = new Expressions();
id = new CallExp(exp.loc, id, arguments);
// If the new expression has arguments, we either should call a
// regular constructor of a copy constructor if the first argument
// is the same type as the struct
- if (nargs && (sd.hasRegularCtor() || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf())))
+ if (nargs && (sd.hasRegularCtor(false) || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf())))
{
FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.argumentList, FuncResolveFlag.standard);
if (!f || f.errors)
*/
Expression lowering = new IdentifierExp(exp.loc, Id.empty);
lowering = new DotIdExp(exp.loc, lowering, Id.object);
- auto tiargs = new Objects();
+
/* Remove `inout`, `const`, `immutable` and `shared` to reduce
* the number of generated `_d_newarrayT` instances.
*/
const isShared = exp.type.nextOf.isShared();
auto t = exp.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ |
MODFlags.immutable_ | MODFlags.shared_);
- tiargs.push(t);
+ auto tiargs = new Objects(t);
lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs);
- auto arguments = new Expressions();
- arguments.push((*exp.arguments)[0]);
- arguments.push(new IntegerExp(exp.loc, isShared, Type.tbool));
+ auto arguments = new Expressions((*exp.arguments)[0], new IntegerExp(exp.loc, isShared, Type.tbool));
lowering = new CallExp(exp.loc, lowering, arguments);
exp.lowering = lowering.expressionSemantic(sc);
auto unqualTbn = tbn.unqualify(MODFlags.wild | MODFlags.const_ |
MODFlags.immutable_ | MODFlags.shared_);
- auto tiargs = new Objects();
- tiargs.push(exp.type);
- tiargs.push(unqualTbn);
+ auto tiargs = new Objects(exp.type, unqualTbn);
lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs);
- auto arguments = new Expressions();
-
- arguments.push(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments));
- arguments.push(new IntegerExp(exp.loc, tbn.isShared(), Type.tbool));
+ auto arguments = new Expressions(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments),
+ new IntegerExp(exp.loc, tbn.isShared(), Type.tbool));
lowering = new CallExp(exp.loc, lowering, arguments);
exp.lowering = lowering.expressionSemantic(sc);
error(exp.loc, "`new` cannot take arguments for an associative array");
return setError();
}
+ tryLowerToNewAA(exp);
}
else
{
symtab = sds.symtab;
}
assert(symtab);
- Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars()));
+ Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(const void*) sc.parent);
exp.fd.ident = id;
if (exp.td)
exp.td.ident = id;
{
if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null)
{
- result.rvalue = tf.isRvalue;
- if (tf.isRvalue && !tf.isRef)
+ if (tf.isRvalue)
{
- error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`");
- setError();
+ if(!tf.isRef)
+ {
+ error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`");
+ setError();
+ }
+ else if (sc.setUnsafe(false, exp.loc, "calling `__rvalue`-annotated function `%s`", exp.f))
+ {
+ setError();
+ }
+ else
+ {
+ result.rvalue = true;
+ }
+ }
+ else if (exp.rvalue && tf.isRef)
+ {
+ if (sc.setUnsafe(false, exp.loc, "moving result of `ref` function `%s` with `__rvalue`", exp.f))
+ {
+ setError();
+ }
}
}
}
if (sd.ctor)
{
auto ctor = sd.ctor.isCtorDeclaration();
- if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated())
+ if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated)
sd.ctor = null;
}
If all constructors are copy constructors, then
try default construction.
*/
- if (!sd.hasRegularCtor &&
+ if (!sd.hasRegularCtor(false) &&
// https://issues.dlang.org/show_bug.cgi?id=22639
// we might still have a copy constructor that could be called
(*exp.arguments)[0].type.mutableOf != sd.type.mutableOf())
if (!exp.type)
{
exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922
- // avoid recursive expression printing
- error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg());
+ // avoid recursive expression printing
+ FuncDeclaration fd = null;
+ if (auto dve = exp.e1.isDotVarExp())
+ fd = cast(FuncDeclaration)dve.var;
+ else if (auto ve = exp.e1.isVarExp())
+ fd = cast(FuncDeclaration)ve.var;
+
+ if (fd && fd.inferRetType && sc && sc.func == fd)
+ {
+ error(exp.loc, "can't infer return type in function `%s`", fd.toChars());
+ }
+ else
+ {
+ error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg());
+ }
return setError();
}
if (tf.next.isBaseOf(t, &offset) && offset)
{
exp.type = tf.next;
- result = Expression.combine(argprefix, exp.castTo(sc, t));
+ auto casted_exp = exp.castTo(sc, t);
+ if (auto cex = casted_exp.isCastExp())
+ {
+ lowerCastExp(cex, sc);
+ }
+ result = Expression.combine(argprefix, casted_exp);
return;
}
}
}
// declare dual-context container
- if (exp.f && exp.f.hasDualContext() && !sc.intypeof && sc.func)
+ if (exp.f && exp.f.hasDualContext && !sc.intypeof && sc.func)
{
// check access to second `this`
if (AggregateDeclaration ad2 = exp.f.isMember2())
}
assert(e.op == EXP.assign || e == exp);
- result = (cast(BinExp)e).reorderSettingAAElem(sc);
+ result = e;
}
private Expression compileIt(MixinExp exp, Scope* sc)
// Defensively assume that function calls may have side effects even
// though it's not detected by hasSideEffect (e.g. `debug puts("Hello")` )
// Rewriting CallExp's also avoids some issues with the inliner/debug generation
- if (op.hasSideEffect(true))
+ if (op.hasSideEffect(true) || op.isAssocArrayLiteralExp())
{
// Don't create an invalid temporary for void-expressions
// Further semantic will issue an appropriate error
isEqualsCallExpression)
{
es = new Expressions(3);
- tiargs = new Objects(1);
if (isEqualsCallExpression)
{
Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : EXPtoString(exp.e1.op));
comp = comp.expressionSemantic(sc);
(*es)[0] = comp;
- (*tiargs)[0] = (*es)[1].type;
+ tiargs = new Objects((*es)[1].type);
}
// Format exp.e1 before any additional boolean conversion
else if (op != EXP.andAnd && op != EXP.orOr)
{
es = new Expressions(2);
- tiargs = new Objects(1);
if (auto ne = exp.e1.isNotExp())
{
(*es)[1] = maybePromoteToTmp(exp.e1);
}
- (*tiargs)[0] = (*es)[1].type;
+ tiargs = new Objects((*es)[1].type);
// Passing __ctfe to auto ref infers ref and aborts compilation:
// "cannot modify compiler-generated variable __ctfe"
printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
printAST(exp);
}
+ scope (exit)
+ {
+ if (result)
+ result.rvalue = exp.rvalue;
+ else
+ setError();
+ }
if (sc.inCfile)
{
}
result = e;
// declare dual-context container
- if (f.hasDualContext() && !sc.intypeof && sc.func)
+ if (f.hasDualContext && !sc.intypeof && sc.func)
{
// check access to second `this`
if (AggregateDeclaration ad2 = f.isMember2())
error(exp.loc, "cannot take address of `%s`", exp.e1.toErrMsg());
return setError();
}
- if (!checkAddressable(exp, sc))
+ if (!checkAddressable(exp, sc, "take address of"))
return setError();
bool hasOverloads;
Expression id = new IdentifierExp(exp.loc, Id.empty);
auto dotid = new DotIdExp(exp.loc, id, Id.object);
- auto tiargs = new Objects();
- tiargs.push(tFrom);
- tiargs.push(tTo);
+ auto tiargs = new Objects(tFrom, tTo);
auto dt = new DotTemplateInstanceExp(exp.loc, dotid, Id.__ArrayCast, tiargs);
- auto arguments = new Expressions();
- arguments.push(exp.e1);
+ auto arguments = new Expressions(exp.e1);
Expression ce = new CallExp(exp.loc, dt, arguments);
result = expressionSemantic(ce, sc);
ex.type = exp.to;
}
}
+
+ if (auto cex = ex.isCastExp())
+ {
+ lowerCastExp(cex, sc);
+ }
+
result = ex;
}
}
if (t1b.isStaticOrDynamicArray())
{
- if (!checkAddressable(exp, sc))
+ if (!checkAddressable(exp, sc, "index through"))
return setError();
}
}
semanticTypeInfo(sc, taa);
- checkNewEscape(*sc, exp.e2, false);
+ bool escape = checkNewEscape(*sc, exp.e2, false);
+ if (escape)
+ return setError();
exp.type = taa.next;
+ if (!exp.modifiable)
+ {
+ result = lowerAAIndexRead(exp, sc);
+ return;
+ }
break;
}
case Ttuple:
}
}
+ if (auto ae = exp.e1.isArrayExp())
+ markArrayExpModifiable(ae);
+
if (Expression ex = binSemantic(exp, sc))
{
result = ex;
}
exp.e1 = e1x;
+ Type[2] aliasThisStop;
+ if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop))
+ {
+ result = res;
+ return;
+ }
if (exp.e1.checkReadModifyWrite(exp.op))
return setError();
override void visit(PreExp exp)
{
+ if (auto ae = exp.e1.isArrayExp())
+ markArrayExpModifiable(ae);
+
// printf("PreExp::semantic('%s')\n", toChars());
if (Expression e = exp.opOverloadUnary(sc))
{
if (auto ae = exp.e1.isArrayExp())
{
Expression res;
+ markArrayExpModifiable(ae);
ae.e1 = ae.e1.expressionSemantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);
/* Rewrite (a[i..j] = e2) as:
* a.opSliceAssign(e2, i, j)
*/
- auto a = new Expressions();
- a.push(exp.e2);
+ auto a = new Expressions(exp.e2);
if (ie)
{
a.push(ie.lwr);
Expression e0;
Expression ev = extractSideEffect(sc, "__tup", e0, e2x);
- auto iexps = new Expressions();
- iexps.push(ev);
+ auto iexps = new Expressions(ev);
for (size_t u = 0; u < iexps.length; u++)
{
Lexpand:
auto t = exp.type;
exp = new ConstructExp(exp.loc, exp.e1, exp.e2);
exp.type = t;
-
- // https://issues.dlang.org/show_bug.cgi?id=13515
- // set Index::modifiable flag for complex AA element initialization
- if (auto ie1 = exp.e1.isIndexExp())
- {
- Expression e1x = ie1.markSettingAAElem();
- if (e1x.op == EXP.error)
- {
- result = e1x;
- return;
- }
- }
}
else if (exp.op == EXP.construct && exp.e1.op == EXP.variable &&
(cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_))
checkUnsafeAccess(sc, exp.e2, true, true); // Initializer must always be checked
+ if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop))
+ {
+ result = res;
+ return;
+ }
+
/* If it is an assignment from a 'foreign' type,
* check for operator overloading.
*/
}
else if (exp.op == EXP.assign)
{
- if (e1x.op == EXP.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
- {
- /*
- * Rewrite:
- * aa[key] = e2;
- * as:
- * ref __aatmp = aa;
- * ref __aakey = key;
- * ref __aaval = e2;
- * (__aakey in __aatmp
- * ? __aatmp[__aakey].opAssign(__aaval)
- * : ConstructExp(__aatmp[__aakey], __aaval));
- */
- // ensure we keep the expr modifiable
- Expression esetting = (cast(IndexExp)e1x).markSettingAAElem();
- if (esetting.op == EXP.error)
- {
- result = esetting;
- return;
- }
- assert(esetting.op == EXP.index);
- IndexExp ie = cast(IndexExp) esetting;
- Type t2 = e2x.type.toBasetype();
-
- Expression e0 = null;
- Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1);
- Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2);
- Expression ev = extractSideEffect(sc, "__aaval", e0, e2x);
-
- AssignExp ae = cast(AssignExp)exp.copy();
- ae.e1 = new IndexExp(exp.loc, ea, ek);
- ae.e1 = ae.e1.expressionSemantic(sc);
- ae.e1 = ae.e1.optimize(WANTvalue);
- ae.e2 = ev;
- if (Expression e = ae.opOverloadAssign(sc, aliasThisStop))
- {
- Expression ey = null;
- if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
- {
- ey = ev;
- }
- else if (!ev.implicitConvTo(ie.type) && sd.ctor)
- {
- // Look for implicit constructor call
- // Rewrite as S().ctor(e2)
- ey = new StructLiteralExp(exp.loc, sd, null);
- ey = new DotIdExp(exp.loc, ey, Id.ctor);
- ey = new CallExp(exp.loc, ey, ev);
- ey = ey.trySemantic(sc);
- }
- if (ey)
- {
- Expression ex;
- ex = new IndexExp(exp.loc, ea, ek);
- ex = ex.expressionSemantic(sc);
- ex = ex.modifiableLvalue(sc); // allocate new slot
- ex = ex.optimize(WANTvalue);
-
- ey = new ConstructExp(exp.loc, ex, ey);
- ey = ey.expressionSemantic(sc);
- if (ey.op == EXP.error)
- {
- result = ey;
- return;
- }
- ex = e;
-
- // https://issues.dlang.org/show_bug.cgi?id=14144
- // The whole expression should have the common type
- // of opAssign() return and assigned AA entry.
- // Even if there's no common type, expression should be typed as void.
- if (!typeMerge(sc, EXP.question, ex, ey))
- {
- ex = new CastExp(ex.loc, ex, Type.tvoid);
- ey = new CastExp(ey.loc, ey, Type.tvoid);
- }
- e = new CondExp(exp.loc, new InExp(exp.loc, ek, ea), ex, ey);
- }
- e = Expression.combine(e0, e);
- e = e.expressionSemantic(sc);
- result = e;
- return;
- }
- }
- else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
+ if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
{
result = e;
return;
{
// Ensure e1 is a modifiable lvalue
- auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1);
+ auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
if (ale1x.op == EXP.error)
return setResult(ale1x);
ale.e1 = ale1x;
id = id.expressionSemantic(sc);
// Generate call: _d_arraysetlengthT(e1, e2)
- auto arguments = new Expressions();
- arguments.push(ale.e1); // array
- arguments.push(exp.e2); // new length
+ auto arguments = new Expressions(ale.e1, // array
+ exp.e2); // new length
Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc);
auto res = new LoweredAssignExp(exp, ce);
se = cast(SliceExp)se.e1;
if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray)
{
- se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1);
+ se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
if (se.e1.op == EXP.error)
return setResult(se.e1);
}
// Try to do a decent error message with the expression
// before it gets constant folded
if (exp.op == EXP.assign)
- e1x = e1x.modifiableLvalueImpl(sc, e1old);
+ e1x = e1x.modifiableLvalue(sc, e1old);
e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
t1.isStaticOrDynamicArray() &&
t1.nextOf().toBasetype().ty == Tvoid)
{
- if (t2.nextOf().implicitConvTo(t1.nextOf()))
+ auto t2n = t2.nextOf();
+ if (!t2n)
+ {
+ // filling not allowed
+ error(exp.loc, "cannot copy `%s` to `%s`",
+ t2.toChars(), t1.toChars());
+ }
+ else if (t2n.implicitConvTo(t1.nextOf()))
{
if (sc.setUnsafe(false, exp.loc, "copying `%s` to `%s`", t2, t1))
return setError();
exp.type = exp.e1.type;
assert(exp.type);
auto assignElem = exp.e2;
- auto res = exp.op == EXP.assign ? exp.reorderSettingAAElem(sc) : exp;
- /* https://issues.dlang.org/show_bug.cgi?id=22366
- *
- * `reorderSettingAAElem` creates a tree of comma expressions, however,
- * `checkAssignExp` expects only AssignExps.
- */
- if (res == exp) // no `AA[k] = v` rewrite was performed
- checkAssignEscape(*sc, res, false, false);
- else
- checkNewEscape(*sc, assignElem, false); // assigning to AA puts it on heap
+ Expression res = exp;
+ checkAssignEscape(*sc, res, false, false);
if (auto ae = res.isConstructExp())
{
id = new DotIdExp(exp.loc, id, Id.object);
id = new DotIdExp(exp.loc, id, func);
- auto arguments = new Expressions();
- arguments.push(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
+ auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
if (lowerToArrayCtor)
{
arguments.push(new CastExp(ae.loc, rhs, t2b.nextOf.arrayOf).expressionSemantic(sc));
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));
+ auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf)
+ .expressionSemantic(sc));
Expression eValue2, value2 = ae.e2;
if (isArrayAssign && value2.isLvalue())
Expression ce = new CallExp(ae.loc, id, arguments);
res = Expression.combine(eValue2, ce).expressionSemantic(sc);
if (isArrayAssign)
- res = Expression.combine(res, ae.e1).expressionSemantic(sc);
+ res = Expression.combine(res, ae.e1).expressionSemantic(sc).checkGC(sc);
if (global.params.v.verbose)
message("lowered %s =>\n %s", ae.toChars(), res.toChars());
res = new LoweredAssignExp(ae, res);
res.type = ae.type;
- res = res.checkGC(sc);
return res;
}
if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating()))
{
Expression e0 = null;
- Expression e = exp.reorderSettingAAElem(sc);
+ Expression e = exp;
e = Expression.extractLast(e, e0);
assert(e == exp);
exp.type = exp.e1.type;
auto assignElem = exp.e2;
- auto res = exp.reorderSettingAAElem(sc);
- if (res != exp) // `AA[k] = v` rewrite was performed
- checkNewEscape(*sc, assignElem, false);
- else if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign)
+ auto res = exp;
+ if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign)
checkAssignEscape(*sc, res, false, false);
result = res;
id = new DotIdExp(exp.loc, id, Id.object);
id = new DotIdExp(exp.loc, id, hook);
- auto arguments = new Expressions();
- arguments.push(exp.e1);
- arguments.push(exp.e2);
+ auto arguments = new Expressions(exp.e1, exp.e2);
Expression ce = new CallExp(exp.loc, id, arguments);
exp.lowering = ce.expressionSemantic(sc);
id = new DotIdExp(exp.loc, id, Id.object);
id = new DotIdExp(exp.loc, id, hook);
- auto arguments = new Expressions();
Expression eValue1;
Expression value1 = extractSideEffect(sc, "__appendtmp", eValue1, exp.e1);
- arguments.push(value1);
- arguments.push(new IntegerExp(exp.loc, 1, Type.tsize_t));
+ auto arguments = new Expressions(value1, new IntegerExp(exp.loc, 1, Type.tsize_t));
Expression ce = new CallExp(exp.loc, id, arguments);
* for expressions like `a ~= a[$-1]`. Here, $ will be modified
* by calling `_d_arrayappendcTX`, so we need to save `a[$-1]` in
* a temporary variable.
+ * `__appendtmp*` will be destroyed together with the array `exp.e1`.
*/
- value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true);
-
- // `__appendtmp*` will be destroyed together with the array `exp.e1`.
- auto vd = eValue2.isDeclarationExp().declaration.isVarDeclaration();
- vd.storage_class |= STC.nodtor;
- // Be more explicit that this "declaration" is local to the expression
- vd.storage_class |= STC.exptemp;
+ value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true, STC.nodtor | STC.exptemp);
}
auto ale = new ArrayLengthExp(exp.loc, value1);
Expression id = new IdentifierExp(exp.loc, Id.empty);
id = new DotIdExp(exp.loc, id, Id.object);
- auto tiargs = new Objects();
- tiargs.push(exp.type);
+ auto tiargs = new Objects(exp.type);
id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
id = new CallExp(exp.loc, id, arguments);
- return id.expressionSemantic(sc);
+ return id.expressionSemantic(sc).checkGC(sc);
}
void trySetCatExpLowering(Expression exp)
}
}
- Module mmath = Module.loadStdMath();
+ Module mmath = loadStdMath();
if (!mmath)
{
error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toErrMsg());
return;
}
+ /**
+ * lowers an `InExp` to a call to `_d_aaIn!(V[K])`.
+ *
+ * Params:
+ * ie = the `InExp` to lower
+ * Returns:
+ * the lowered expression or an ErrorExp
+ */
+ private Expression lowerToAAIn(InExp ie)
+ {
+ Identifier hook = Id._d_aaIn;
+ if (!verifyHookExist(ie.loc, *sc, hook, "key in AA"))
+ return ErrorExp.get();
+
+ Expression id = new IdentifierExp(ie.loc, Id.empty);
+ id = new DotIdExp(ie.loc, id, Id.object);
+ auto tiargs = new Objects();
+ id = new DotIdExp(ie.loc, id, hook);
+
+ Expression e1;
+ Expression ekey = extractSideEffect(sc, "__aakey", e1, ie.e1);
+ auto arguments = new Expressions(ie.e2, ekey);
+ auto ce = new CallExp(ie.loc, id, arguments);
+ ce.loweredFrom = ie;
+ e1 = Expression.combine(e1, ce);
+ return e1.expressionSemantic(sc);
+ }
+
+ /**
+ * lowers a `RemoveExp` to a call to `_d_aaDel!(V[K])`.
+ *
+ * Params:
+ * re = the `RemoveExp` to lower
+ * Returns:
+ * the lowered expression or an ErrorExp
+ */
+ private Expression lowerToAADel(RemoveExp re)
+ {
+ Identifier hook = Id._d_aaDel;
+ if (!verifyHookExist(re.loc, *sc, hook, "remove key in AA"))
+ return ErrorExp.get();
+
+ Expression id = new IdentifierExp(re.loc, Id.empty);
+ id = new DotIdExp(re.loc, id, Id.object);
+ auto tiargs = new Objects();
+ id = new DotIdExp(re.loc, id, hook);
+
+ auto arguments = new Expressions(re.e1, re.e2);
+ auto ce = new CallExp(re.loc, id, arguments);
+ ce.loweredFrom = re;
+ return ce.expressionSemantic(sc);
+ }
+
override void visit(InExp exp)
{
if (Expression e = exp.opOverloadBinary(sc, aliasThisStop))
exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
}
+ // for backward compatibility, type accepted during semantic, but errors in glue layer
+ if (exp.e2.isTypeExp())
+ {
+ exp.type = ta.nextOf().pointerTo();
+ break;
+ }
+ result = lowerToAAIn(exp);
+
// even though the glue layer only needs the type info of the index,
// this might be the first time an AA literal is accessed, so check
// the full type info
semanticTypeInfo(sc, ta);
-
- // Return type is pointer to value
- exp.type = ta.nextOf().pointerTo();
- break;
+ return;
}
case Terror:
result = ex;
return;
}
- result = e;
+
+ result = lowerToAADel(e);
+ }
+
+ /**
+ * lower a `EqualExp` to a call to `_d_aaEqual!(aa1, aa2)`.
+ *
+ * Params:
+ * ee = the `EqualExp` to lower
+ * Returns:
+ * the lowered expression or an ErrorExp
+ */
+ private Expression lowerToAAEqual(EqualExp ee)
+ {
+ Identifier hook = Id._d_aaEqual;
+ if (!verifyHookExist(ee.loc, *sc, hook, "compare AAs"))
+ return ErrorExp.get();
+
+ Expression id = new IdentifierExp(ee.loc, Id.empty);
+ id = new DotIdExp(ee.loc, id, Id.object);
+ auto tiargs = new Objects();
+ id = new DotIdExp(ee.loc, id, hook);
+
+ auto arguments = new Expressions(ee.e1, ee.e2);
+ auto ce = new CallExp(ee.loc, id, arguments);
+ if (ee.op == EXP.notEqual)
+ {
+ auto ne = new NotExp(ee.loc, ce);
+ ne.loweredFrom = ee;
+ return ne.expressionSemantic(sc);
+ }
+ ce.loweredFrom = ee;
+ return ce.expressionSemantic(sc);
}
override void visit(EqualExp exp)
Type t1 = exp.e1.type.toBasetype();
Type t2 = exp.e2.type.toBasetype();
- // Indicates whether the comparison of the 2 specified array types
- // requires an object.__equals() lowering.
- static bool needsDirectEq(Type t1, Type t2, Scope* sc)
+ static bool unifyArrayTypes(Type t1, Type t2, Scope* sc)
{
Type t1n = t1.nextOf().toBasetype();
Type t2n = t2.nextOf().toBasetype();
+
if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) ||
(t1n.ty == Tvoid || t2n.ty == Tvoid))
{
- return false;
+ return true;
}
if (t1n.constOf() != t2n.constOf())
+ {
+ return false;
+ }
+
+ Type t = t1n;
+ while (t.toBasetype().nextOf())
+ t = t.nextOf().toBasetype();
+ if (auto ts = t.isTypeStruct())
+ {
+ // semanticTypeInfo() makes sure hasIdentityEquals has been computed
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, ts);
+
+ return !ts.sym.hasIdentityEquals; // has custom opEquals
+ }
+
+ return true;
+ }
+
+ static bool shouldUseMemcmp(Type t1, Type t2, Scope *sc)
+ {
+ Type t1n = t1.nextOf().toBasetype();
+ Type t2n = t2.nextOf().toBasetype();
+ const t1nsz = t1n.size();
+ const t2nsz = t2n.size();
+
+ if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) &&
+ (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) &&
+ t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned())
+ {
return true;
+ }
+ if (t1n.constOf() != t2n.constOf())
+ {
+ return false;
+ }
Type t = t1n;
while (t.toBasetype().nextOf())
if (global.params.useTypeInfo && Type.dtypeinfo)
semanticTypeInfo(sc, ts);
- return ts.sym.hasIdentityEquals; // has custom opEquals
+ auto v = new IsMemcmpableVisitor();
+ ts.accept(v);
+ return v.result;
}
return false;
}
const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray();
- const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc);
- if (!needsArrayLowering)
+ if (!isArrayComparison || unifyArrayTypes(t1, t2, sc))
{
// https://issues.dlang.org/show_bug.cgi?id=23783
if (exp.e1.checkSharedAccess(sc) || exp.e2.checkSharedAccess(sc))
}
// lower some array comparisons to object.__equals(e1, e2)
- if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
+ if (isArrayComparison && !shouldUseMemcmp(t1, t2, sc))
{
//printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars());
return;
}
- if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays"))
+ Identifier hook = Id.__equals;
+ if (!verifyHookExist(exp.loc, *sc, hook, "equal checks on arrays"))
return setError();
- Expression __equals = new IdentifierExp(exp.loc, Id.empty);
- Identifier id = Identifier.idPool("__equals");
- __equals = new DotIdExp(exp.loc, __equals, Id.object);
- __equals = new DotIdExp(exp.loc, __equals, id);
+ Expression lowering = new IdentifierExp(exp.loc, Id.empty);
+ lowering = new DotIdExp(exp.loc, lowering, Id.object);
+ lowering = new DotIdExp(exp.loc, lowering, hook);
/* https://issues.dlang.org/show_bug.cgi?id=23674
*
exp.e1 = exp.e1.optimize(WANTvalue);
exp.e2 = exp.e2.optimize(WANTvalue);
+ auto e1c = exp.e1.copy();
+ auto e2c = exp.e2.copy();
+
+ /* Remove qualifiers from the types of the arguments to reduce the number
+ * of generated `__equals` instances.
+ */
+ static void unqualifyExp(Expression e)
+ {
+ e.type = e.type.unqualify(MODFlags.wild | MODFlags.immutable_ | MODFlags.shared_);
+ auto eNext = e.type.nextOf();
+ if (eNext && !eNext.toBasetype().isTypeStruct())
+ e.type = e.type.unqualify(MODFlags.const_);
+
+ if (!e.isArrayLiteralExp())
+ return;
+
+ if (auto elems = e.isArrayLiteralExp().elements)
+ foreach(elem; *elems)
+ if (elem)
+ unqualifyExp(elem);
+ }
+ unqualifyExp(e1c);
+ unqualifyExp(e2c);
+
auto arguments = new Expressions(2);
- (*arguments)[0] = exp.e1;
- (*arguments)[1] = exp.e2;
+ (*arguments)[0] = e1c;
+ (*arguments)[1] = e2c;
- __equals = new CallExp(exp.loc, __equals, arguments);
+ lowering = new CallExp(exp.loc, lowering, arguments);
if (exp.op == EXP.notEqual)
{
- __equals = new NotExp(exp.loc, __equals);
+ lowering = new NotExp(exp.loc, lowering);
}
- __equals = __equals.trySemantic(sc); // for better error message
- if (!__equals)
+ lowering = lowering.trySemantic(sc); // for better error message
+ if (!lowering)
{
- error(exp.loc, "incompatible types for array comparison: `%s` and `%s`",
- exp.e1.type.toChars(), exp.e2.type.toChars());
- __equals = ErrorExp.get();
+ if (sc.func)
+ error(exp.loc, "can't infer return type in function `%s`", sc.func.toChars());
+ else
+ error(exp.loc, "incompatible types for array comparison: `%s` and `%s`",
+ exp.e1.type.toChars(), exp.e2.type.toChars());
+ lowering = ErrorExp.get();
}
-
- result = __equals;
+ exp.lowering = lowering;
+ result = exp;
return;
}
+ // When array comparison is not lowered to `__equals`, `memcmp` is used, but
+ // GC checks occur before the expression is lowered to `memcmp` in e2ir.d.
+ // Thus, we will consider the literal arrays as on-stack arrays to avoid issues
+ // during GC checks.
+ if (isArrayComparison)
+ {
+ if (auto ale1 = exp.e1.isArrayLiteralExp())
+ {
+ ale1.onstack = true;
+ }
+
+ if (auto ale2 = exp.e2.isArrayLiteralExp())
+ {
+ ale2.onstack = true;
+ }
+ }
+
if (exp.e1.type.toBasetype().ty == Taarray)
{
+ result = lowerToAAEqual(exp);
semanticTypeInfo(sc, exp.e1.type.toBasetype());
+ return;
}
if (!target.isVectorOpSupported(t1, exp.op, t2))
|| e.isTypeExp() // stores its type in the Expression.type field
|| e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal
|| e.isVarExp() // type sometimes gets set already before semantic
- || (e.isAssocArrayLiteralExp() && !e.type.vtinfo) // semanticTypeInfo not run during initialization
+ || (e.isAssocArrayLiteralExp() && // semanticTypeInfo not run during initialization
+ (!e.type.vtinfo || !e.isAssocArrayLiteralExp().lowering))
);
}
Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
return e;
}
- else if ((exp.ident == Id.max || exp.ident == Id.min) &&
- exp.e1.isVarExp() &&
- exp.e1.isVarExp().var.isBitFieldDeclaration())
- {
- // For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
- auto bf = exp.e1.isVarExp().var.isBitFieldDeclaration();
- return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
- }
- else if ((exp.ident == Id.max || exp.ident == Id.min) &&
- exp.e1.isDotVarExp() &&
- exp.e1.isDotVarExp().var.isBitFieldDeclaration())
+ else if ((exp.ident == Id.max || exp.ident == Id.min) && exp.e1.isBitField())
{
// For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
- auto bf = exp.e1.isDotVarExp().var.isBitFieldDeclaration();
+ auto bf = exp.e1.isBitField();
return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
}
else
element = element.resolveLoc(loc, sc);
}
+ if (exp.lowering)
+ exp.lowering.resolveLoc(loc, sc);
+
return exp;
}
/*******************************
* Try to convert an expression to be an lvalue.
*
- * Give error if we're not an lvalue.
+ * Give error if we are not an lvalue.
* Params:
* _this = expression to convert
* sc = scope
* action = for error messages, what the lvalue is needed for (e.g. take address of for `&x`, modify for `x++`)
+ * eorig = original un-lowered expression for error messages, in case of recursive calls; null means use `_this`
* Returns: converted expression, or `ErrorExp` on error
*/
-Expression toLvalue(Expression _this, Scope* sc, const(char)* action)
-{
- return toLvalueImpl(_this, sc, action, _this);
-}
-
-// e = original un-lowered expression for error messages, in case of recursive calls
-private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action, Expression e)
+Expression toLvalue(Expression _this, Scope* sc, const(char)* action, Expression eorig = null)
{
+ //printf("toLvalue() %s\n", _this.toChars());
+ if (!eorig)
+ eorig = _this;
if (!action)
action = "create lvalue of";
- assert(e);
+ assert(eorig);
Expression visit(Expression _this)
{
// BinaryAssignExp does not have an EXP associated
if (_this.isBinAssignExp())
return _this;
if (!_this.loc.isValid())
- _this.loc = e.loc;
+ _this.loc = eorig.loc;
- if (e.op == EXP.type)
- error(_this.loc, "cannot %s type `%s`", action, e.type.toChars());
- else if (e.op == EXP.template_)
- error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toErrMsg());
+ if (eorig.op == EXP.type)
+ error(_this.loc, "cannot %s type `%s`", action, eorig.type.toChars());
+ else if (eorig.op == EXP.template_)
+ error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, eorig.toErrMsg());
else
- error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toErrMsg());
+ error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, eorig.toErrMsg());
return ErrorExp.get();
}
Expression visitInteger(IntegerExp _this)
{
if (!_this.loc.isValid())
- _this.loc = e.loc;
- error(e.loc, "cannot %s constant `%s`", action, e.toErrMsg());
+ _this.loc = eorig.loc;
+ error(eorig.loc, "cannot %s constant `%s`", action, eorig.toErrMsg());
return ErrorExp.get();
}
{
auto e1 = _this.e1;
auto var = _this.var;
- //printf("DotVarExp::toLvalue(%s)\n", toChars());
+ //printf("DotVarExp::toLvalue(%s)\n", _this.toChars());
if (sc && sc.inCfile)
{
/* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
* is an lvalue if the first expression is an lvalue.
*/
- if (!e1.isLvalue())
- return visit(_this);
+ e1 = e1.toLvalue(sc, action, eorig);
+ if (e1.isErrorExp())
+ return e1;
}
if (!_this.isLvalue())
return visit(_this);
Expression visitVectorArray(VectorArrayExp _this)
{
- _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+ _this.e1 = _this.e1.toLvalue(sc, action, eorig);
return _this;
}
Expression visitDelegatePointer(DelegatePtrExp _this)
{
- _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+ _this.e1 = _this.e1.toLvalue(sc, action, eorig);
return _this;
}
Expression visitDelegateFuncptr(DelegateFuncptrExp _this)
{
- _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+ _this.e1 = _this.e1.toLvalue(sc, action, eorig);
return _this;
}
Expression visitCond(CondExp _this)
{
+ if (sc && sc.inCfile)
+ {
+ // C11 6.5.15: A conditional expression does not yield an lvalue.
+ return visit(_this);
+ }
// convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
- CondExp e = cast(CondExp)(_this.copy());
- e.e1 = _this.e1.toLvalue(sc, action).addressOf();
- e.e2 = _this.e2.toLvalue(sc, action).addressOf();
- e.type = _this.type.pointerTo();
- return new PtrExp(_this.loc, e, _this.type);
+ CondExp ecopy = cast(CondExp)(_this.copy());
+ ecopy.e1 = _this.e1.toLvalue(sc, action).addressOf();
+ checkAddressable(ecopy.e1, sc, "take address of");
+ ecopy.e2 = _this.e2.toLvalue(sc, action).addressOf();
+ checkAddressable(ecopy.e2, sc, "take address of");
+ ecopy.type = _this.type.pointerTo();
+ return new PtrExp(_this.loc, ecopy, _this.type);
}
* Params:
* _this = Expression to convert
* sc = scope
+ * eorig = original / un-lowered expression to print in error messages, if null default to `_this`
* Returns: `_this` converted to an lvalue, or an `ErrorExp`
*/
-Expression modifiableLvalue(Expression _this, Scope* sc)
+Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null)
{
- return modifiableLvalueImpl(_this, sc, _this);
-}
+ if (!eorig)
+ eorig = _this;
-// e = original / un-lowered expression to print in error messages
-private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e)
-{
- assert(e);
Expression visit(Expression exp)
{
//printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars());
return ErrorExp.get();
}
}
- return exp.toLvalueImpl(sc, "modify", e);
+ return exp.toLvalue(sc, "modify", eorig);
}
Expression visitString(StringExp exp)
Expression visitComma(CommaExp exp)
{
- exp.e2 = exp.e2.modifiableLvalueImpl(sc, e);
+ exp.e2 = exp.e2.modifiableLvalue(sc, eorig);
return exp;
}
return visit(exp);
}
- Expression visitIndex(IndexExp exp)
- {
- //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars());
- Expression ex = exp.markSettingAAElem();
- if (ex.op == EXP.error)
- return ex;
-
- return visit(exp);
- }
-
Expression visitCond(CondExp exp)
{
if (!exp.e1.isLvalue() && !exp.e2.isLvalue())
case EXP.comma: return visitComma(_this.isCommaExp());
case EXP.delegatePointer: return visitDelegatePtr(_this.isDelegatePtrExp());
case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
- case EXP.index: return visitIndex(_this.isIndexExp());
case EXP.question: return visitCond(_this.isCondExp());
}
}
}
if (sc.func && !sc.intypeof && !v.isDataseg())
{
+ auto msg = (v.storage_class & STC.ref_) ?
+ "taking the address of local variable `%s`" :
+ "taking the address of stack-allocated local variable `%s`";
if (sc.useDIP1000 != FeatureState.enabled &&
!(v.storage_class & STC.temp) &&
- sc.setUnsafe(false, exp.loc, "taking the address of stack-allocated local variable `%s`", v))
+ sc.setUnsafe(false, exp.loc, msg.ptr, v))
{
return false;
}
* Params:
* e = expression to check
* sc = context
+ * action = for error messages, what the address is needed for (e.g. ref return, indexing an array)
* Returns:
* true if the expression is addressable
*/
-bool checkAddressable(Expression e, Scope* sc)
+bool checkAddressable(Expression e, Scope* sc, const(char)* action)
{
+ //printf("checkAddressable() %s\n", e.toChars());
Expression ex = e;
while (true)
{
{
case EXP.dotVariable:
// https://issues.dlang.org/show_bug.cgi?id=22749
- // Error about taking address of any bit-field, regardless of
+ // Error about taking address of any bitfield, regardless of
// whether SCOPE.Cfile is set.
if (auto bf = ex.isDotVarExp().var.isBitFieldDeclaration())
{
- error(e.loc, "cannot take address of bit-field `%s`", bf.toChars());
+ sc.eSink.error(e.loc, "cannot %s bitfield `%s`", action, ex.toErrMsg());
return false;
}
goto case EXP.cast_;
continue;
case EXP.variable:
- if (sc.inCfile)
+ if (sc && sc.inCfile)
{
// C11 6.5.3.2: A variable that has its address taken cannot be
// stored in a register.
// or cast to an lvalue pointer cannot be stored in a register.
if (ex.isVarExp().var.storage_class & STC.register)
{
- if (e.isIndexExp())
- error(e.loc, "cannot index through register variable `%s`", ex.toErrMsg());
- else
- error(e.loc, "cannot take address of register variable `%s`", ex.toErrMsg());
+ sc.eSink.error(e.loc, "cannot %s register variable `%s`", action, ex.toErrMsg());
return false;
}
}
{
n++;
e1 = new VarExp(loc, f.vthis);
- if (f.hasDualContext())
+ if (f.hasDualContext)
{
// (*__this)[i]
if (n > 1)
return true;
const nfields = sd.nonHiddenFields();
- size_t offset = 0;
+ ulong bitoffset = 0;
for (size_t i = 0; i < elements.length; i++)
{
Expression e = (*elements)[i];
return false;
}
VarDeclaration v = sd.fields[i];
- if (v.offset < offset)
+ // Overlap is checked by comparing bit offsets
+ ulong vbitoffset = v.offset * 8;
+ auto vbf = v.isBitFieldDeclaration();
+ if (vbf)
+ vbitoffset += vbf.bitOffset;
+
+ if (vbitoffset < bitoffset)
{
.error(loc, "overlapping initialization for `%s`", v.toChars());
if (!sd.isUnionDeclaration())
const vsize = v.type.size();
if (vsize == SIZE_INVALID)
return false;
- offset = cast(uint)(v.offset + vsize);
+ // Bitsize of types are overridden by any bitfield widths.
+ if (vbf)
+ bitoffset = vbitoffset + vbf.fieldWidth;
+ else
+ bitoffset = vbitoffset + vsize * 8;
Type t = v.type;
if (stype)
if (!t.isBoolean())
{
if (tb != Type.terror)
+ {
error(exp.loc, "expression `%s` of type `%s` does not have a boolean value",
exp.toChars(), t.toChars());
+ if (auto ts = tb.isTypeStruct())
+ errorSupplemental(ts.sym.loc, "perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`");
+ }
return ErrorExp.get();
}
return e;
* postblit. Print the first field that has
* a disabled postblit.
*/
- if (postblit.isGenerated())
+ if (postblit.isGenerated)
{
auto sd = p.isStructDeclaration();
assert(sd);
if (auto ctor = d.isCtorDeclaration())
{
//printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type));
- if (ctor.isCpCtor && ctor.isGenerated())
+ if (ctor.isCpCtor && ctor.isGenerated)
{
.error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars());
return true;
return true;
}
+
+/*******************************************
+ * If variable has a constant expression initializer, get it.
+ * Otherwise, return null.
+ */
+Expression getConstInitializer(VarDeclaration vd, bool needFullType = true)
+{
+ assert(vd.type && vd._init);
+
+ // Ungag errors when not speculative
+ const oldgag = global.gag;
+ if (global.gag)
+ {
+ Dsymbol sym = vd.isMember();
+ if (sym && !sym.isSpeculative())
+ global.gag = 0;
+ }
+
+ if (vd._scope)
+ {
+ vd.inuse++;
+ vd._init = vd._init.initializerSemantic(vd._scope, vd.type, INITinterpret);
+ import dmd.semantic2 : lowerStaticAAs;
+ lowerStaticAAs(vd, vd._scope);
+ vd._scope = null;
+ vd.inuse--;
+ }
+
+ Expression e = vd._init.initializerToExpression(needFullType ? vd.type : null);
+ global.gag = oldgag;
+ return e;
+}
+
/*******************************************
* Helper function for the expansion of manifest constant.
*/
}
e = e.copy();
+ if (auto aae = e.isAssocArrayLiteralExp())
+ aae.lowering = null; // need to redo lowering as it contains temporary variables that must be renamed
e.loc = loc; // for better error message
return e;
}
Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
foreach (i; 0 .. nvars)
{
- foreach (params; pparams)
+ foreach (j, params; pparams)
{
auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param;
- params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null));
+ auto storageClass = j == 2 ? p.storageClass : p.storageClass & ~(STC.manifest | STC.alias_);
+ params.push(new Parameter(aloc, storageClass, p.type, p.ident, null, null));
}
}
Expression[2] res;
sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue);
sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret();
}
- auto s1 = new Statements();
+
auto stmts = new Statements();
if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym));
stmts.push(new ReturnStatement(aloc, res[0]));
- s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)));
- s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
+ auto s1 = new Statements(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)),
+ new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
auto aty = ety.arrayOf();
auto idres = Identifier.generateId("__res");
return v.result;
}
+/*******************************************
+ * Look for constructor declaration.
+ */
+Dsymbol searchCtor(AggregateDeclaration ad)
+{
+ auto s = ad.search(Loc.initial, Id.ctor);
+ if (s)
+ {
+ if (!(s.isCtorDeclaration() ||
+ s.isTemplateDeclaration() ||
+ s.isOverloadSet()))
+ {
+ error(s.loc, "%s name `__ctor` is not allowed", s.kind);
+ errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use");
+ ad.errors = true;
+ s = null;
+ }
+ }
+ if (s && s.toParent() != ad)
+ s = null; // search() looks through ancestor classes
+ if (s)
+ {
+ // Finish all constructors semantics to determine this.noDefaultCtor.
+ static int searchCtor(Dsymbol s, void*)
+ {
+ auto f = s.isCtorDeclaration();
+ if (f && f.semanticRun == PASS.initial)
+ f.dsymbolSemantic(null);
+ return 0;
+ }
+
+ for (size_t i = 0; i < ad.members.length; i++)
+ {
+ auto sm = (*ad.members)[i];
+ sm.apply(&searchCtor, null);
+ }
+ }
+ return s;
+}
+
private extern(C++) class IncludeVisitor : Visitor {
alias visit = Visitor.visit;
result = (sic.inc == Include.yes);
}
}
+
+/***************************************
+* mark ArrayExp on the left side of an assignment recursively as modifiable before
+* semantic analysis to defer potential lowerings into the assignment when
+* the right side of the expression is analyzed, too, i.e.
+*
+* a[i][j] = 1
+*
+* is parsed to
+*
+* AssignExp(ArrayExp(ArrayExp(Id('a'), Id('i')), Id('j')), 1)
+*
+* ArrayExp is converted to IndexExp during semantic analysis, but the lowering
+* for IndexExp on associative arrays is different for reading or writing. For
+* struct types, it can even depend on the right hand side of an assignment.
+*
+* Params:
+* ae = the ArrayExp to mark
+*/
+void markArrayExpModifiable(ArrayExp ae)
+{
+ ae.modifiable = true;
+ for (auto ae1 = ae.e1.isArrayExp(); ae1; ae1 = ae1.e1.isArrayExp())
+ ae1.modifiable = true;
+}
+
+/***************************************
+* convert an IndexExp on an associative array `aa[key]`
+* to `_d_aaGetRvalueX!(K,V)(aa, key)[0]`
+*
+* Params:
+* ie = the IndexExp to lower
+* sc = context
+* Returns:
+* on success, the lowered expression that is still an IndexExp
+* with its `loweredFrom` field set to the original expression
+* on failure, null is returned
+*/
+Expression lowerAAIndexRead(IndexExp ie, Scope* sc)
+{
+ Expression ce = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc);
+ if (!ce)
+ return ie;
+ auto rv = new IndexExp(ie.loc, ce, IntegerExp.literal!0);
+ rv.loweredFrom = ie;
+
+ return rv.expressionSemantic(sc);
+}
+
+// helper for lowerAAIndexRead and revertIndexAssignToRvalues
+// to rewrite `aa[key]` to `_d_aaGetRvalueX!(K,V)(aa, key)[0]`
+private Expression buildAAIndexRValueX(Type t, Expression eaa, Expression ekey, Scope* sc)
+{
+ auto taa = t.toBasetype().isTypeAArray();
+ if (!taa)
+ return null;
+
+ auto loc = eaa.loc;
+ Identifier hook = Id._d_aaGetRvalueX;
+ if (!verifyHookExist(loc, *sc, hook, "indexing AA"))
+ return null;
+
+ Expression func = new IdentifierExp(loc, Id.empty);
+ func = new DotIdExp(loc, func, Id.object);
+ auto tiargs = new Objects(taa.index, taa.next);
+ func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+ Expression e0;
+ auto arguments = new Expressions(eaa, ekey);
+ auto call = new CallExp(loc, func, arguments);
+ e0 = Expression.combine(e0, call);
+
+ if (arrayBoundsCheck(sc.func))
+ {
+ // __aaget = _d_aaGetRvalueX(aa, key), __aaget ? __aaget : onRangeError(__FILE__, __LINE__)
+ auto ei = new ExpInitializer(loc, e0);
+ auto vartmp = Identifier.generateId("__aaget");
+ auto vardecl = new VarDeclaration(loc, null, vartmp, ei, STC.exptemp);
+ auto declexp = new DeclarationExp(loc, vardecl);
+
+ //Expression idrange = new IdentifierExp(loc, Identifier.idPool("_d_arraybounds"));
+ Expression idrange = new IdentifierExp(loc, Id.empty);
+ idrange = new DotIdExp(loc, idrange, Id.object);
+ idrange = new DotIdExp(loc, idrange, Identifier.idPool("_d_arraybounds"));
+ auto locargs = new Expressions(new FileInitExp(loc, EXP.file), new LineInitExp(loc));
+ auto ex = new CallExp(loc, idrange, locargs);
+
+ auto idvar1 = new IdentifierExp(loc, vartmp);
+ auto idvar2 = new IdentifierExp(loc, vartmp);
+ auto cond = new CondExp(loc, idvar1, idvar2, ex);
+ auto comma = new CommaExp(loc, declexp, cond);
+ return comma;
+ }
+ return e0;
+}
+
+/***************************************
+* in a multi-dimensional array assignment without the out-most access being to an AA,
+* convert AA accesses back to rvalues
+*
+* Params:
+* ie = the IndexExp to lower
+* sc = context
+* Returns:
+* the lowered expression if the assignment was actually on an associative array,
+* the original expression otherwise,
+* an ErorrExp on failure
+*/
+Expression revertIndexAssignToRvalues(IndexExp ie, Scope* sc)
+{
+ if (auto ie1 = ie.e1.isIndexExp())
+ {
+ // convert inner access first, otherwise we'd have to crawl into the lowered AST
+ ie.e1 = revertIndexAssignToRvalues(ie1, sc);
+ }
+ if (!ie.e1.type.isTypeAArray())
+ return ie;
+
+ assert(ie.modifiable);
+ return lowerAAIndexRead(ie, sc);
+}
+
+// helper for rewriteAAIndexAssign
+private Expression implicitConvertToStruct(Expression ev, StructDeclaration sd, Scope* sc)
+{
+ Type t2 = ev.type.toBasetype();
+ Expression ey = null;
+ if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
+ {
+ ey = ev;
+ }
+ else if (!ev.implicitConvTo(sd.type) && sd.ctor)
+ {
+ // Look for implicit constructor call
+ // Rewrite as S().ctor(e2)
+ ey = new StructLiteralExp(ev.loc, sd, null);
+ ey = new DotIdExp(ev.loc, ey, Id.ctor);
+ ey = new CallExp(ev.loc, ey, ev);
+ ey = ey.trySemantic(sc);
+ }
+ return ey;
+}
+
+// helper for rewriteIndexAssign
+private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] aliasThisStop)
+{
+ auto ie = exp.e1.isIndexExp();
+ assert(ie);
+ auto loc = ie.e1.loc;
+
+ Identifier hook = Id._d_aaGetY;
+ if (!verifyHookExist(loc, *sc, hook, "modifying AA"))
+ return ErrorExp.get();
+
+ bool escape = checkNewEscape(*sc, exp.e2, false);
+ if (escape)
+ return ErrorExp.get();
+ auto gcexp = exp.checkGC(sc);
+ if (gcexp.op == EXP.error)
+ return gcexp;
+
+ // build `bool __aafound, auto __aaget = _d_aaGetY(aa, key, found);`
+ auto idfound = Identifier.generateId("__aafound");
+ auto varfound = new VarDeclaration(loc, Type.tbool, idfound, null, STC.temp | STC.ctfe);
+ auto declfound = new DeclarationExp(loc, varfound);
+
+ // handle side effects before the actual AA insert
+ Expression e0 = declfound.expressionSemantic(sc);
+ Expression eaa = ie.e1;
+ // find the AA of multi dimensional access
+ for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp())
+ eaa = ieaa.e1;
+ eaa = extractSideEffect(sc, "__aatmp", e0, eaa);
+ // collect all keys of multi dimensional access
+ Expressions ekeys;
+ ekeys.push(ie.e2);
+ for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp())
+ ekeys.push(ieaa.e2);
+ foreach (ekey; ekeys)
+ {
+ Type tidx = ekey.type.toBasetype();
+ if (tidx.ty == Tarray && tidx.nextOf().isMutable())
+ {
+ error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", tidx.toChars());
+ return ErrorExp.get();
+ }
+ }
+ // extract side effects in lexical order
+ for (size_t i = ekeys.length; i > 0; --i)
+ ekeys[i-1] = extractSideEffect(sc, "__aakey", e0, ekeys[i-1]);
+ Expression ev = extractSideEffect(sc, "__aaval", e0, exp.e2);
+
+ // generate series of calls to _d_aaGetY
+ for (size_t i = ekeys.length; i > 0; --i)
+ {
+ auto taa = eaa.type.isTypeAArray();
+ assert (taa); // type must not have changed during rewrite
+ Expression func = new IdentifierExp(loc, Id.empty);
+ func = new DotIdExp(loc, func, Id.object);
+ auto tiargs = new Objects(taa.index, taa.next);
+ func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+ auto arguments = new Expressions(eaa, ekeys[i-1], new IdentifierExp(loc, idfound));
+ eaa = new CallExp(loc, func, arguments);
+ if (i > 1)
+ {
+ // extractSideEffect() cannot be used to ref the IndexExp,
+ // because ConstructExp below will not insert a postblit
+ auto ie1 = new IndexExp(loc, eaa, IntegerExp.literal!0);
+ ie1.modifiable = true;
+ ie1.loweredFrom = eaa;
+ eaa = ie1;
+ }
+ eaa = eaa.expressionSemantic(sc);
+ if (eaa.op == EXP.error)
+ return eaa;
+ }
+ Expression eg = extractSideEffect(sc, "__aaget", e0, eaa);
+ auto ie1 = new IndexExp(loc, eg, IntegerExp.literal!0);
+ ie1.modifiable = true;
+ ie1.loweredFrom = ie;
+ auto ex = ie1.expressionSemantic(sc);
+ ex = ex.optimize(WANTvalue);
+ if (ex.op == EXP.error)
+ return ex;
+ if (!exp.isAssignExp())
+ {
+ // modifying assignments work with zero-initialized inserted value
+ BinExp bex = cast(BinExp)exp.copy();
+ bex.e1 = ex;
+ bex.e2 = ev;
+ ex = Expression.combine(e0, bex);
+ ex.isCommaExp().originalExp = exp;
+ return ex.expressionSemantic(sc);
+ }
+ AssignExp ae = new AssignExp(loc, ex, ev);
+ auto ts = ie.type.isTypeStruct();
+ if (Expression overexp = ts ? ae.opOverloadAssign(sc, aliasThisStop) : null)
+ {
+ if (overexp.op == EXP.error)
+ return overexp;
+ if (auto ey = implicitConvertToStruct(ev, ts.sym, sc))
+ {
+ // __aafound ? __aaget.opAssign(__aaval) : __aaget.ctor(__aaval)
+ ey = new ConstructExp(loc, ex, ey);
+ ey = ey.expressionSemantic(sc);
+ if (ey.op == EXP.error)
+ return ey;
+ ex = overexp;
+
+ // https://issues.dlang.org/show_bug.cgi?id=14144
+ // The whole expression should have the common type
+ // of opAssign() return and assigned AA entry.
+ // Even if there's no common type, expression should be typed as void.
+ if (!typeMerge(sc, EXP.question, ex, ey))
+ {
+ ex = new CastExp(ex.loc, ex, Type.tvoid);
+ ey = new CastExp(ey.loc, ey, Type.tvoid);
+ }
+ Expression condfound = new IdentifierExp(loc, idfound);
+ ex = new CondExp(loc, condfound, ex, ey);
+ ex = Expression.combine(e0, ex);
+ ex.isCommaExp().originalExp = exp;
+ }
+ else
+ {
+ // write back to _aaGetRValueX(aa, key)[0].opAssign(__aaval)
+ auto call = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc);
+ ie1 = new IndexExp(loc, call, IntegerExp.literal!0);
+ ie1.loweredFrom = ie;
+ ex = ie1.expressionSemantic(sc);
+ ex = ex.optimize(WANTvalue);
+ ae = new AssignExp(loc, ex, ev);
+ ex = ae.opOverloadAssign(sc, aliasThisStop);
+ assert(ex);
+ }
+ }
+ else
+ {
+ ex = Expression.combine(e0, ae);
+ ex.isCommaExp().originalExp = exp;
+ }
+ ex = ex.expressionSemantic(sc);
+ return ex;
+}
+
+/***************************************
+* rewrite multi-dimensional array modifying assignments on associative arrays
+*
+* Params:
+* exp = the assignment expression, AssignExp, ConstructExp, BinAssignExp or PostExp
+* sc = context
+* aliasThisStop = for recursion check on `alias this`
+* Returns:
+* the lowered expression if the assignment was actually on an associative array,
+* null if no modifying index expression was found on the lhs of the assignemnt
+*/
+Expression rewriteIndexAssign(BinExp exp, Scope* sc, Type[2] aliasThisStop)
+{
+ if (auto ie1 = exp.e1.isIndexExp())
+ {
+ if (ie1.e1.type.isTypeAArray())
+ {
+ if (exp.op == EXP.construct)
+ exp.e1 = exp.e1.toLvalue(sc, "initialize");
+ else
+ exp.e1 = exp.e1.modifiableLvalue(sc);
+ if (exp.e1.op == EXP.error)
+ return exp.e1;
+ assert(exp.e1 == ie1);
+ assert(ie1.modifiable);
+ return rewriteAAIndexAssign(exp, sc, aliasThisStop);
+ }
+ exp.e1 = revertIndexAssignToRvalues(ie1, sc);
+ }
+ return null;
+}
+
+/****************************************
+ * Get bitfield from expression.
+ */
+BitFieldDeclaration isBitField(Expression e)
+{
+ if (auto ve = e.isVarExp())
+ return ve.var.isBitFieldDeclaration();
+
+ if (auto dve = e.isDotVarExp())
+ return dve.var.isBitFieldDeclaration();
+
+ return null;
+}
whichPathFoundThis = -1;
- // see if we should check for the module locally.
- bool checkLocal = pathCache.pathExists(filename);
- const sdi = FileName.forceExt(filename, hdr_ext);
- if (checkLocal && FileName.exists(sdi) == 1)
- return sdi;
- scope(exit) FileName.free(sdi.ptr);
-
- const sd = FileName.forceExt(filename, mars_ext);
- if (checkLocal && FileName.exists(sd) == 1)
- return sd;
- scope(exit) FileName.free(sd.ptr);
-
- if (checkLocal)
+ // List of extensions to match, in order of precedence.
+ const(char)[][2] extensions = [
+ FileName.forceExt(filename, hdr_ext),
+ FileName.forceExt(filename, mars_ext),
+ ];
+ const(char)[][3] importCextensions = [
+ FileName.forceExt(filename, i_ext),
+ FileName.forceExt(filename, h_ext),
+ FileName.forceExt(filename, c_ext),
+ ];
+ scope(exit)
{
- if (pathCache.isExistingPath(filename))
- {
- /* The filename exists but it's a directory.
- * Therefore, the result should be: filename/package.d
- * iff filename/package.d is a file
- */
- const ni = FileName.combine(filename, package_di);
- if (FileName.exists(ni) == 1)
- return ni;
- FileName.free(ni.ptr);
-
- const n = FileName.combine(filename, package_d);
- if (FileName.exists(n) == 1)
- return n;
- FileName.free(n.ptr);
- }
+ foreach (ext; extensions)
+ FileName.free(ext.ptr);
+ foreach (ext; importCextensions)
+ FileName.free(ext.ptr);
}
- if (FileName.absolute(filename))
- return null;
- if (!pathsInfo.length)
- return null;
- foreach (pathIndex, entry; pathsInfo)
+ /* Search for all combinations of files (mod.di, mod.d, mod/package.d, ...)
+ * within in directory `path`.
+ */
+ const(char)[] lookForSourceFileInPath(const char[] path)
{
- const p = entry.path.toDString();
-
- const(char)[] n = FileName.combine(p, sdi);
-
- if (!pathCache.pathExists(n))
+ // When checking for modules locally, combine won't allocate a new string.
+ bool checkLocal = path is null;
+ void freePath(const(char)[] p)
{
- FileName.free(n.ptr);
- continue; // no need to check for anything else.
+ if (checkLocal)
+ return;
+ FileName.free(p.ptr);
}
- if (FileName.exists(n) == 1)
- return n;
- FileName.free(n.ptr);
+ const p = FileName.combine(path, filename);
+ scope(exit) freePath(p);
+ if (!pathCache.pathExists(p))
+ return null; // no need to check for anything else.
- n = FileName.combine(p, sd);
- if (FileName.exists(n) == 1)
+ // Search for any file matching {path}/{file}.{ext}
+ foreach (ext; extensions)
{
- whichPathFoundThis = pathIndex;
- return n;
+ const file = FileName.combine(path, ext);
+ if (FileName.exists(file) == 1)
+ {
+ import dmd.root.rmem : xarraydup;
+ return checkLocal ? file.xarraydup : file;
+ }
+ freePath(file);
}
- FileName.free(n.ptr);
-
- n = FileName.combine(p, FileName.sansExt(filename));
- scope(exit) FileName.free(n.ptr);
- // also cache this if we are looking for package.d[i]
+ const n = FileName.combine(path, FileName.sansExt(filename));
+ scope(exit) freePath(n);
if (pathCache.isExistingPath(n))
{
- const n2i = FileName.combine(n, package_di);
- if (FileName.exists(n2i) == 1)
- {
- whichPathFoundThis = pathIndex;
- return n2i;
- }
+ /* The filename exists but it's a directory.
+ * Therefore, the result should be: filename/package.d
+ * iff filename/package.d is a file
+ */
+ const ni = FileName.combine(n, package_di);
+ if (FileName.exists(ni) == 1)
+ return ni;
+ FileName.free(ni.ptr);
+
+ const nd = FileName.combine(n, package_d);
+ if (FileName.exists(nd) == 1)
+ return nd;
+ FileName.free(nd.ptr);
+ }
- FileName.free(n2i.ptr);
- const n2 = FileName.combine(n, package_d);
- if (FileName.exists(n2) == 1)
+ /* Search for any file with importC extensions after all attempts
+ to find a D module/package in the path are exhausted. */
+ foreach (ext; importCextensions)
+ {
+ const file = FileName.combine(path, ext);
+ if (FileName.exists(file) == 1)
{
- whichPathFoundThis = pathIndex;
- return n2;
+ import dmd.root.rmem : xarraydup;
+ return checkLocal ? file.xarraydup : file;
}
- FileName.free(n2.ptr);
+ freePath(file);
}
+ return null;
}
- /* ImportC: No D modules found, now search along paths[] for .i file, then .c file.
- */
- const si = FileName.forceExt(filename, i_ext);
- if (FileName.exists(si) == 1)
- return si;
- scope(exit) FileName.free(si.ptr);
-
- const sc = FileName.forceExt(filename, c_ext);
- if (FileName.exists(sc) == 1)
- return sc;
- scope(exit) FileName.free(sc.ptr);
- foreach (pathIndex, entry; pathsInfo)
+ // First see if module is found in any search paths.
+ if (!FileName.absolute(filename))
{
- const p = entry.path.toDString();
-
- const(char)[] n = FileName.combine(p, si);
- if (FileName.exists(n) == 1)
- {
- whichPathFoundThis = pathIndex;
- return n;
- }
- FileName.free(n.ptr);
-
- n = FileName.combine(p, sc);
- if (FileName.exists(n) == 1)
+ foreach (pathIndex, entry; pathsInfo)
{
- whichPathFoundThis = pathIndex;
- return n;
+ if (auto s = lookForSourceFileInPath(entry.path.toDString()))
+ {
+ whichPathFoundThis = pathIndex;
+ return s;
+ }
}
- FileName.free(n.ptr);
}
+ // No modules found, check for the module locally.
+ if (auto s = lookForSourceFileInPath(null))
+ return s;
+
return null;
}
import dmd.arraytypes;
import dmd.astenums;
import dmd.blockexit;
-import dmd.gluelayer;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.dtemplate;
import dmd.escape;
import dmd.expression;
-import dmd.funcsem : overloadApply;
+import dmd.funcsem : isUnique;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
// Things that should really go into Scope
VarDeclaration nrvo_var; /// variable to replace with shidden
- Symbol* shidden; /// hidden pointer passed to function
+ void* shidden; /// hidden pointer passed to function
ReturnStatements* returns;
version (MARS)
{
VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign
- Symbol* salignSection; /// pointer to aligned section, if any
+ void* salignSection; /// pointer to aligned section, if any
}
/// set if this is a known, builtin function we can evaluate at compile time
bool addPreInvariant()
{
auto ad = isThis();
- return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+ return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
}
bool addPostInvariant()
{
auto ad = isThis();
- return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+ return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
}
override const(char)* kind() const
{
- return this.isGenerated() ? "generated function" : "function";
- }
-
- /********************************************
- * Returns:
- * true if there are no overloads of this function
- */
- final bool isUnique() const
- {
- bool result = false;
- overloadApply(cast() this, (Dsymbol s)
- {
- auto f = s.isFuncDeclaration();
- auto td = s.isTemplateDeclaration();
- if (!f && !td)
- return 0;
- if (result)
- {
- result = false;
- return 1; // ambiguous, done
- }
- else
- {
- result = true;
- return 0;
- }
- });
- return result;
+ return this.isGenerated ? "generated function" : "function";
}
/*******************************
override const(char)* kind() const
{
- return isCpCtor ? "copy constructor" : "constructor";
+ return isCpCtor ? "copy constructor" :
+ isMoveCtor ? "move constructor" : "constructor";
}
override bool isVirtual() const
import dmd.astenums;
import dmd.attrib;
import dmd.blockexit;
-import dmd.gluelayer;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
override void visit(TryFinallyStatement s)
{
DtorExpStatement des;
- if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
+ if (fd.isNRVO && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
fd.nrvo_var == des.var)
{
if (!(global.params.useExceptions && ClassDeclaration.throwable))
handler = new CompoundStatement(Loc.initial, handler, ts);
}
- auto catches = new Catches();
auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
ctch.internalCatch = true;
ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o'
- catches.push(ctch);
+ auto catches = new Catches(ctch);
Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches);
fd.hasNoEH = false;
return true;
}
+/********************************************
+ * Returns:
+ * true if there are no overloads of this function
+ */
+bool isUnique(const FuncDeclaration fd)
+{
+ bool result = false;
+ overloadApply(cast() fd, (Dsymbol s)
+ {
+ auto f = s.isFuncDeclaration();
+ auto td = s.isTemplateDeclaration();
+ if (!f && !td)
+ return 0;
+ if (result)
+ {
+ result = false;
+ return 1; // ambiguous, done
+ }
+ else
+ {
+ result = true;
+ return 0;
+ }
+ });
+ return result;
+}
+
/**********************************
* Main semantic routine for functions.
*/
AggregateDeclaration ad = funcdecl.isThis();
// Don't nest structs b/c of generated methods which should not access the outer scopes.
// https://issues.dlang.org/show_bug.cgi?id=16627
- if (ad && !funcdecl.isGenerated())
+ if (ad && !funcdecl.isGenerated)
{
funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_);
ad.makeNested();
sc.linkage = funcdecl._linkage;
- if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
+ if (!tf.isNaked && !(funcdecl.isThis() || funcdecl.isNested()))
{
import core.bitop : popcnt;
auto mods = MODtoChars(tf.mod);
return false;
}
- return !fd.errors && !fd.hasSemantic3Errors();
+ return !fd.errors && !fd.hasSemantic3Errors;
}
// called from semantic3
fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(),
fd.toPrettyChars(), fd.type.toTypeFunction().parameterList.parametersTypeToChars(), fd.type.modToChars());
- const char* where = fd.type.isNaked() ? "parameters" : "type";
+ const char* where = fd.type.isNaked ? "parameters" : "type";
deprecationSupplemental(fd.loc, "Either remove `override`, or adjust the `const` qualifiers of the "
~ "overriding function %s", where);
}
* Returns:
* true if can
*/
-bool canInferAttributes(FuncDeclaration fd, Scope* sc)
+private bool canInferAttributes(FuncDeclaration fd, Scope* sc)
{
if (!fd.fbody)
return false;
return false;
}
-/*********************************************
- * In the current function 'sc.func', we are calling 'fd'.
- * 1. Check to see if the current function can call 'fd' , issue error if not.
- * 2. If the current function is not the parent of 'fd' , then add
- * the current function to the list of siblings of 'fd' .
- * 3. If the current function is a literal, and it's accessing an uplevel scope,
- * then mark it as a delegate.
- * Returns true if error occurs.
- */
-bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc)
-{
- //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars());
- if (auto fld = fd.isFuncLiteralDeclaration())
- {
- if (fld.tok == TOK.reserved)
- {
- fld.tok = TOK.function_;
- fld.vthis = null;
- }
- }
- if (!fd.parent || fd.parent == sc.parent)
- return false;
- if (fd.ident == Id.require || fd.ident == Id.ensure)
- return false;
- if (!fd.isThis() && !fd.isNested())
- return false;
- // The current function
- FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
- if (!fdthis)
- return false; // out of function scope
- Dsymbol p = fd.toParentLocal();
- Dsymbol p2 = fd.toParent2();
- // Function literals from fdthis to p must be delegates
- ensureStaticLinkTo(fdthis, p);
- if (p != p2)
- ensureStaticLinkTo(fdthis, p2);
- if (!fd.isNested())
- return false;
-
- // The function that this function is in
- bool checkEnclosing(FuncDeclaration fdv)
- {
- if (!fdv)
- return false;
- if (fdv == fdthis)
- return false;
- //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
- //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
- //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
- // Add this function to the list of those which called us
- if (fdthis != fd)
- {
- bool found = false;
- for (size_t i = 0; i < fd.siblingCallers.length; ++i)
- {
- if (fd.siblingCallers[i] == fdthis)
- found = true;
- }
- if (!found)
- {
- //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
- if (!sc.intypeof && !sc.traitsCompiles)
- {
- fd.siblingCallers.push(fdthis);
- fd.computedEscapingSiblings = false;
- }
- }
- }
- const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
- if (lv == fd.LevelError)
- return true; // error
- if (lv == -1)
- return false; // downlevel call
- if (lv == 0)
- return false; // same level call
- return false; // Uplevel call
- }
- if (checkEnclosing(p.isFuncDeclaration()))
- return true;
- if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
- return true;
- return false;
-}
-
/****************************************************
* Check whether result variable can be built.
* Returns:
Statement s2 = new ExpStatement(fd.loc, e);
auto c = new Catch(fd.loc, getThrowable(), null, sf);
c.internalCatch = true;
- auto catches = new Catches();
- catches.push(c);
+ auto catches = new Catches(c);
sf = new TryCatchStatement(fd.loc, s2, catches);
}
return sf;
Statement s3 = new CompoundStatement(loc, s2, fail);
auto c = new Catch(loc, getThrowable(), id, s3);
c.internalCatch = true;
- auto catches = new Catches();
- catches.push(c);
+ auto catches = new Catches(c);
sf = new TryCatchStatement(loc, sf, catches);
}
return sf;
extern (D) bool checkNRVO(FuncDeclaration fd)
{
//printf("checkNRVO*() %s\n", fd.ident.toChars());
- if (!fd.isNRVO() || fd.returns is null)
+ if (!fd.isNRVO || fd.returns is null)
return false;
auto tf = fd.type.toTypeFunction();
* Returns:
* true if reference to `tb` is isolated from reference to `ta`
*/
-bool traverseIndirections(Type ta, Type tb)
+private bool traverseIndirections(Type ta, Type tb)
{
//printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars());
* which could have come from the function's parameters, mutable
* globals, or uplevel functions.
*/
-bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t)
+private bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t)
{
//printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars());
assert(t);
return false;
}
+
+/**********************
+ * Check to see if array bounds checking code has to be generated
+ *
+ * Params:
+ * fd = function for which code is to be generated
+ * Returns:
+ * true if do array bounds checking for the given function
+ */
+bool arrayBoundsCheck(FuncDeclaration fd)
+{
+ final switch (global.params.useArrayBounds)
+ {
+ case CHECKENABLE.off:
+ return false;
+ case CHECKENABLE.on:
+ return true;
+ case CHECKENABLE.safeonly:
+ {
+ if (fd)
+ {
+ Type t = fd.type;
+ if (t.ty == Tfunction && (cast(TypeFunction)t).trust == TRUST.safe)
+ return true;
+ }
+ return false;
+ }
+ case CHECKENABLE._default:
+ assert(0);
+ }
+}
extern (C++) struct ImportPathInfo {
const(char)* path; // char*'s of where to look for import modules
+ bool isOutOfBinary; // Will any module found from this path be out of binary?
}
/// Put command line switches in here
Help help;
Verbose v;
+ // Editions
+ Edition edition; // edition year
+ Edition[const(char)*] editionFiles; // Edition corresponding to a filespec
+
+
// Options for `-preview=/-revert=`
FeatureState useDIP25 = FeatureState.enabled; // implement https://wiki.dlang.org/DIP25
FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
enum json_ext = "json"; // for JSON files
enum map_ext = "map"; // for .map files
enum c_ext = "c"; // for C source files
+enum h_ext = "h"; // for C header source files
enum i_ext = "i"; // for preprocessed C source file
/**
errorSinkNull = new ErrorSinkNull;
this.fileManager = new FileManager();
+
version (MARS)
{
compileEnv.vendor = "Digital Mars D";
-
- // -color=auto is the default value
- import dmd.console : detectTerminal, detectColorPreference;
- params.v.color = detectTerminal() && detectColorPreference();
}
else version (IN_GCC)
{
else version (IN_LLVM)
{
compileEnv.vendor = "LDC";
-
- import dmd.console : detectTerminal;
- params.v.color = detectTerminal();
}
+ else
+ static assert(0, "unknown vendor");
- params.v.errorPrintMode = ErrorPrintMode.printErrorContext; // Enable error context globally by default
compileEnv.versionNumber = parseVersionNumber(versionString());
/* Initialize date, time, and timestamp
-
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
struct ImportPathInfo
{
const char* path;
+ d_bool isOutOfBinary;
- ImportPathInfo() : path(NULL) { }
- ImportPathInfo(const char* p) : path(p) { }
+ ImportPathInfo() :
+ path(),
+ isOutOfBinary()
+ {
+ }
+ ImportPathInfo(const char* path, d_bool isOutOfBinary = false) :
+ path(path),
+ isOutOfBinary(isOutOfBinary)
+ {}
};
// Put command line switches in here
Help help;
Verbose v;
+ unsigned short edition; // edition year
+ void* editionFiles; // Edition corresponding to a filespec
+
// 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
+++ /dev/null
-/**
- * Declarations for back-end functions that the front-end invokes.
- *
- * This 'glues' either the DMC or GCC back-end to the front-end.
- *
- * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
- * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
- * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d, _gluelayer.d)
- * Documentation: https://dlang.org/phobos/dmd_gluelayer.html
- * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/gluelayer.d
- */
-
-module dmd.gluelayer;
-
-import dmd.dmodule;
-import dmd.dscope;
-import dmd.dsymbol;
-import dmd.mtype;
-import dmd.statement;
-import dmd.root.file;
-
-version (NoBackend)
-{
- struct Symbol;
- struct code;
- struct block;
- struct BlockState;
- struct elem;
- struct TYPE;
- alias type = TYPE;
-
- extern(C++) abstract class ObjcGlue
- {
- static void initialize() {}
- }
-}
-else version (IN_GCC)
-{
- extern (C++) union tree_node;
-
- alias Symbol = tree_node;
- alias code = tree_node;
- alias type = tree_node;
-
- // stubs
- extern(C++) abstract class ObjcGlue
- {
- static void initialize() {}
- }
-}
-else
-{
- public import dmd.backend.cc : block, BlockState, Symbol;
- public import dmd.backend.type : type;
- public import dmd.backend.el : elem;
- public import dmd.backend.x86.code_x86 : code;
- public import dmd.objc_glue : ObjcGlue;
-}
if (buf.length > maxLength)
{
buf.setsize(maxLength - 3);
- buf.writestring("...");
+ buf.put("...");
}
}
HdrGenState hgs;
OutBuffer buf;
toCBuffer(s, buf, hgs);
- buf.writeByte(0);
+ buf.put(cast(ubyte)0);
return buf.extractSlice().ptr;
}
if (auto tid = d.isTypeInfoDeclaration())
{
OutBuffer buf;
- buf.writestring("typeid(");
- buf.writestring(tid.tinfo.toChars());
- buf.writeByte(')');
+ buf.put("typeid(");
+ buf.put(tid.tinfo.toChars());
+ buf.put(')');
return buf.extractChars();
}
{
if (m.userAttribDecl)
{
- buf.writestring("@(");
+ buf.put("@(");
argsToBuffer(m.userAttribDecl.atts, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
}
if (m.md.isdeprecated)
{
if (m.md.msg)
{
- buf.writestring("deprecated(");
+ buf.put("deprecated(");
m.md.msg.expressionToBuffer(buf, hgs);
- buf.writestring(") ");
+ buf.put(") ");
}
else
- buf.writestring("deprecated ");
+ buf.put("deprecated ");
}
- buf.writestring("module ");
- buf.writestring(m.md.toChars());
- buf.writeByte(';');
+ buf.put("module ");
+ buf.put(m.md.toChars());
+ buf.put(';');
buf.writenl();
}
void visitError(ErrorStatement s)
{
- buf.writestring("__error__");
+ buf.put("__error__");
buf.writenl();
}
}
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
if (!hgs.forStmtInit)
buf.writenl();
}
void visitMixin(MixinStatement s)
{
- buf.writestring("mixin(");
+ buf.put("mixin(");
argsToBuffer(s.exps, buf, hgs, null);
- buf.writestring(");");
+ buf.put(");");
if (!hgs.forStmtInit)
buf.writenl();
}
anywritten = true;
}
}
- buf.writeByte(';');
+ buf.put(';');
if (!hgs.forStmtInit)
buf.writenl();
}
void visitUnrolledLoop(UnrolledLoopStatement s)
{
- buf.writestring("/*unrolled*/ {");
+ buf.put("/*unrolled*/ {");
buf.writenl();
buf.level++;
foreach (sx; *s.statements)
sx.statementToBuffer(buf, hgs);
}
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void visitScope(ScopeStatement s)
{
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (s.statement)
s.statement.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
if (!p.type && !stc)
stc = STC.auto_;
if (stcToBuffer(buf, stc))
- buf.writeByte(' ');
+ buf.put(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
- buf.writestring(p.ident.toString());
- buf.writestring(" = ");
+ buf.put(p.ident.toString());
+ buf.put(" = ");
}
condition.expressionToBuffer(buf, hgs);
}
void visitWhile(WhileStatement s)
{
- buf.writestring("while (");
+ buf.put("while (");
printConditionAssignment(s.param, s.condition);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
if (s._body)
s._body.statementToBuffer(buf, hgs);
void visitDo(DoStatement s)
{
- buf.writestring("do");
+ buf.put("do");
buf.writenl();
if (s._body)
s._body.statementToBuffer(buf, hgs);
- buf.writestring("while (");
+ buf.put("while (");
s.condition.expressionToBuffer(buf, hgs);
- buf.writestring(");");
+ buf.put(");");
buf.writenl();
}
void visitFor(ForStatement s)
{
- buf.writestring("for (");
+ buf.put("for (");
if (s._init)
{
hgs.forStmtInit++;
hgs.forStmtInit--;
}
else
- buf.writeByte(';');
+ buf.put(';');
if (s.condition)
{
- buf.writeByte(' ');
+ buf.put(' ');
s.condition.expressionToBuffer(buf, hgs);
}
- buf.writeByte(';');
+ buf.put(';');
if (s.increment)
{
- buf.writeByte(' ');
+ buf.put(' ');
s.increment.expressionToBuffer(buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void foreachWithoutBody(ForeachStatement s)
{
- buf.writestring(Token.toString(s.op));
- buf.writestring(" (");
+ buf.put(Token.toString(s.op));
+ buf.put(" (");
foreach (i, p; *s.parameters)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (stcToBuffer(buf, p.storageClass))
- buf.writeByte(' ');
+ buf.put(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
- buf.writestring(p.ident.toString());
+ buf.put(p.ident.toString());
}
- buf.writestring("; ");
+ buf.put("; ");
s.aggr.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
}
void visitForeach(ForeachStatement s)
{
foreachWithoutBody(s);
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void foreachRangeWithoutBody(ForeachRangeStatement s)
{
- buf.writestring(Token.toString(s.op));
- buf.writestring(" (");
+ buf.put(Token.toString(s.op));
+ buf.put(" (");
if (s.param.type)
typeToBuffer(s.param.type, s.param.ident, buf, hgs);
else
- buf.writestring(s.param.ident.toString());
- buf.writestring("; ");
+ buf.put(s.param.ident.toString());
+ buf.put("; ");
s.lwr.expressionToBuffer(buf, hgs);
- buf.writestring(" .. ");
+ buf.put(" .. ");
s.upr.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
}
void visitForeachRange(ForeachRangeStatement s)
{
foreachRangeWithoutBody(s);
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void visitStaticForeach(StaticForeachStatement s)
{
- buf.writestring("static ");
+ buf.put("static ");
if (s.sfe.aggrfe)
{
visitForeach(s.sfe.aggrfe);
void visitIf(IfStatement s)
{
- buf.writestring("if (");
+ buf.put("if (");
printConditionAssignment(s.param, s.condition);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
if (s.ifbody.isScopeStatement())
{
}
if (s.elsebody)
{
- buf.writestring("else");
+ buf.put("else");
if (!s.elsebody.isIfStatement())
{
buf.writenl();
}
else
{
- buf.writeByte(' ');
+ buf.put(' ');
}
if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
{
{
s.condition.conditionToBuffer(buf, hgs);
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (s.ifbody)
s.ifbody.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
if (s.elsebody)
{
- buf.writestring("else");
+ buf.put("else");
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.level++;
buf.writenl();
s.elsebody.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
}
buf.writenl();
}
void visitPragma(PragmaStatement s)
{
- buf.writestring("pragma (");
- buf.writestring(s.ident.toString());
+ buf.put("pragma (");
+ buf.put(s.ident.toString());
if (s.args && s.args.length)
{
- buf.writestring(", ");
+ buf.put(", ");
argsToBuffer(s.args, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
if (s._body)
{
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
else
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
}
void visitSwitch(SwitchStatement s)
{
- buf.writestring(s.isFinal ? "final switch (" : "switch (");
+ buf.put(s.isFinal ? "final switch (" : "switch (");
printConditionAssignment(s.param, s.condition);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
if (s._body)
{
if (!s._body.isScopeStatement())
{
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
else
void visitCase(CaseStatement s)
{
- buf.writestring("case ");
+ buf.put("case ");
s.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(':');
+ buf.put(':');
buf.writenl();
s.statement.statementToBuffer(buf, hgs);
}
void visitCaseRange(CaseRangeStatement s)
{
- buf.writestring("case ");
+ buf.put("case ");
s.first.expressionToBuffer(buf, hgs);
- buf.writestring(": .. case ");
+ buf.put(": .. case ");
s.last.expressionToBuffer(buf, hgs);
- buf.writeByte(':');
+ buf.put(':');
buf.writenl();
s.statement.statementToBuffer(buf, hgs);
}
void visitDefault(DefaultStatement s)
{
- buf.writestring("default:");
+ buf.put("default:");
buf.writenl();
s.statement.statementToBuffer(buf, hgs);
}
void visitGotoDefault(GotoDefaultStatement s)
{
- buf.writestring("goto default;");
+ buf.put("goto default;");
buf.writenl();
}
void visitGotoCase(GotoCaseStatement s)
{
- buf.writestring("goto case");
+ buf.put("goto case");
if (s.exp)
{
- buf.writeByte(' ');
+ buf.put(' ');
s.exp.expressionToBuffer(buf, hgs);
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitSwitchError(SwitchErrorStatement s)
{
- buf.writestring("SwitchErrorStatement::toCBuffer()");
+ buf.put("SwitchErrorStatement::toCBuffer()");
buf.writenl();
}
void visitReturn(ReturnStatement s)
{
- buf.writestring("return ");
+ buf.put("return ");
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitBreak(BreakStatement s)
{
- buf.writestring("break");
+ buf.put("break");
if (s.ident)
{
- buf.writeByte(' ');
- buf.writestring(s.ident.toString());
+ buf.put(' ');
+ buf.put(s.ident.toString());
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitContinue(ContinueStatement s)
{
- buf.writestring("continue");
+ buf.put("continue");
if (s.ident)
{
- buf.writeByte(' ');
- buf.writestring(s.ident.toString());
+ buf.put(' ');
+ buf.put(s.ident.toString());
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitSynchronized(SynchronizedStatement s)
{
- buf.writestring("synchronized");
+ buf.put("synchronized");
if (s.exp)
{
- buf.writeByte('(');
+ buf.put('(');
s.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
if (s._body)
{
- buf.writeByte(' ');
+ buf.put(' ');
s._body.statementToBuffer(buf, hgs);
}
}
void visitWith(WithStatement s)
{
- buf.writestring("with (");
+ buf.put("with (");
s.exp.expressionToBuffer(buf, hgs);
- buf.writestring(")");
+ buf.put(")");
buf.writenl();
if (s._body)
s._body.statementToBuffer(buf, hgs);
void visitTryCatch(TryCatchStatement s)
{
- buf.writestring("try");
+ buf.put("try");
buf.writenl();
if (s._body)
{
}
foreach (c; *s.catches)
{
- buf.writestring("catch");
+ buf.put("catch");
if (c.type)
{
- buf.writeByte('(');
+ buf.put('(');
typeToBuffer(c.type, c.ident, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (c.handler)
c.handler.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
}
void visitTryFinally(TryFinallyStatement s)
{
- buf.writestring("try");
+ buf.put("try");
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
s._body.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
- buf.writestring("finally");
+ buf.put("finally");
buf.writenl();
if (s.finalbody.isScopeStatement())
{
void visitScopeGuard(ScopeGuardStatement s)
{
- buf.writestring(Token.toString(s.tok));
- buf.writeByte(' ');
+ buf.put(Token.toString(s.tok));
+ buf.put(' ');
if (s.statement)
s.statement.statementToBuffer(buf, hgs);
}
void visitThrow(ThrowStatement s)
{
- buf.writestring("throw ");
+ buf.put("throw ");
s.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitGoto(GotoStatement s)
{
- buf.writestring("goto ");
- buf.writestring(s.ident.toString());
- buf.writeByte(';');
+ buf.put("goto ");
+ buf.put(s.ident.toString());
+ buf.put(';');
buf.writenl();
}
void visitLabel(LabelStatement s)
{
- buf.writestring(s.ident.toString());
- buf.writeByte(':');
+ buf.put(s.ident.toString());
+ buf.put(':');
buf.writenl();
if (s.statement)
s.statement.statementToBuffer(buf, hgs);
void visitAsm(AsmStatement s)
{
- buf.writestring("asm { ");
+ buf.put("asm { ");
Token* t = s.tokens;
buf.level++;
while (t)
{
- buf.writestring(t.toString());
+ buf.put(t.toString());
if (t.next &&
t.value != TOK.min &&
t.value != TOK.comma && t.next.value != TOK.comma &&
t.next.value != TOK.rightParenthesis &&
t.value != TOK.dot && t.next.value != TOK.dot)
{
- buf.writeByte(' ');
+ buf.put(' ');
}
t = t.next;
}
buf.level--;
- buf.writestring("; }");
+ buf.put("; }");
buf.writenl();
}
{
void visitDsymbol(Dsymbol s)
{
- buf.writestring(s.toChars());
+ buf.put(s.toChars());
}
void visitStaticAssert(StaticAssert s)
{
- buf.writestring(s.kind());
- buf.writeByte('(');
+ buf.put(s.kind());
+ buf.put('(');
s.exp.expressionToBuffer(buf, hgs);
if (s.msgs)
{
foreach (m; (*s.msgs)[])
{
- buf.writestring(", ");
+ buf.put(", ");
m.expressionToBuffer(buf, hgs);
}
}
- buf.writestring(");");
+ buf.put(");");
buf.writenl();
}
void visitDebugSymbol(DebugSymbol s)
{
- buf.writestring("debug = ");
- buf.writestring(s.ident.toString());
- buf.writeByte(';');
+ buf.put("debug = ");
+ buf.put(s.ident.toString());
+ buf.put(';');
buf.writenl();
}
void visitVersionSymbol(VersionSymbol s)
{
- buf.writestring("version = ");
- buf.writestring(s.ident.toString());
- buf.writeByte(';');
+ buf.put("version = ");
+ buf.put(s.ident.toString());
+ buf.put(';');
buf.writenl();
}
if (em.type)
typeToBuffer(em.type, em.ident, buf, hgs);
else
- buf.writestring(em.ident.toString());
+ buf.put(em.ident.toString());
if (em.value)
{
- buf.writestring(" = ");
+ buf.put(" = ");
em.value.expressionToBuffer(buf, hgs);
}
}
if (hgs.hdrgen && imp.id == Id.object)
return; // object is imported by default
if (imp.isstatic)
- buf.writestring("static ");
- buf.writestring("import ");
+ buf.put("static ");
+ buf.put("import ");
if (imp.aliasId)
{
buf.printf("%s = ", imp.aliasId.toChars());
foreach (const pid; imp.packages)
{
buf.write(pid.toString());
- buf.writeByte('.');
+ buf.put('.');
}
- buf.writestring(imp.id.toString());
+ buf.put(imp.id.toString());
if (imp.names.length)
{
- buf.writestring(" : ");
+ buf.put(" : ");
foreach (const i, const name; imp.names)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
const _alias = imp.aliases[i];
if (_alias)
buf.printf("%s = %s", _alias.toChars(), name.toChars());
else
- buf.writestring(name.toChars());
+ buf.put(name.toChars());
}
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitAliasThis(AliasThis d)
{
- buf.writestring("alias ");
- buf.writestring(d.ident.toString());
- buf.writestring(" this;\n");
+ buf.put("alias ");
+ buf.put(d.ident.toString());
+ buf.put(" this;\n");
}
void visitAttribDeclaration(AttribDeclaration d)
if (!d.decl)
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
return;
}
if (d.decl.length == 0 || (hgs.hdrgen && d.decl.length == 1 && (*d.decl)[0].isUnitTestDeclaration()))
{
// hack for https://issues.dlang.org/show_bug.cgi?id=8081
- if (hasSTC) buf.writeByte(' ');
- buf.writestring("{}");
+ if (hasSTC) buf.put(' ');
+ buf.put("{}");
}
else if (d.decl.length == 1)
{
- if (hasSTC) buf.writeByte(' ');
+ if (hasSTC) buf.put(' ');
toCBuffer((*d.decl)[0], buf, hgs);
return;
}
else
{
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (de; *d.decl)
toCBuffer(de, buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
}
buf.writenl();
}
void visitDeprecatedDeclaration(DeprecatedDeclaration d)
{
- buf.writestring("deprecated(");
+ buf.put("deprecated(");
d.msg.expressionToBuffer(buf, hgs);
- buf.writestring(") ");
+ buf.put(") ");
visitAttribDeclaration(d);
}
void visitLinkDeclaration(LinkDeclaration d)
{
- buf.writestring("extern (");
- buf.writestring(linkageToString(d.linkage));
- buf.writestring(") ");
+ buf.put("extern (");
+ buf.put(linkageToString(d.linkage));
+ buf.put(") ");
visitAttribDeclaration(d);
}
case CPPMANGLE.def:
break;
}
- buf.writestring("extern (C++, ");
- buf.writestring(s);
- buf.writestring(") ");
+ buf.put("extern (C++, ");
+ buf.put(s);
+ buf.put(") ");
visitAttribDeclaration(d);
}
visibilityToBuffer(buf, d.visibility);
AttribDeclaration ad = cast(AttribDeclaration)d;
if (ad.decl.length <= 1)
- buf.writeByte(' ');
+ buf.put(' ');
if (ad.decl.length == 1 && (*ad.decl)[0].isVisibilityDeclaration)
visitAttribDeclaration((*ad.decl)[0].isVisibilityDeclaration);
else
foreach (i, exp; (*d.exps)[])
{
if (i)
- buf.writeByte(' ');
- buf.writestring("align (");
+ buf.put(' ');
+ buf.put("align (");
toCBuffer(exp, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
if (d.decl && d.decl.length < 2)
- buf.writeByte(' ');
+ buf.put(' ');
}
else
- buf.writestring("align ");
+ buf.put("align ");
visitAttribDeclaration(d.isAttribDeclaration());
}
void visitAnonDeclaration(AnonDeclaration d)
{
- buf.writestring(d.isunion ? "union" : "struct");
+ buf.put(d.isunion ? "union" : "struct");
buf.writenl();
- buf.writestring("{");
+ buf.put("{");
buf.writenl();
buf.level++;
if (d.decl)
toCBuffer(de, buf, hgs);
}
buf.level--;
- buf.writestring("}");
+ buf.put("}");
buf.writenl();
}
void visitPragmaDeclaration(PragmaDeclaration d)
{
- buf.writestring("pragma (");
- buf.writestring(d.ident.toString());
+ buf.put("pragma (");
+ buf.put(d.ident.toString());
if (d.args && d.args.length)
{
- buf.writestring(", ");
+ buf.put(", ");
argsToBuffer(d.args, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
// https://issues.dlang.org/show_bug.cgi?id=14690
// Unconditionally perform a full output dump
if (d.decl || d.elsedecl)
{
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
if (d.decl)
toCBuffer(de, buf, hgs);
}
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
if (d.elsedecl)
{
buf.writenl();
- buf.writestring("else");
+ buf.put("else");
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (de; *d.elsedecl)
toCBuffer(de, buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
}
}
else
- buf.writeByte(':');
+ buf.put(':');
buf.writenl();
}
{
void foreachWithoutBody(ForeachStatement s)
{
- buf.writestring(Token.toString(s.op));
- buf.writestring(" (");
+ buf.put(Token.toString(s.op));
+ buf.put(" (");
foreach (i, p; *s.parameters)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (stcToBuffer(buf, p.storageClass))
- buf.writeByte(' ');
+ buf.put(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
- buf.writestring(p.ident.toString());
+ buf.put(p.ident.toString());
}
- buf.writestring("; ");
+ buf.put("; ");
s.aggr.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
}
{
/* s.op ( param ; lwr .. upr )
*/
- buf.writestring(Token.toString(s.op));
- buf.writestring(" (");
+ buf.put(Token.toString(s.op));
+ buf.put(" (");
if (s.param.type)
typeToBuffer(s.param.type, s.param.ident, buf, hgs);
else
- buf.writestring(s.param.ident.toString());
- buf.writestring("; ");
+ buf.put(s.param.ident.toString());
+ buf.put("; ");
s.lwr.expressionToBuffer(buf, hgs);
- buf.writestring(" .. ");
+ buf.put(" .. ");
s.upr.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
}
- buf.writestring("static ");
+ buf.put("static ");
if (s.sfe.aggrfe)
{
foreachWithoutBody(s.sfe.aggrfe);
assert(s.sfe.rangefe);
foreachRangeWithoutBody(s.sfe.rangefe);
}
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
visitAttribDeclaration(s);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void visitMixinDeclaration(MixinDeclaration d)
{
- buf.writestring("mixin(");
+ buf.put("mixin(");
argsToBuffer(d.exps, buf, hgs, null);
- buf.writestring(");");
+ buf.put(");");
buf.writenl();
}
void visitUserAttributeDeclaration(UserAttributeDeclaration d)
{
- buf.writestring("@(");
+ buf.put("@(");
argsToBuffer(d.atts, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
visitAttribDeclaration(d);
}
{
if (!constraint)
return;
- buf.writestring(" if (");
+ buf.put(" if (");
constraint.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
/// Returns: whether `do` is needed to write the function body
{
foreach (frequire; *f.frequires)
{
- buf.writestring("in");
+ buf.put("in");
if (auto es = frequire.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
- buf.writestring(" (");
+ buf.put(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
requireDo = false;
}
{
foreach (fensure; *f.fensures)
{
- buf.writestring("out");
+ buf.put("out");
if (auto es = fensure.ensure.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
- buf.writestring(" (");
+ buf.put(" (");
if (fensure.id)
{
- buf.writestring(fensure.id.toString());
+ buf.put(fensure.id.toString());
}
- buf.writestring("; ");
+ buf.put("; ");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
buf.writenl();
requireDo = false;
}
{
if (fensure.id)
{
- buf.writeByte('(');
- buf.writestring(fensure.id.toString());
- buf.writeByte(')');
+ buf.put('(');
+ buf.put(fensure.id.toString());
+ buf.put(')');
}
buf.writenl();
fensure.ensure.statementToBuffer(buf, hgs);
buf.writenl();
contractsToBuffer(f);
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
return;
}
if (requireDo)
{
- buf.writestring("do");
+ buf.put("do");
buf.writenl();
}
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
f.fbody.statementToBuffer(buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
hgs.tpltMember = savetlpt;
hgs.autoMember = saveauto;
if (!d || !d.baseclasses.length)
return;
if (!d.isAnonymous())
- buf.writestring(" : ");
+ buf.put(" : ");
foreach (i, b; *d.baseclasses)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
typeToBuffer(b.type, null, buf, hgs);
}
}
{
assert(fd.type);
if (stcToBuffer(buf, fd.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
visitTemplateConstraint(d.constraint);
hgs.tpltMember++;
}
if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
{
- buf.writestring(ad.kind());
- buf.writeByte(' ');
- buf.writestring(ad.ident.toString());
- buf.writeByte('(');
+ buf.put(ad.kind());
+ buf.put(' ');
+ buf.put(ad.ident.toString());
+ buf.put('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
visitTemplateConstraint(d.constraint);
visitBaseClasses(ad.isClassDeclaration());
hgs.tpltMember++;
if (ad.members)
{
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (s; *ad.members)
toCBuffer(s, buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
}
else
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
hgs.tpltMember--;
return true;
if (d.constraint)
return false;
if (stcToBuffer(buf, vd.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
if (vd.type)
typeToBuffer(vd.type, vd.ident, buf, hgs);
else
- buf.writestring(vd.ident.toString());
- buf.writeByte('(');
+ buf.put(vd.ident.toString());
+ buf.put('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
if (vd._init)
{
- buf.writestring(" = ");
+ buf.put(" = ");
ExpInitializer ie = vd._init.isExpInitializer();
if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
(cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
else
vd._init.initializerToBuffer(buf, hgs);
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
return true;
}
{
// Should handle template functions for doc generation
if (onemember && onemember.isFuncDeclaration())
- buf.writestring("foo ");
+ buf.put("foo ");
}
if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
return;
if (hgs.ddoc)
- buf.writestring(d.kind());
+ buf.put(d.kind());
else
- buf.writestring("template");
- buf.writeByte(' ');
- buf.writestring(d.ident.toString());
- buf.writeByte('(');
+ buf.put("template");
+ buf.put(' ');
+ buf.put(d.ident.toString());
+ buf.put('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
visitTemplateConstraint(d.constraint);
if (hgs.hdrgen || hgs.fullDump)
{
hgs.tpltMember++;
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
toCBuffer(s, buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
hgs.tpltMember--;
}
void visitTemplateInstance(TemplateInstance ti)
{
- buf.writestring(ti.name.toChars());
+ buf.put(ti.name.toChars());
tiargsToBuffer(ti, buf, hgs);
if (hgs.fullDump)
void visitTemplateMixin(TemplateMixin tm)
{
- buf.writestring("mixin ");
+ buf.put("mixin ");
typeToBuffer(tm.tqual, null, buf, hgs);
tiargsToBuffer(tm, buf, hgs);
if (tm.ident && memcmp(tm.ident.toString().ptr, cast(const(char)*) "__mixin", 7) != 0)
{
- buf.writeByte(' ');
- buf.writestring(tm.ident.toString());
+ buf.put(' ');
+ buf.put(tm.ident.toString());
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
if (hgs.fullDump)
dumpTemplateInstance(tm, buf, hgs);
auto oldInEnumDecl = hgs.inEnumDecl;
scope(exit) hgs.inEnumDecl = oldInEnumDecl;
hgs.inEnumDecl = d;
- buf.writestring("enum ");
+ buf.put("enum ");
if (d.ident)
{
- buf.writestring(d.ident.toString());
+ buf.put(d.ident.toString());
}
if (d.memtype)
{
- buf.writestring(" : ");
+ buf.put(" : ");
typeToBuffer(d.memtype, null, buf, hgs);
}
if (!d.members)
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
return;
}
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (em; *d.members)
if (!em)
continue;
toCBuffer(em, buf, hgs);
- buf.writeByte(',');
+ buf.put(',');
buf.writenl();
}
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
if (!hgs.importcHdr || !d.ident)
{
if (!em)
continue;
- buf.writestring("alias ");
- buf.writestring(em.ident.toString);
- buf.writestring(" = ");
- buf.writestring(d.ident.toString);
- buf.writeByte('.');
- buf.writestring(em.ident.toString);
- buf.writeByte(';');
+ buf.put("alias ");
+ buf.put(em.ident.toString);
+ buf.put(" = ");
+ buf.put(d.ident.toString);
+ buf.put('.');
+ buf.put(em.ident.toString);
+ buf.put(';');
buf.writenl();
}
}
void visitNspace(Nspace d)
{
- buf.writestring("extern (C++, ");
- buf.writestring(d.ident.toString());
- buf.writeByte(')');
+ buf.put("extern (C++, ");
+ buf.put(d.ident.toString());
+ buf.put(')');
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
toCBuffer(s, buf, hgs);
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
void visitStructDeclaration(StructDeclaration d)
{
//printf("visitStructDeclaration() %s\n", d.ident.toChars());
- buf.writestring(d.kind());
- buf.writeByte(' ');
+ buf.put(d.kind());
+ buf.put(' ');
if (!d.isAnonymous())
- buf.writestring(d.toChars());
+ buf.put(d.toChars());
if (!d.members)
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
return;
}
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
hgs.insideAggregate++;
toCBuffer(s, buf, hgs);
hgs.insideAggregate--;
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
{
if (!d.isAnonymous())
{
- buf.writestring(d.kind());
- buf.writeByte(' ');
- buf.writestring(d.ident.toString());
+ buf.put(d.kind());
+ buf.put(' ');
+ buf.put(d.ident.toString());
}
visitBaseClasses(d);
if (d.members)
{
buf.writenl();
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
hgs.insideAggregate++;
toCBuffer(s, buf, hgs);
hgs.insideAggregate--;
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
}
else
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
return;
if (d.hidden)
return;
- buf.writestring("alias ");
+ buf.put("alias ");
if (d.aliassym)
{
- buf.writestring(d.ident.toString());
- buf.writestring(" = ");
+ buf.put(d.ident.toString());
+ buf.put(" = ");
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
/*
https://issues.dlang.org/show_bug.cgi?id=23223
https://issues.dlang.org/show_bug.cgi?id=23222
*/
if (d.aliassym.isModule())
{
- buf.writestring(d.aliassym.ident.toString());
+ buf.put(d.aliassym.ident.toString());
}
else
{
else if (d.type.ty == Tfunction)
{
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
typeToBuffer(d.type, d.ident, buf, hgs);
}
else if (d.ident)
{
hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring);
- buf.writestring(d.ident.toString());
- buf.writestring(" = ");
+ buf.put(d.ident.toString());
+ buf.put(" = ");
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
hgs.inCAlias = hgs.importcHdr;
typeToBuffer(d.type, null, buf, hgs);
hgs.inCAlias = false;
hgs.declstring = false;
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitAliasAssign(AliasAssign d)
{
- buf.writestring(d.ident.toString());
- buf.writestring(" = ");
+ buf.put(d.ident.toString());
+ buf.put(" = ");
if (d.aliassym)
toCBuffer(d.aliassym, buf, hgs);
else // d.type
typeToBuffer(d.type, null, buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
if (d.storage_class & STC.local)
return;
visitVarDecl(d, false, buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
// https://issues.dlang.org/show_bug.cgi?id=24891
// return/scope storage classes are printed as part of function type
- if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope)))
- buf.writeByte(' ');
+ if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef)))
+ buf.put(' ');
typeToBuffer(f.type, f.ident, buf, hgs);
auto tf = f.type.isTypeFunction();
buf.writenl();
contractsToBuffer(f);
}
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
else
{
if (f.type.ty == Terror)
{
- buf.writestring("__error");
+ buf.put("__error");
return;
}
if (f.tok != TOK.reserved && !hgs.errorMsg)
{
- buf.writestring(f.kind());
- buf.writeByte(' ');
+ buf.put(f.kind());
+ buf.put(' ');
}
TypeFunction tf = cast(TypeFunction)f.type;
// https://issues.dlang.org/show_bug.cgi?id=20074
void printAttribute(string str)
{
- buf.writeByte(' ');
- buf.writestring(str);
+ buf.put(' ');
+ buf.put(str);
}
if (!hgs.errorMsg)
ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
if (rs && rs.exp)
{
- buf.writestring(" => ");
+ buf.put(" => ");
rs.exp.expressionToBuffer(buf, hgs);
}
else
void visitPostBlitDeclaration(PostBlitDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
- buf.writestring("this(this)");
+ buf.put(' ');
+ buf.put("this(this)");
bodyToBuffer(d);
}
void visitDtorDeclaration(DtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
- buf.writestring("~this()");
+ buf.put(' ');
+ buf.put("~this()");
bodyToBuffer(d);
}
void visitStaticCtorDeclaration(StaticCtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
- buf.writeByte(' ');
+ buf.put(' ');
if (d.isSharedStaticCtorDeclaration())
- buf.writestring("shared ");
- buf.writestring("static this()");
+ buf.put("shared ");
+ buf.put("static this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
else
void visitStaticDtorDeclaration(StaticDtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
- buf.writeByte(' ');
+ buf.put(' ');
if (d.isSharedStaticDtorDeclaration())
- buf.writestring("shared ");
- buf.writestring("static ~this()");
+ buf.put("shared ");
+ buf.put("static ~this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
else
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
- buf.writestring("invariant");
+ buf.put(' ');
+ buf.put("invariant");
auto es = d.fbody.isExpStatement();
if (es && es.exp && es.exp.op == EXP.assert_)
{
- buf.writestring(" (");
+ buf.put(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
- buf.writestring(");");
+ buf.put(");");
buf.writenl();
}
else
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
- buf.writestring("unittest");
+ buf.put(' ');
+ buf.put("unittest");
bodyToBuffer(d);
}
void visitBitFieldDeclaration(BitFieldDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
- buf.writeByte(' ');
+ buf.put(' ');
Identifier id = d.isAnonymous() ? null : d.ident;
typeToBuffer(d.type, id, buf, hgs);
- buf.writestring(" : ");
+ buf.put(" : ");
d.width.expressionToBuffer(buf, hgs);
- buf.writeByte(';');
+ buf.put(';');
buf.writenl();
}
void visitNewDeclaration(NewDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
- buf.writeByte(' ');
- buf.writestring("new();");
+ buf.put(' ');
+ buf.put("new();");
}
void visitModule(Module m)
public
void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, ref HdrGenState hgs)
{
- buf.writestring(td.ident == Id.ctor ? "this" : td.ident.toString());
- buf.writeByte('(');
+ buf.put(td.ident == Id.ctor ? "this" : td.ident.toString());
+ buf.put('(');
foreach (i, const tp; *td.parameters)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
toCBuffer(tp, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
if (hgs.showOneMember && td.onemember)
{
if (TypeFunction tf = cast(TypeFunction)fd.type.isTypeFunction())
{
// !! Casted away const
- buf.writestring(parametersTypeToChars(tf.parameterList));
+ buf.put(parametersTypeToChars(tf.parameterList));
if (tf.mod)
{
- buf.writeByte(' ');
+ buf.put(' ');
buf.MODtoBuffer(tf.mod);
}
}
if (!hgs.skipConstraints &&
td.constraint)
{
- buf.writestring(" if (");
+ buf.put(" if (");
toCBuffer(td.constraint, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
}
foreach (i, p; *parameters)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
toCBuffer(p, buf, hgs);
}
}
const commentIt = hgs.importcHdr && isSpecialCName(v.ident);
if (commentIt)
- buf.writestring("/+");
+ buf.put("/+");
if (anywritten)
{
- buf.writestring(", ");
- buf.writestring(v.ident.toString());
+ buf.put(", ");
+ buf.put(v.ident.toString());
}
else
{
if (useTypeof)
stc &= ~STC.auto_;
if (stcToBuffer(buf, stc))
- buf.writeByte(' ');
+ buf.put(' ');
if (v.type)
typeToBuffer(v.type, v.ident, buf, hgs);
else if (useTypeof)
{
- buf.writestring("typeof(");
+ buf.put("typeof(");
vinit(v);
- buf.writestring(") ");
- buf.writestring(v.ident.toString());
+ buf.put(") ");
+ buf.put(v.ident.toString());
}
else
- buf.writestring(v.ident.toString());
+ buf.put(v.ident.toString());
}
if (v._init && !isextern)
{
- buf.writestring(" = ");
+ buf.put(" = ");
vinit(v);
}
if (commentIt)
- buf.writestring("+/");
+ buf.put("+/");
}
/*************************************
{
void visit(Expression e)
{
- buf.writestring(EXPtoString(e.op));
+ buf.put(EXPtoString(e.op));
}
void visitInteger(IntegerExp e)
break;
}
case Tint8:
- buf.writestring("cast(byte)");
+ buf.put("cast(byte)");
goto L2;
case Tint16:
- buf.writestring("cast(short)");
+ buf.put("cast(short)");
goto L2;
case Tint32:
L2:
buf.printf("%d", cast(int)v);
break;
case Tuns8:
- buf.writestring("cast(ubyte)");
+ buf.put("cast(ubyte)");
goto case Tuns32;
case Tuns16:
- buf.writestring("cast(ushort)");
+ buf.put("cast(ushort)");
goto case Tuns32;
case Tuns32:
buf.printf("%uu", cast(uint)v);
// 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");
+ buf.put("cast(long)-9223372036854775808");
}
else
{
buf.printf("%lluLU", v);
break;
case Tbool:
- buf.writestring(v ? "true" : "false");
+ buf.put(v ? "true" : "false");
break;
case Tpointer:
- buf.writestring("cast(");
+ buf.put("cast(");
HdrGenState hgs2; // should re-examine need for new hgs
hgs2.fullQual = (t.ty == Tclass && !t.mod);
toCBuffer(t, buf, null, hgs2);
- buf.writestring(")cast(size_t)");
+ buf.put(")cast(size_t)");
goto case Tuns64;
case Tvoid:
- buf.writestring("cast(void)0");
+ buf.put("cast(void)0");
break;
default:
void visitError(ErrorExp e)
{
- buf.writestring("__error");
+ buf.put("__error");
}
void visitVoidInit(VoidInitExp e)
{
- buf.writestring("void");
+ buf.put("void");
}
void floatToBuffer(Type type, real_t value)
/* Print as:
* (re+imi)
*/
- buf.writeByte('(');
+ buf.put('(');
floatToBuffer(e.type, creall(e.value));
- buf.writeByte('+');
+ buf.put('+');
floatToBuffer(e.type, cimagl(e.value));
- buf.writestring("i)");
+ buf.put("i)");
}
void visitIdentifier(IdentifierExp e)
{
if (hgs.hdrgen || hgs.ddoc)
- buf.writestring(e.ident.toHChars2());
+ buf.put(e.ident.toHChars2());
else
- buf.writestring(e.ident.toString());
+ buf.put(e.ident.toString());
}
void visitDsymbol(Dsymbol s)
// This condition is a bit kludge, and can be cleaned up if the
// mutual dependency `AST.toChars <> hdrgen.d` gets refactored
if (hgs.vcg_ast && s.ident && !s.isTemplateInstance() && !s.isTemplateDeclaration())
- buf.writestring(s.ident.toChars());
+ buf.put(s.ident.toChars());
else
- buf.writestring(s.toChars());
+ buf.put(s.toChars());
}
void visitDsymbolExp(DsymbolExp e)
void visitThis(ThisExp e)
{
- buf.writestring("this");
+ buf.put("this");
}
void visitSuper(SuperExp e)
{
- buf.writestring("super");
+ buf.put("super");
}
void visitNull(NullExp e)
{
- buf.writestring("null");
+ buf.put("null");
}
void visitString(StringExp e)
{
if (e.hexString || e.sz == 8)
{
- buf.writeByte('x');
- buf.writeByte('"');
+ buf.put('x');
+ buf.put('"');
foreach (i; 0 .. e.len)
buf.printf("%0*llX", e.sz, e.getIndex(i));
- buf.writeByte('"');
+ buf.put('"');
if (e.postfix)
- buf.writeByte(e.postfix);
+ buf.put(e.postfix);
return;
}
- buf.writeByte('"');
+ buf.put('"');
const o = buf.length;
foreach (i; 0 .. e.len)
{
}
if (hgs.ddoc)
escapeDdocString(buf, o);
- buf.writeByte('"');
+ buf.put('"');
if (e.postfix)
- buf.writeByte(e.postfix);
+ buf.put(e.postfix);
}
void visitInterpolation(InterpExp e)
{
- buf.writeByte('i');
- buf.writeByte('"');
+ buf.put('i');
+ buf.put('"');
const o = buf.length;
foreach (idx, str; e.interpolatedSet.parts)
}
else
{
- buf.writeByte('$');
- buf.writeByte('(');
+ buf.put('$');
+ buf.put('(');
foreach(ch; str)
- buf.writeByte(ch);
- buf.writeByte(')');
+ buf.put(ch);
+ buf.put(')');
}
}
if (hgs.ddoc)
escapeDdocString(buf, o);
- buf.writeByte('"');
+ buf.put('"');
if (e.postfix)
- buf.writeByte(e.postfix);
+ buf.put(e.postfix);
}
void visitArrayLiteral(ArrayLiteralExp e)
{
- buf.writeByte('[');
+ buf.put('[');
argsToBuffer(e.elements, buf, hgs, e.basis);
- buf.writeByte(']');
+ buf.put(']');
}
void visitAssocArrayLiteral(AssocArrayLiteralExp e)
{
- buf.writeByte('[');
+ if (hgs.vcg_ast && e.lowering)
+ {
+ expToBuffer(e.lowering, PREC.assign, buf, hgs);
+ return;
+ }
+ buf.put('[');
foreach (i, key; *e.keys)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
expToBuffer(key, PREC.assign, buf, hgs);
- buf.writeByte(':');
+ buf.put(':');
auto value = (*e.values)[i];
expToBuffer(value, PREC.assign, buf, hgs);
}
- buf.writeByte(']');
+ buf.put(']');
}
void visitStructLiteral(StructLiteralExp e)
{
- buf.writestring(e.sd.toChars());
- buf.writeByte('(');
+ buf.put(e.sd.toChars());
+ buf.put('(');
// CTFE can generate struct literals that contain an AddrExp pointing
// to themselves, need to avoid infinite recursion:
// struct S { this(int){ this.s = &this; } S* s; }
// const foo = new S(0);
if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer)
- buf.writestring("<recursion>");
+ buf.put("<recursion>");
else
{
const old = e.stageflags;
argsToBuffer(e.elements, buf, hgs);
e.stageflags = old;
}
- buf.writeByte(')');
+ buf.put(')');
}
void visitCompoundLiteral(CompoundLiteralExp e)
{
- buf.writeByte('(');
+ buf.put('(');
typeToBuffer(e.type, null, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
e.initializer.initializerToBuffer(buf, hgs);
}
{
// fixes bug 6491
if (auto m = e.sds.isModule())
- buf.writestring(m.md.toChars());
+ buf.put(m.md.toChars());
else
- buf.writestring(e.sds.toChars());
+ buf.put(e.sds.toChars());
}
else
{
- buf.writestring(e.sds.kind());
- buf.writeByte(' ');
- buf.writestring(e.sds.toChars());
+ buf.put(e.sds.kind());
+ buf.put(' ');
+ buf.put(e.sds.toChars());
}
}
void visitTemplate(TemplateExp e)
{
- buf.writestring(e.td.toChars());
+ buf.put(e.td.toChars());
}
void visitNew(NewExp e)
{
+ if (hgs.vcg_ast && e.lowering)
+ {
+ expToBuffer(e.lowering, PREC.primary, buf, hgs);
+ return;
+ }
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
}
- buf.writestring("new ");
+ buf.put("new ");
if (e.placement)
{
- buf.writeByte('(');
+ buf.put('(');
expToBuffer(e.placement, PREC.assign, buf, hgs);
- buf.writeByte(')');
- buf.writeByte(' ');
+ buf.put(')');
+ buf.put(' ');
}
typeToBuffer(e.newtype, null, buf, hgs);
if (e.arguments && e.arguments.length)
{
- buf.writeByte('(');
+ buf.put('(');
argsToBuffer(e.arguments, buf, hgs, null, e.names);
- buf.writeByte(')');
+ buf.put(')');
}
}
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
}
- buf.writestring("new");
+ buf.put("new");
if (e.placement)
{
- buf.writeByte(' ');
- buf.writeByte('(');
+ buf.put(' ');
+ buf.put('(');
expToBuffer(e.placement, PREC.assign, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
- buf.writestring(" class ");
+ buf.put(" class ");
if (e.arguments && e.arguments.length)
{
- buf.writeByte('(');
+ buf.put('(');
argsToBuffer(e.arguments, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
if (e.cd)
e.cd.dsymbolToBuffer(buf, hgs);
if (e.offset)
buf.printf("(& %s + %llu)", e.var.toChars(), e.offset);
else if (e.var.isTypeInfoDeclaration())
- buf.writestring(e.var.toChars());
+ buf.put(e.var.toChars());
else
buf.printf("& %s", e.var.toChars());
}
void visitOver(OverExp e)
{
- buf.writestring(e.vars.ident.toString());
+ buf.put(e.vars.ident.toString());
}
void visitTuple(TupleExp e)
{
if (e.e0)
{
- buf.writeByte('(');
+ buf.put('(');
e.e0.expressionPrettyPrint(buf, hgs);
- buf.writestring(", AliasSeq!(");
+ buf.put(", AliasSeq!(");
argsToBuffer(e.exps, buf, hgs);
- buf.writestring("))");
+ buf.put("))");
}
else
{
- buf.writestring("AliasSeq!(");
+ buf.put("AliasSeq!(");
argsToBuffer(e.exps, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
}
void visitFunc(FuncExp e)
{
e.fd.dsymbolToBuffer(buf, hgs);
- //buf.writestring(e.fd.toChars());
+ //buf.put(e.fd.toChars());
}
void visitDeclaration(DeclarationExp e)
// - Avoid printing newline.
// - Intentionally use the format (Type var;)
// which isn't correct as regular D code.
- buf.writeByte('(');
+ buf.put('(');
visitVarDecl(var, false, buf, hgs);
- buf.writeByte(';');
- buf.writeByte(')');
+ buf.put(';');
+ buf.put(')');
}
else e.declaration.dsymbolToBuffer(buf, hgs);
}
void visitTypeid(TypeidExp e)
{
- buf.writestring("typeid(");
+ buf.put("typeid(");
objectToBuffer(e.obj, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
void visitTraits(TraitsExp e)
{
- buf.writestring("__traits(");
+ buf.put("__traits(");
if (e.ident)
- buf.writestring(e.ident.toString());
+ buf.put(e.ident.toString());
if (e.args)
{
foreach (arg; *e.args)
{
- buf.writestring(", ");
+ buf.put(", ");
objectToBuffer(arg, buf, hgs);
}
}
- buf.writeByte(')');
+ buf.put(')');
}
void visitHalt(HaltExp e)
{
- buf.writestring("halt");
+ buf.put("halt");
}
void visitIs(IsExp e)
{
- buf.writestring("is(");
+ buf.put("is(");
typeToBuffer(e.targ, e.id, buf, hgs);
if (e.tok2 != TOK.reserved)
{
- buf.writeByte(' ');
- buf.writestring(Token.toString(e.tok));
- buf.writeByte(' ');
- buf.writestring(Token.toString(e.tok2));
+ buf.put(' ');
+ buf.put(Token.toString(e.tok));
+ buf.put(' ');
+ buf.put(Token.toString(e.tok2));
}
else if (e.tspec)
{
if (e.tok == TOK.colon)
- buf.writestring(" : ");
+ buf.put(" : ");
else
- buf.writestring(" == ");
+ buf.put(" == ");
typeToBuffer(e.tspec, null, buf, hgs);
}
if (e.parameters && e.parameters.length)
{
- buf.writestring(", ");
+ buf.put(", ");
visitTemplateParameters(e.parameters, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
}
void visitUna(UnaExp e)
{
- buf.writestring(EXPtoString(e.op));
+ buf.put(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitBin(BinExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
- buf.writeByte(' ');
- buf.writestring(EXPtoString(e.op));
- buf.writeByte(' ');
+ buf.put(' ');
+ buf.put(EXPtoString(e.op));
+ buf.put(' ');
expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
}
if (commaExtract)
{
- expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
+ expToBuffer(commaExtract, expPrecedence(hgs, exp), buf, hgs);
return;
}
}
void visitMixin(MixinExp e)
{
- buf.writestring("mixin(");
+ buf.put("mixin(");
argsToBuffer(e.exps, buf, hgs, null);
- buf.writeByte(')');
+ buf.put(')');
}
void visitImport(ImportExp e)
{
- buf.writestring("import(");
+ buf.put("import(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
void visitAssert(AssertExp e)
{
- buf.writestring("assert(");
+ buf.put("assert(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
if (e.msg)
{
- buf.writestring(", ");
+ buf.put(", ");
expToBuffer(e.msg, PREC.assign, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
}
void visitThrow(ThrowExp e)
{
- buf.writestring("throw ");
+ buf.put("throw ");
expToBuffer(e.e1, PREC.unary, buf, hgs);
}
void visitDotId(DotIdExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
- buf.writestring(e.ident.toString());
+ buf.put('.');
+ buf.put(e.ident.toString());
}
void visitDotTemplate(DotTemplateExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
- buf.writestring(e.td.toChars());
+ buf.put('.');
+ buf.put(e.td.toChars());
}
void visitDotVar(DotVarExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
visitDsymbol(e.var);
}
void visitDotTemplateInstance(DotTemplateInstanceExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
e.ti.dsymbolToBuffer(buf, hgs);
}
void visitDelegate(DelegateExp e)
{
- buf.writeByte('&');
+ buf.put('&');
if (!e.func.isNested() || e.func.needThis())
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
}
- buf.writestring(e.func.toChars());
+ buf.put(e.func.toChars());
}
void visitDotType(DotTypeExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
- buf.writestring(e.sym.toChars());
+ buf.put('.');
+ buf.put(e.sym.toChars());
}
void visitCall(CallExp e)
e.e1.expressionPrettyPrint(buf, hgs);
}
else
+ {
+ if (!hgs.vcg_ast && e.loweredFrom)
+ {
+ // restore original syntax for expressions lowered to calls
+ expressionToBuffer(e.loweredFrom, buf, hgs);
+ return;
+ }
expToBuffer(e.e1, precedence[e.op], buf, hgs);
- buf.writeByte('(');
+ }
+ buf.put('(');
argsToBuffer(e.arguments, buf, hgs, null, e.names);
- buf.writeByte(')');
+ buf.put(')');
+ }
+
+ void visitNot(NotExp e)
+ {
+ if (!hgs.vcg_ast && e.loweredFrom)
+ return expressionToBuffer(e.loweredFrom, buf, hgs);
+ return visitUna(e);
}
void visitPtr(PtrExp e)
{
- buf.writeByte('*');
+ buf.put('*');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitDelete(DeleteExp e)
{
- buf.writestring("delete ");
+ buf.put("delete ");
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitCast(CastExp e)
{
- buf.writestring("cast(");
+ buf.put("cast(");
if (e.to)
typeToBuffer(e.to, null, buf, hgs);
else
{
MODtoBuffer(buf, e.mod);
}
- buf.writeByte(')');
+ buf.put(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitVector(VectorExp e)
{
- buf.writestring("cast(");
+ buf.put("cast(");
typeToBuffer(e.to, null, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitVectorArray(VectorArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writestring(".array");
+ buf.put(".array");
}
void visitSlice(SliceExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
if (e.upr || e.lwr)
{
if (e.lwr)
sizeToBuffer(e.lwr, buf, hgs);
else
- buf.writeByte('0');
- buf.writestring("..");
+ buf.put('0');
+ buf.put("..");
if (e.upr)
sizeToBuffer(e.upr, buf, hgs);
else
- buf.writeByte('$');
+ buf.put('$');
}
- buf.writeByte(']');
+ buf.put(']');
}
void visitArrayLength(ArrayLengthExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writestring(".length");
+ buf.put(".length");
}
void visitInterval(IntervalExp e)
{
expToBuffer(e.lwr, PREC.assign, buf, hgs);
- buf.writestring("..");
+ buf.put("..");
expToBuffer(e.upr, PREC.assign, buf, hgs);
}
void visitDelegatePtr(DelegatePtrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writestring(".ptr");
+ buf.put(".ptr");
}
void visitDelegateFuncptr(DelegateFuncptrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writestring(".funcptr");
+ buf.put(".funcptr");
}
void visitArray(ArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
argsToBuffer(e.arguments, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
void visitDot(DotExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ buf.put('.');
expToBuffer(e.e2, PREC.primary, buf, hgs);
}
+ void visitCat(CatExp e)
+ {
+ if (hgs.vcg_ast && e.lowering)
+ expressionToBuffer(e.lowering, buf, hgs);
+ else
+ visitBin(e);
+ }
+
+ void visitCatAssign(CatAssignExp e)
+ {
+ if (hgs.vcg_ast && e.lowering)
+ expressionToBuffer(e.lowering, buf, hgs);
+ else
+ visitBin(e);
+ }
+
void visitIndex(IndexExp e)
{
+ if (!hgs.vcg_ast && e.loweredFrom)
+ {
+ expressionToBuffer(e.loweredFrom, buf, hgs);
+ return;
+ }
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
sizeToBuffer(e.e2, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
void visitPost(PostExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
- buf.writestring(EXPtoString(e.op));
+ buf.put(EXPtoString(e.op));
}
void visitPre(PreExp e)
{
- buf.writestring(EXPtoString(e.op));
+ buf.put(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitRemove(RemoveExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writestring(".remove(");
+ buf.put(".remove(");
expToBuffer(e.e2, PREC.assign, buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
void visitCond(CondExp e)
{
expToBuffer(e.econd, PREC.oror, buf, hgs);
- buf.writestring(" ? ");
+ buf.put(" ? ");
expToBuffer(e.e1, PREC.expr, buf, hgs);
- buf.writestring(" : ");
+ buf.put(" : ");
expToBuffer(e.e2, PREC.cond, buf, hgs);
}
void visitDefaultInit(DefaultInitExp e)
{
- buf.writestring(EXPtoString(e.op));
+ buf.put(EXPtoString(e.op));
}
void visitClassReference(ClassReferenceExp e)
{
- buf.writestring(e.value.toChars());
+ buf.put(e.value.toChars());
}
if (e.rvalue)
- buf.writestring("__rvalue(");
+ buf.put("__rvalue(");
scope (exit)
if (e.rvalue)
- buf.writeByte(')');
+ buf.put(')');
switch (e.op)
{
case EXP.delegate_: return visitDelegate(e.isDelegateExp());
case EXP.dotType: return visitDotType(e.isDotTypeExp());
case EXP.call: return visitCall(e.isCallExp());
+ case EXP.not: return visitNot(e.isNotExp());
case EXP.star: return visitPtr(e.isPtrExp());
case EXP.delete_: return visitDelete(e.isDeleteExp());
case EXP.cast_: return visitCast(e.isCastExp());
case EXP.array: return visitArray(e.isArrayExp());
case EXP.dot: return visitDot(e.isDotExp());
case EXP.index: return visitIndex(e.isIndexExp());
+ case EXP.concatenate: return visitCat(e.isCatExp());
+ case EXP.concatenateAssign: return visitCatAssign(e.isCatAssignExp());
+ case EXP.concatenateElemAssign: return visitCatAssign(e.isCatElemAssignExp());
+ case EXP.concatenateDcharAssign: return visitCatAssign(e.isCatDcharAssignExp());
case EXP.minusMinus:
case EXP.plusPlus: return visitPost(e.isPostExp());
case EXP.preMinusMinus:
if (r != value) // if exact duplication
CTFloat.sprint(buffer.ptr, BUFFER_LEN, 'a', value);
}
- buf.writestring(buffer.ptr);
+ buf.put(buffer.ptr);
if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
buf.remove(buf.length() - 1, 1);
case Tfloat32:
case Timaginary32:
case Tcomplex32:
- buf.writeByte('F');
+ buf.put('F');
break;
case Tfloat80:
case Timaginary80:
case Tcomplex80:
- buf.writeByte('L');
+ buf.put('L');
break;
default:
break;
}
if (t.isImaginary())
- buf.writeByte('i');
+ buf.put('i');
}
}
override void visit(TemplateTypeParameter tp)
{
- buf.writestring(tp.ident.toString());
+ buf.put(tp.ident.toString());
if (tp.specType)
{
- buf.writestring(" : ");
+ buf.put(" : ");
typeToBuffer(tp.specType, null, *buf, *hgs);
}
if (tp.defaultType)
{
- buf.writestring(" = ");
+ buf.put(" = ");
typeToBuffer(tp.defaultType, null, *buf, *hgs);
}
}
override void visit(TemplateThisParameter tp)
{
- buf.writestring("this ");
+ buf.put("this ");
visit(cast(TemplateTypeParameter)tp);
}
override void visit(TemplateAliasParameter tp)
{
- buf.writestring("alias ");
+ buf.put("alias ");
if (tp.specType)
typeToBuffer(tp.specType, tp.ident, *buf, *hgs);
else
- buf.writestring(tp.ident.toString());
+ buf.put(tp.ident.toString());
if (tp.specAlias)
{
- buf.writestring(" : ");
+ buf.put(" : ");
objectToBuffer(tp.specAlias, *buf, *hgs);
}
if (tp.defaultAlias)
{
- buf.writestring(" = ");
+ buf.put(" = ");
objectToBuffer(tp.defaultAlias, *buf, *hgs);
}
}
typeToBuffer(tp.valType, tp.ident, *buf, *hgs);
if (tp.specValue)
{
- buf.writestring(" : ");
+ buf.put(" : ");
tp.specValue.expressionToBuffer(*buf, *hgs);
}
if (tp.defaultValue)
{
- buf.writestring(" = ");
+ buf.put(" = ");
tp.defaultValue.expressionToBuffer(*buf, *hgs);
}
}
override void visit(TemplateTupleParameter tp)
{
- buf.writestring(tp.ident.toString());
- buf.writestring("...");
+ buf.put(tp.ident.toString());
+ buf.put("...");
}
}
override void visit(DebugCondition c)
{
- buf.writestring("debug (");
- buf.writestring(c.ident.toString());
- buf.writeByte(')');
+ buf.put("debug");
+ if (c.ident)
+ {
+ buf.put(" (");
+ buf.put(c.ident.toString());
+ buf.put(')');
+ }
}
override void visit(VersionCondition c)
{
- buf.writestring("version (");
- buf.writestring(c.ident.toString());
- buf.writeByte(')');
+ buf.put("version (");
+ buf.put(c.ident.toString());
+ buf.put(')');
}
override void visit(StaticIfCondition c)
{
- buf.writestring("static if (");
+ buf.put("static if (");
c.exp.expressionToBuffer(*buf, *hgs);
- buf.writeByte(')');
+ buf.put(')');
}
}
HdrGenState hgs;
hgs.fullQual = qualifyTypes;
- buf.writestring(ti.name.toChars());
+ buf.put(ti.name.toChars());
tiargsToBuffer(cast() ti, buf, hgs);
}
if (stc & STC.scopeinferred)
{
- //buf.writestring("scope-inferred ");
+ //buf.put("scope-inferred ");
stc &= ~(STC.scope_ | STC.scopeinferred);
}
if (stc & STC.returninferred)
{
- //buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
+ //buf.put((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
stc &= ~(STC.return_ | STC.returninferred);
}
// ensure `auto ref` keywords are (almost) adjacent
if (stc & STC.auto_)
{
- buf.writestring("auto ");
+ buf.put("auto ");
stc &= ~STC.auto_;
}
/* Put scope ref return into a standard order
case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1;
case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1;
L1:
- buf.writestring(rrs);
+ buf.put(rrs);
result = true;
stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
break;
if (!s.length)
break;
if (result)
- buf.writeByte(' ');
+ buf.put(' ');
result = true;
- buf.writestring(s);
+ buf.put(s);
}
return result;
const s = linkageToString(linkage);
if (s.length)
{
- buf.writestring("extern (");
- buf.writestring(s);
- buf.writeByte(')');
+ buf.put("extern (");
+ buf.put(s);
+ buf.put(')');
}
}
void visibilityToBuffer(ref OutBuffer buf, Visibility vis)
{
- buf.writestring(visibilityToString(vis.kind));
+ buf.put(visibilityToString(vis.kind));
if (vis.kind == Visibility.Kind.package_ && vis.pkg)
{
- buf.writeByte('(');
- buf.writestring(vis.pkg.toPrettyChars(true));
- buf.writeByte(')');
+ buf.put('(');
+ buf.put(vis.pkg.toPrettyChars(true));
+ buf.put(')');
}
}
foreach (i, arg; *arguments)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
typeToBuffer(arg.type, null, buf, hgs);
}
}
foreach (i, o; *objects)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
objectToBuffer(o, buf, hgs);
}
}
if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.length - 1])
{
- buf.writestring("...");
+ buf.put("...");
}
return buf.extractChars();
}
private void parametersToBuffer(ParameterList pl, ref OutBuffer buf, ref HdrGenState hgs)
{
- buf.writeByte('(');
+ buf.put('(');
foreach (i; 0 .. pl.length)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
pl[i].parameterToBuffer(buf, hgs);
}
final switch (pl.varargs)
case VarArg.variadic:
if (pl.length)
- buf.writestring(", ");
+ buf.put(", ");
if (stcToBuffer(buf, pl.stc))
- buf.writeByte(' ');
+ buf.put(' ');
goto case VarArg.typesafe;
case VarArg.typesafe:
- buf.writestring("...");
+ buf.put("...");
break;
}
- buf.writeByte(')');
+ buf.put(')');
}
{
if (p.userAttribDecl)
{
- buf.writeByte('@');
+ buf.put('@');
bool isAnonymous = p.userAttribDecl.atts.length > 0 && !(*p.userAttribDecl.atts)[0].isCallExp();
if (isAnonymous)
- buf.writeByte('(');
+ buf.put('(');
argsToBuffer(p.userAttribDecl.atts, buf, hgs);
if (isAnonymous)
- buf.writeByte(')');
- buf.writeByte(' ');
+ buf.put(')');
+ buf.put(' ');
}
if (p.storageClass & STC.auto_)
- buf.writestring("auto ");
+ buf.put("auto ");
STC stc = p.storageClass;
if (p.storageClass & STC.in_)
{
- buf.writestring("in ");
+ buf.put("in ");
if ((p.storageClass & (STC.constscoperef | STC.ref_)) == (STC.constscoperef | STC.ref_))
stc &= ~STC.ref_;
}
else if (p.storageClass & STC.lazy_)
- buf.writestring("lazy ");
+ buf.put("lazy ");
else if (p.storageClass & STC.alias_)
- buf.writestring("alias ");
+ buf.put("alias ");
if (p.type && p.type.mod & MODFlags.shared_)
stc &= ~STC.shared_;
if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ |
STC.return_ | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ | STC.ref_ | STC.returnScope)))
- buf.writeByte(' ');
+ buf.put(' ');
const(char)[] s;
if (p.storageClass & STC.alias_)
{
if (p.ident)
- buf.writestring(p.ident.toString());
+ buf.put(p.ident.toString());
}
else if (p.type.isTypeIdentifier() &&
(s = p.type.isTypeIdentifier().ident.toString()).length > 3 &&
s[0..3] == "__T")
{
// print parameter name, instead of undetermined type parameter
- buf.writestring(p.ident.toString());
+ buf.put(p.ident.toString());
}
else
{
if (p.defaultArg)
{
- buf.writestring(" = ");
+ buf.put(" = ");
p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
}
}
foreach (i, el; *expressions)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (names && i < names.length && (*names)[i].name)
{
- buf.writestring((*names)[i].name.toString());
- buf.writestring(": ");
+ buf.put((*names)[i].name.toString());
+ buf.put(": ");
}
if (!el)
el = basis;
// [0..length: basis, 1: e1, 5: e5]
if (basis)
{
- buf.writestring("0..");
+ buf.put("0..");
buf.print(expressions.length);
- buf.writestring(": ");
+ buf.put(": ");
expToBuffer(basis, PREC.assign, buf, hgs);
}
foreach (i, el; *expressions)
{
if (basis)
{
- buf.writestring(", ");
+ buf.put(", ");
buf.print(i);
- buf.writestring(": ");
+ buf.put(": ");
}
else if (i)
- buf.writestring(", ");
+ buf.put(", ");
expToBuffer(el, PREC.assign, buf, hgs);
}
}
}
if (uval <= 0x7FFF_FFFF_FFFF_FFFFUL)
{
- buf.writestring("cast(size_t)");
+ buf.put("cast(size_t)");
buf.print(uval);
return;
}
expressionPrettyPrint(e, buf, hgs);
}
+// to be called if e could be loweredFrom another expression instead of acessing precedence[e.op] directly
+private PREC expPrecedence(ref HdrGenState hgs, Expression e)
+{
+ if (!hgs.vcg_ast)
+ {
+ if (auto ce = e.isCallExp())
+ {
+ if (ce.loweredFrom)
+ e = ce.loweredFrom;
+ }
+ else if (auto ne = e.isNotExp())
+ if (ne.loweredFrom)
+ e = ne.loweredFrom;
+ }
+ return precedence[e.op];
+}
+
/**************************************************
* Write expression out to buf, but wrap it
* in ( ) if its precedence is less than pr.
*/
private void expToBuffer(Expression e, PREC pr, ref OutBuffer buf, ref HdrGenState hgs)
{
+ auto prec = expPrecedence(hgs, e);
debug
{
- if (precedence[e.op] == PREC.zero)
+ if (prec == PREC.zero)
printf("precedence not defined for token '%s'\n", EXPtoString(e.op).ptr);
}
if (e.op == 0xFF)
{
- buf.writestring("<FF>");
+ buf.put("<FF>");
return;
}
- assert(precedence[e.op] != PREC.zero);
+ assert(prec != PREC.zero);
assert(pr != PREC.zero);
/* Despite precedence, we don't allow a<b<c expressions.
* They must be parenthesized.
*/
- if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
- || (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
+ if (prec < pr || (pr == PREC.rel && prec == pr)
+ || (pr >= PREC.or && pr <= PREC.and && prec == PREC.rel))
{
- buf.writeByte('(');
+ buf.put('(');
e.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
}
else
{
visitWithMask(t, modMask, buf, hgs);
if (ident)
{
- buf.writeByte(' ');
- buf.writestring(ident.toString());
+ buf.put(' ');
+ buf.put(ident.toString());
}
}
if (m & MODFlags.shared_)
{
MODtoBuffer(buf, MODFlags.shared_);
- buf.writeByte('(');
+ buf.put('(');
}
if (m & MODFlags.wild)
{
MODtoBuffer(buf, MODFlags.wild);
- buf.writeByte('(');
+ buf.put('(');
}
if (m & (MODFlags.const_ | MODFlags.immutable_))
{
MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
- buf.writeByte('(');
+ buf.put('(');
}
typeToBufferx(t, buf, hgs);
if (m & (MODFlags.const_ | MODFlags.immutable_))
- buf.writeByte(')');
+ buf.put(')');
if (m & MODFlags.wild)
- buf.writeByte(')');
+ buf.put(')');
if (m & MODFlags.shared_)
- buf.writeByte(')');
+ buf.put(')');
}
}
private void dumpTemplateInstance(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs)
{
- buf.writeByte('{');
+ buf.put('{');
buf.writenl();
buf.level++;
}
buf.level--;
- buf.writeByte('}');
+ buf.put('}');
buf.writenl();
}
private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs)
{
- buf.writeByte('!');
+ buf.put('!');
if (ti.nest)
{
- buf.writestring("(...)");
+ buf.put("(...)");
return;
}
if (!ti.tiargs)
{
- buf.writestring("()");
+ buf.put("()");
return;
}
if (ti.tiargs.length == 1)
}
}
}
- buf.writeByte('(');
+ buf.put('(');
ti.nestUp();
foreach (i, arg; *ti.tiargs)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
objectToBuffer(arg, buf, hgs);
}
ti.nestDown();
- buf.writeByte(')');
+ buf.put(')');
}
/****************************************
else if (Dsymbol s = isDsymbol(oarg))
{
if (s.ident)
- buf.writestring(s.ident.toString());
+ buf.put(s.ident.toString());
else
- buf.writestring(s.toChars());
+ buf.put(s.toChars());
}
else if (auto v = isTuple(oarg))
{
foreach (i, arg; *args)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
objectToBuffer(arg, buf, hgs);
}
}
}
else if (!oarg)
{
- buf.writestring("NULL");
+ buf.put("NULL");
}
else
{
if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
{
linkageToBuffer(buf, t.linkage);
- buf.writeByte(' ');
+ buf.put(' ');
}
if (t.linkage == LINK.objc && isStatic)
buf.write("static ");
{
typeToBuffer(t.next, null, buf, hgs);
if (ident)
- buf.writeByte(' ');
+ buf.put(' ');
}
else if (hgs.ddoc)
- buf.writestring("auto ");
+ buf.put("auto ");
if (ident)
- buf.writestring(ident);
+ buf.put(ident);
parametersToBuffer(t.parameterList, buf, hgs);
/* Use postfix style for attributes
*/
if (t.mod)
{
- buf.writeByte(' ');
+ buf.put(' ');
MODtoBuffer(buf, t.mod);
}
void dg(string str)
{
- buf.writeByte(' ');
- buf.writestring(str);
+ buf.put(' ');
+ buf.put(str);
}
t.attributesApply(&dg);
if (t.mod && !(hgs.ddoc || hgs.hdrgen))
{
MODtoBuffer(buf, t.mod);
- buf.writeByte(' ');
+ buf.put(' ');
}
void dg(string str)
// don't write 'ref' for ctors
if ((ident == Id.ctor) && str == "ref")
return;
- buf.writestring(str);
- buf.writeByte(' ');
+ buf.put(str);
+ buf.put(' ');
}
}
t.attributesApply(&dg);
if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
{
linkageToBuffer(buf, t.linkage);
- buf.writeByte(' ');
+ buf.put(' ');
}
if (ident && ident.toHChars2() != ident.toChars())
{
{
typeToBuffer(t.next, null, buf, hgs);
if (ident)
- buf.writeByte(' ');
+ buf.put(' ');
}
else if (hgs.ddoc)
- buf.writestring("auto ");
+ buf.put("auto ");
if (ident)
- buf.writestring(ident.toHChars2());
+ buf.put(ident.toHChars2());
if (td)
{
- buf.writeByte('(');
+ buf.put('(');
foreach (i, p; *td.origParameters)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
toCBuffer(p, buf, hgs);
}
- buf.writeByte(')');
+ buf.put(')');
}
parametersToBuffer(t.parameterList, buf, hgs);
// postfix this attributes are more readable
if (t.mod && (hgs.ddoc || hgs.hdrgen))
{
- buf.writeByte(' ');
+ buf.put(' ');
MODtoBuffer(buf, t.mod);
}
if (t.isReturnScope && !t.isReturnInferred)
{
- buf.writestring(" return scope");
+ buf.put(" return scope");
}
else if (t.isScopeQual && !t.isScopeInferred)
{
- buf.writestring(" scope");
+ buf.put(" scope");
}
if (t.isReturn && !t.isReturnScope && !t.isReturnInferred)
{
- buf.writestring(" return");
+ buf.put(" return");
}
t.inuse--;
}
{
void visitError(ErrorInitializer iz)
{
- buf.writestring("__error__");
+ buf.put("__error__");
}
void visitVoid(VoidInitializer iz)
{
- buf.writestring("void");
+ buf.put("void");
}
void visitDefault(DefaultInitializer iz)
{
- buf.writestring("{ }");
+ buf.put("{ }");
}
void visitStruct(StructInitializer si)
{
//printf("StructInitializer::toCBuffer()\n");
- buf.writeByte('{');
+ buf.put('{');
foreach (i, const id; si.field)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (id)
{
- buf.writestring(id.toString());
- buf.writeByte(':');
+ buf.put(id.toString());
+ buf.put(':');
}
if (auto iz = si.value[i])
initializerToBuffer(iz, buf, hgs);
}
- buf.writeByte('}');
+ buf.put('}');
}
void visitArray(ArrayInitializer ai)
{
- buf.writeByte('[');
+ buf.put('[');
foreach (i, ex; ai.index)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (ex)
{
ex.expressionToBuffer(buf, hgs);
- buf.writeByte(':');
+ buf.put(':');
}
if (auto iz = ai.value[i])
initializerToBuffer(iz, buf, hgs);
}
- buf.writeByte(']');
+ buf.put(']');
}
void visitExp(ExpInitializer ei)
void visitC(CInitializer ci)
{
- buf.writeByte('{');
+ buf.put('{');
foreach (i, ref DesigInit di; ci.initializerList)
{
if (i)
- buf.writestring(", ");
+ buf.put(", ");
if (di.designatorList)
{
foreach (ref Designator d; (*di.designatorList)[])
{
if (d.exp)
{
- buf.writeByte('[');
+ buf.put('[');
toCBuffer(d.exp, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
else
{
- buf.writeByte('.');
- buf.writestring(d.ident.toString());
+ buf.put('.');
+ buf.put(d.ident.toString());
}
}
- buf.writeByte('=');
+ buf.put('=');
}
initializerToBuffer(di.initializer, buf, hgs);
}
- buf.writeByte('}');
+ buf.put('}');
}
mixin VisitInitializer!void visit;
void visitError(TypeError t)
{
- buf.writestring("_error_");
+ buf.put("_error_");
}
void visitBasic(TypeBasic t)
{
//printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
- buf.writestring(t.dstring);
+ buf.put(t.dstring);
}
void visitTraits(TypeTraits t)
void visitVector(TypeVector t)
{
//printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
- buf.writestring("__vector(");
+ buf.put("__vector(");
visitWithMask(t.basetype, t.mod, buf, hgs);
- buf.writestring(")");
+ buf.put(")");
}
void visitSArray(TypeSArray t)
{
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
sizeToBuffer(t.dim, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
void visitDArray(TypeDArray t)
if (hgs.declstring)
goto L1;
if (ut.equals(Type.tstring))
- buf.writestring("string");
+ buf.put("string");
else if (ut.equals(Type.twstring))
- buf.writestring("wstring");
+ buf.put("wstring");
else if (ut.equals(Type.tdstring))
- buf.writestring("dstring");
+ buf.put("dstring");
else
{
L1:
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writestring("[]");
+ buf.put("[]");
}
}
void visitAArray(TypeAArray t)
{
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
visitWithMask(t.index, 0, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
void visitPointer(TypePointer t)
else
{
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writeByte('*');
+ buf.put('*');
}
}
void visitReference(TypeReference t)
{
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writeByte('&');
+ buf.put('&');
}
void visitFunction(TypeFunction t)
switch (id.dyncast()) with (DYNCAST)
{
case dsymbol:
- buf.writeByte('.');
+ buf.put('.');
TemplateInstance ti = cast(TemplateInstance)id;
ti.dsymbolToBuffer(buf, hgs);
break;
case expression:
- buf.writeByte('[');
+ buf.put('[');
(cast(Expression)id).expressionToBuffer(buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
break;
case type:
- buf.writeByte('[');
+ buf.put('[');
typeToBufferx(cast(Type)id, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
break;
default:
- buf.writeByte('.');
- buf.writestring(id.toString());
+ buf.put('.');
+ buf.put(id.toString());
}
}
}
void visitIdentifier(TypeIdentifier t)
{
//printf("visitTypeIdentifier() %s\n", t.ident.toChars());
- buf.writestring(t.ident.toString());
+ buf.put(t.ident.toString());
visitTypeQualifiedHelper(t);
}
void visitTypeof(TypeTypeof t)
{
- buf.writestring("typeof(");
+ buf.put("typeof(");
t.exp.expressionToBuffer(buf, hgs);
- buf.writeByte(')');
+ buf.put(')');
visitTypeQualifiedHelper(t);
}
void visitReturn(TypeReturn t)
{
- buf.writestring("typeof(return)");
+ buf.put("typeof(return)");
visitTypeQualifiedHelper(t);
}
void visitEnum(TypeEnum t)
{
//printf("visitEnum: %s\n", t.sym.toChars());
- buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitStruct(TypeStruct t)
// while printing messages.
TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
if (ti && ti.aliasdecl == t.sym)
- buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+ buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
else
- buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitClass(TypeClass t)
// while printing messages.
TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
if (ti && ti.aliasdecl == t.sym)
- buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+ buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
else
- buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
}
void visitTag(TypeTag t)
// https://issues.dlang.org/show_bug.cgi?id=24670
// `const` must be parenthesized because it can be a return type
if (t.mod & MODFlags.const_)
- buf.writestring("const(");
+ buf.put("const(");
// For C to D translation, `struct S` or `enum S` simply becomes `S`
- buf.writestring(t.id.toString());
+ buf.put(t.id.toString());
if (t.mod & MODFlags.const_)
- buf.writestring(")");
+ buf.put(")");
return;
}
// The following produces something like "const enum E : short"
if (t.mod & MODFlags.const_)
- buf.writestring("const ");
- buf.writestring(Token.toString(t.tok));
- buf.writeByte(' ');
+ buf.put("const ");
+ buf.put(Token.toString(t.tok));
+ buf.put(' ');
if (t.id)
- buf.writestring(t.id.toString());
+ buf.put(t.id.toString());
if (t.tok == TOK.enum_ && t.base && t.base.ty != TY.Tint32)
{
- buf.writestring(" : ");
+ buf.put(" : ");
visitWithMask(t.base, t.mod, buf, hgs);
}
}
void visitSlice(TypeSlice t)
{
visitWithMask(t.next, t.mod, buf, hgs);
- buf.writeByte('[');
+ buf.put('[');
sizeToBuffer(t.lwr, buf, hgs);
- buf.writestring(" .. ");
+ buf.put(" .. ");
sizeToBuffer(t.upr, buf, hgs);
- buf.writeByte(']');
+ buf.put(']');
}
void visitNull(TypeNull t)
{
- buf.writestring("typeof(null)");
+ buf.put("typeof(null)");
}
void visitMixin(TypeMixin t)
{
- buf.writestring("mixin(");
+ buf.put("mixin(");
argsToBuffer(t.exps, buf, hgs, null);
- buf.writeByte(')');
+ buf.put(')');
}
void visitNoreturn(TypeNoreturn t)
{
- buf.writestring("noreturn");
+ buf.put("noreturn");
}
if (hgs.importcHdr && !hgs.inCAlias && t.mcache && t.mcache.typedefIdent)
{
- buf.writestring(t.mcache.typedefIdent.toString());
+ buf.put(t.mcache.typedefIdent.toString());
return;
}
--- /dev/null
+/**
+ * Inline assembler for the GCC D compiler.
+ *
+ * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved
+ * Authors: Iain Buclaw
+ * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d)
+ * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d
+ */
+
+module dmd.iasm.gcc;
+
+import core.stdc.string;
+
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.errorsink;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.identifier;
+import dmd.globals;
+import dmd.location;
+import dmd.cparse;
+import dmd.parse;
+import dmd.target;
+import dmd.tokens;
+import dmd.statement;
+import dmd.statementsem;
+
+/***********************************
+ * Parse and run semantic analysis on a GccAsmStatement.
+ * Params:
+ * s = gcc asm statement being parsed
+ * sc = the scope where the asm statement is located
+ * Returns:
+ * the completed gcc asm statement, or null if errors occurred
+ */
+public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
+{
+ //printf("GccAsmStatement.semantic()\n");
+ const bool doUnittests = global.params.parsingUnittestsRequired();
+ scope p = (sc && sc.inCfile)
+ ? new CParser!ASTCodegen(sc._module, "", false, global.errorSink, target.c, null, &global.compileEnv)
+ : new Parser!ASTCodegen(sc._module, "", false, global.errorSink, &global.compileEnv, doUnittests);
+
+ // Make a safe copy of the token list before parsing.
+ Token* toklist = null;
+ Token **ptoklist = &toklist;
+
+ for (Token* token = s.tokens; token; token = token.next)
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ *ptoklist = null;
+ }
+ // Append closing `;` location.
+ *ptoklist = p.allocateToken();
+ (*ptoklist).value = TOK.semicolon;
+ (*ptoklist).loc = s.loc;
+ ptoklist = &(*ptoklist).next;
+ *ptoklist = null;
+
+ // Adjust starting line number of the parser.
+ p.token = *toklist;
+ p.baseLoc.startLine = s.loc.linnum;
+ p.linnum = s.loc.linnum;
+
+ // Parse the gcc asm statement.
+ const errors = global.errors;
+ s = p.parseGccAsm(s);
+ if (errors != global.errors)
+ return null;
+ s.stc = sc.stc;
+
+ // Fold the instruction template string.
+ s.insn = semanticAsmString(sc, s.insn, "asm instruction template");
+
+ if (s.labels && s.outputargs)
+ p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints");
+
+ // Analyse all input and output operands.
+ if (s.args)
+ {
+ foreach (i; 0 .. s.args.length)
+ {
+ Expression ec = (*s.constraints)[i];
+ (*s.constraints)[i] = semanticAsmString(sc, ec, "asm operand");
+
+ Expression earg = (*s.args)[i];
+ earg = earg.expressionSemantic(sc);
+ // Check argument is a valid lvalue/rvalue.
+ if (i < s.outputargs)
+ earg = earg.modifiableLvalue(sc);
+ else if (earg.checkValue())
+ earg = ErrorExp.get();
+ (*s.args)[i] = earg;
+ }
+ }
+
+ // Analyse all clobbers.
+ if (s.clobbers)
+ {
+ foreach (i; 0 .. s.clobbers.length)
+ {
+ Expression ec = (*s.clobbers)[i];
+ (*s.clobbers)[i] = semanticAsmString(sc, ec, "asm clobber");
+ }
+ }
+
+ // Analyse all goto labels.
+ if (s.labels)
+ {
+ foreach (i; 0 .. s.labels.length)
+ {
+ Identifier ident = (*s.labels)[i];
+ GotoStatement gs = new GotoStatement(s.loc, ident);
+ if (!s.gotos)
+ s.gotos = new GotoStatements();
+ s.gotos.push(gs);
+ gs.statementSemantic(sc);
+ }
+ }
+
+ return s;
+}
+
+/***********************************
+ * Run semantic analysis on an CAsmDeclaration.
+ * Params:
+ * ad = asm declaration
+ * sc = the scope where the asm declaration is located
+ */
+public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc)
+{
+ import dmd.typesem : pointerTo;
+ ad.code = semanticString(sc, ad.code, "asm definition");
+ ad.code.type = ad.code.type.nextOf().pointerTo();
+
+ // Asm definition always needs emitting into the root module.
+ import dmd.dmodule : Module;
+ if (sc._module && sc._module.isRoot())
+ return;
+ if (Module m = Module.rootModule)
+ m.members.push(ad);
+}
+
+private:
+
+
+/***********************************
+ * Issue error if the current token is not `value`.
+ * Otherwise, advance to next token.
+ * Params:
+ * p = parser state
+ * value = token value to compare with
+ * Returns:
+ * true advanced to next token
+ * false error was issued
+ */
+bool requireToken(Parser)(Parser p, TOK value)
+{
+ if (p.token.value == value)
+ {
+ p.nextToken();
+ return true;
+ }
+
+ p.eSink.error(p.token.loc, "found `%s` when expecting `%s`",
+ p.token.toChars(), Token.toChars(value));
+ return false;
+}
+
+/***********************************
+ * Run semantic analysis on `exp`, resolving it as a compile-time string.
+ * Params:
+ * sc = scope
+ * exp = Expression which expected as a string
+ * s = What the string is expected for, used in error diagnostic
+ * Returns:
+ * StringExp or ErrorExp
+ */
+Expression semanticAsmString(Scope* sc, Expression exp, const char *s)
+{
+ import dmd.dcast : implicitCastTo;
+ import dmd.dsymbolsem : resolveAliasThis;
+ import dmd.mtype : isAggregate, Type;
+
+ exp = expressionSemantic(exp, sc);
+
+ // Resolve `alias this` if we were given a struct literal.
+ if (auto ad = isAggregate(exp.type))
+ {
+ if (ad.aliasthis && ad.type && !ad.type.isTypeError())
+ exp = resolveAliasThis(sc, exp);
+ }
+
+ // Evaluate the expression as a string now or error trying.
+ if (auto se = semanticString(sc, exp, s))
+ exp = implicitCastTo(se, sc, Type.tstring);
+
+ return exp;
+}
+
+/***********************************
+ * Parse a D or ImportC assignment expression
+ */
+Expression parseAssignment(Parser)(Parser p)
+{
+ if (p.Ccompile)
+ return (cast(CParser!ASTCodegen)p).cparseAssignExp();
+
+ return p.parseAssignExp();
+}
+
+/***********************************
+ * Parse a D or ImportC conditional expression
+ */
+Expression parseConditional(Parser)(Parser p)
+{
+ if (p.Ccompile)
+ return (cast(CParser!ASTCodegen)p).cparseCondExp();
+
+ return p.parseCondExp();
+}
+
+/***********************************
+ * Parse a D or ImportC primary expression
+ */
+Expression parsePrimary(Parser)(Parser p)
+{
+ if (p.Ccompile)
+ return (cast(CParser!ASTCodegen)p).cparsePrimaryExp();
+
+ return p.parsePrimaryExp();
+}
+
+/***********************************
+ * Parse an expression that evaluates to a string.
+ * Grammar:
+ * | AsmStringExpr:
+ * | StringLiteral
+ * | ( ConditionalExpression )
+ * Params:
+ * p = parser state
+ * Returns:
+ * the parsed string expression
+ */
+Expression parseAsmString(Parser)(Parser p)
+{
+ if (p.token.value == TOK.leftParenthesis)
+ {
+ // Skip over opening `(`
+ p.nextToken();
+ Expression insn = p.parseConditional();
+ if (insn.isErrorExp())
+ return insn;
+
+ // Look for closing `)`.
+ if (!p.requireToken(TOK.rightParenthesis))
+ return ErrorExp.get();
+
+ return insn;
+ }
+ else if (p.token.value != TOK.string_)
+ {
+ p.eSink.error(p.token.loc, "expected string literal or expression in parentheses");
+ return ErrorExp.get();
+ }
+
+ return p.parsePrimary();
+}
+
+/***********************************
+ * Parse list of extended asm input or output operands.
+ * Grammar:
+ * | Operands:
+ * | SymbolicName(opt) AsmStringExpr ( AssignExpression )
+ * | SymbolicName(opt) AsmStringExpr ( AssignExpression ), Operands
+ * |
+ * | SymbolicName:
+ * | [ Identifier ]
+ * Params:
+ * p = parser state
+ * s = asm statement to parse
+ * Returns:
+ * number of operands added to the gcc asm statement
+ */
+int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
+{
+ int numargs = 0;
+
+ if (p.token.value == TOK.colon ||
+ p.token.value == TOK.colonColon ||
+ p.token.value == TOK.semicolon ||
+ p.token.value == TOK.endOfFile)
+ return numargs;
+
+ while (1)
+ {
+ Expression arg;
+ Identifier name;
+
+ if (p.token.value == TOK.leftBracket)
+ {
+ // Skip over opening `[`
+ p.nextToken();
+ if (p.token.value == TOK.identifier)
+ {
+ // Store the symbolic name
+ name = p.token.ident;
+ p.nextToken();
+ }
+ else
+ {
+ p.eSink.error(p.token.loc, "identifier expected after `[`");
+ goto Lerror;
+ }
+ // Look for closing `]`
+ if (!p.requireToken(TOK.rightBracket))
+ goto Lerror;
+ }
+
+ // Look for the constraint string.
+ Expression constraint = p.parseAsmString();
+ if (constraint.isErrorExp())
+ goto Lerror;
+
+ // Look for the opening `(`
+ if (!p.requireToken(TOK.leftParenthesis))
+ goto Lerror;
+
+ // Parse the assign expression
+ arg = p.parseAssignment();
+ if (arg.isErrorExp())
+ goto Lerror;
+
+ // Look for the closing `)`
+ if (!p.requireToken(TOK.rightParenthesis))
+ goto Lerror;
+
+ // Add this operand to the list.
+ if (!s.args)
+ {
+ s.names = new Identifiers();
+ s.constraints = new Expressions();
+ s.args = new Expressions();
+ }
+ s.names.push(name);
+ s.args.push(arg);
+ s.constraints.push(constraint);
+ numargs++;
+
+ // If the next token is not a `,`, there are no more operands.
+ if (p.token.value != TOK.comma)
+ return numargs;
+
+ // Skip over the `,` token.
+ p.nextToken();
+ }
+Lerror:
+ while (p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return 0;
+}
+
+/***********************************
+ * Parse list of extended asm clobbers.
+ * Grammar:
+ * | Clobbers:
+ * | AsmStringExpr
+ * | AsmStringExpr , Clobbers
+ * Params:
+ * p = parser state
+ * Returns:
+ * array of parsed clobber expressions
+ */
+Expressions* parseExtAsmClobbers(Parser)(Parser p)
+{
+ Expressions* clobbers;
+
+ if (p.token.value == TOK.colon ||
+ p.token.value == TOK.colonColon ||
+ p.token.value == TOK.semicolon ||
+ p.token.value == TOK.endOfFile)
+ return clobbers;
+
+ while (1)
+ {
+ // Look for the clobbers string
+ Expression clobber = p.parseAsmString();
+ if (clobber.isErrorExp())
+ goto Lerror;
+
+ // Add it to the list.
+ if (!clobbers)
+ clobbers = new Expressions();
+ clobbers.push(clobber);
+
+ // If the next token is not a `,`, there are no more clobbers.
+ if (p.token.value != TOK.comma)
+ return clobbers;
+
+ // Skip over the `,` token.
+ p.nextToken();
+ }
+Lerror:
+ while (p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return null;
+}
+
+/***********************************
+ * Parse list of extended asm goto labels.
+ * Grammar:
+ * | GotoLabels:
+ * | Identifier
+ * | Identifier , GotoLabels
+ * Params:
+ * p = parser state
+ * Returns:
+ * array of parsed goto labels
+ */
+Identifiers* parseExtAsmGotoLabels(Parser)(Parser p)
+{
+ Identifiers* labels;
+
+ while (1)
+ {
+ if (p.token.value == TOK.identifier)
+ {
+ if (!labels)
+ labels = new Identifiers();
+ labels.push(p.token.ident);
+
+ // If the next token is not a `,`, there are no more labels.
+ if (p.nextToken() != TOK.comma)
+ return labels;
+
+ // Skip over the `,` token.
+ p.nextToken();
+ }
+ else
+ {
+ p.eSink.error(p.token.loc, "identifier expected for goto label name, not `%s`",
+ p.token.toChars());
+ goto Lerror;
+ }
+ }
+Lerror:
+ while (p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return null;
+}
+
+/***********************************
+ * Parse a gcc asm statement.
+ * There are three forms of inline asm statements, basic, extended, and goto.
+ * Grammar:
+ * | AsmInstruction:
+ * | BasicAsmInstruction
+ * | ExtAsmInstruction
+ * | GotoAsmInstruction
+ * |
+ * | BasicAsmInstruction:
+ * | AsmStringExpr
+ * |
+ * | ExtAsmInstruction:
+ * | AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt)
+ * |
+ * | GotoAsmInstruction:
+ * | AsmStringExpr : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
+ * Params:
+ * p = parser state
+ * s = asm statement to parse
+ * Returns:
+ * the parsed gcc asm statement
+ */
+GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
+{
+ s.insn = p.parseAsmString();
+ if (s.insn.isErrorExp())
+ return s;
+
+ // No semicolon followed after instruction template, treat as extended asm.
+ if (p.token.value == TOK.colon || p.token.value == TOK.colonColon)
+ {
+ bool inputs;
+ bool clobbers;
+ bool labels;
+
+ // Look for outputs.
+ if (p.token.value == TOK.colon)
+ {
+ // Skip over the `:` token.
+ p.nextToken();
+ // Parse the output operands.
+ s.outputargs = p.parseExtAsmOperands(s);
+ }
+ else if (p.token.value == TOK.colonColon)
+ inputs = true;
+
+ // Look for inputs.
+ if (inputs || p.token.value == TOK.colon)
+ {
+ // Skip over the `:` or `::` token.
+ p.nextToken();
+ // Parse the input operands.
+ p.parseExtAsmOperands(s);
+ }
+ else if (p.token.value == TOK.colonColon)
+ clobbers = true;
+
+ // Look for clobbers.
+ if (clobbers || p.token.value == TOK.colon)
+ {
+ // Skip over the `:` or `::` token.
+ p.nextToken();
+ // Parse the clobbers.
+ s.clobbers = p.parseExtAsmClobbers();
+ }
+ else if (p.token.value == TOK.colonColon)
+ labels = true;
+
+ // Look for labels.
+ if (labels || p.token.value == TOK.colon)
+ {
+ // Skip over the `:` or `::` token.
+ p.nextToken();
+ // Parse the labels.
+ s.labels = p.parseExtAsmGotoLabels();
+ }
+ }
+
+ if (p.token.value == TOK.endOfFile)
+ assert(global.errors);
+ else
+ p.requireToken(TOK.semicolon);
+
+ return s;
+}
+
+unittest
+{
+ import dmd.mtype : TypeBasic;
+
+ if (!global.errorSink)
+ global.errorSink = new ErrorSinkCompiler;
+
+ const errors = global.startGagging();
+ scope(exit) global.endGagging(errors);
+
+ // If this check fails, then Type._init() was called before reaching here,
+ // and the entire chunk of code that follows can be removed.
+ assert(ASTCodegen.Type.tint32 is null);
+ // Minimally initialize the cached types in ASTCodegen.Type, as they are
+ // dependencies for some fail asm tests to succeed.
+ ASTCodegen.Type.stringtable._init();
+ scope(exit)
+ {
+ ASTCodegen.Type.deinitialize();
+ ASTCodegen.Type.tint32 = null;
+ ASTCodegen.Type.tchar = null;
+ }
+ scope tint32 = new TypeBasic(ASTCodegen.Tint32);
+ ASTCodegen.Type.tint32 = tint32;
+ scope tchar = new TypeBasic(ASTCodegen.Tchar);
+ ASTCodegen.Type.tchar = tchar;
+
+ // Imitates asmSemantic if version = IN_GCC.
+ static int semanticAsm(Token* tokens, bool importC)
+ {
+ const errors = global.errors;
+ scope gas = new GccAsmStatement(Loc.initial, tokens);
+ const bool doUnittests = false;
+ scope p = importC
+ ? new CParser!ASTCodegen(null, ";", false, global.errorSink, target.c, null, &global.compileEnv)
+ : new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests);
+ p.token = *tokens;
+ p.parseGccAsm(gas);
+ return global.errors - errors;
+ }
+
+ // Imitates parseStatement for asm statements.
+ static void parseAsm(string input, bool expectError, bool importC = false)
+ {
+ // Generate tokens from input test.
+ const bool doUnittests = false;
+ scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests);
+ p.nextToken();
+
+ Token* toklist = null;
+ Token** ptoklist = &toklist;
+ p.check(TOK.asm_);
+ p.check(TOK.leftCurly);
+ while (1)
+ {
+ if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
+ break;
+ if (p.token.value == TOK.colonColon)
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+ }
+ else
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ }
+ *ptoklist = null;
+ p.nextToken();
+ }
+ p.check(TOK.rightCurly);
+
+ auto res = semanticAsm(toklist, importC);
+ // Checks for both unexpected passes and failures.
+ assert((res == 0) != expectError, input);
+ }
+
+ /// Assembly Tests, all should pass.
+ /// Note: Frontend is not initialized, use only strings and identifiers.
+ immutable string[] passAsmTests = [
+ // Basic asm statement
+ q{ asm { "nop";
+ } },
+
+ // Extended asm statement
+ q{ asm { "cpuid"
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
+ : "a" (input);
+ } },
+
+ // Assembly with symbolic names
+ q{ asm { "bts %[base], %[offset]"
+ : [base] "+rm" (*ptr)
+ : [offset] "Ir" (bitnum);
+ } },
+
+ // Assembly with clobbers
+ q{ asm { "cpuid"
+ : "=a" (a)
+ : "a" (input)
+ : "ebx", "ecx", "edx";
+ } },
+
+ // Goto asm statement
+ q{ asm { "jmp %l0"
+ :
+ :
+ :
+ : Ljmplabel;
+ } },
+
+ // Any CTFE-able string allowed as instruction template.
+ q{ asm { (generateAsm);
+ } },
+
+ // Likewise mixins, permissible so long as the result is a string.
+ q{ asm { (mixin(`"repne"`, `~ "scasb"`));
+ } },
+
+ // :: token tests
+ q{ asm { "" : : : "memory"; } },
+ q{ asm { "" :: : "memory"; } },
+ q{ asm { "" : :: "memory"; } },
+ q{ asm { "" ::: "memory"; } },
+ q{ asm { "" :::: label; } },
+
+ // https://github.com/dlang/dmd/issues/21299
+ q{ asm { (insn) : (output) (a) : (input) (1) : (clobber); } },
+ q{ asm { (['t','e','s','t']) : (['=','r']) (a) : (['r']) (1) : (['m','e','m','o','r','y']); } },
+
+ // https://github.com/dlang/dmd/issues/21679
+ q{ asm { "" : "=r" (s.x); } },
+ ];
+
+ immutable string[] failAsmTests = [
+ // Found 'h' when expecting ';'
+ q{ asm { ""h;
+ } },
+
+ // https://issues.dlang.org/show_bug.cgi?id=20592
+ q{ asm { "nop" : [name] string (expr); } },
+
+ // Expression expected, not ';'
+ q{ asm { ""[;
+ } },
+
+ // Expression expected, not ':'
+ q{ asm { ""
+ :
+ : "g" (a ? b : : c);
+ } },
+
+ // Found ',' when expecting ':'
+ q{ asm { "", "";
+ } },
+
+ // Identifier expected, not ';'
+ q{ asm { "" : ::: ; } },
+ q{ asm { "" :: :: ; } },
+ q{ asm { "" ::: : ; } },
+ q{ asm { "" :::: ; } },
+
+ // https://issues.dlang.org/show_bug.cgi?id=20593
+ q{ asm { "instruction" : : "operand" 123; } },
+
+ // https://github.com/dlang/dmd/issues/21298
+ q{ asm { 1; } },
+ q{ asm { int; } },
+ q{ asm { : "=r" (i); } },
+ q{ asm { (; } },
+ q{ asm { (""; } },
+ q{ asm { "" ,; } },
+ q{ asm { "" d; } },
+ q{ asm { "" : (; } },
+ q{ asm { "" : (""; } },
+ q{ asm { "" : ""; } },
+ q{ asm { "" : "" (; } },
+ q{ asm { "" : "" (a; } },
+ q{ asm { "" : "" (a) ,; } },
+ q{ asm { "" : "" (a) d; } },
+ q{ asm { "" : "" (a) : (; } },
+ q{ asm { "" : "" (a) : (""; } },
+ q{ asm { "" : "" (a) : ""; } },
+ q{ asm { "" : "" (a) : "" (; } },
+ q{ asm { "" : "" (a) : "" (b; } },
+ q{ asm { "" : "" (a) : "" (b) ,; } },
+ q{ asm { "" : "" (a) : "" (b) d; } },
+ q{ asm { "" : "" (a) : "" (b) : (; } },
+ q{ asm { "" : "" (a) : "" (b) : (""; } },
+ q{ asm { "" : "" (a) : "" (b) : "" ,; } },
+ q{ asm { "" : "" (a) : "" (b) : "" d; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : (; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c ,; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c d; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c :; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c : (; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c : (""; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c : "" (; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d; } },
+ q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d); } },
+
+ // https://github.com/dlang/dmd/issues/21679
+ q{ asm { "" : "=r" (s->x); } },
+ ];
+
+ immutable string[] passCAsmTests = [
+ // https://github.com/dlang/dmd/issues/21679
+ q{ asm { "" : "=r" (s->x); } },
+ q{ asm { "" : "=r" (s.x); } }
+ ];
+
+ foreach (test; passAsmTests)
+ parseAsm(test, false);
+
+ foreach (test; failAsmTests)
+ parseAsm(test, true);
+
+ foreach (test; passCAsmTests)
+ parseAsm(test, false, /*importC*/true);
+}
import dmd.expression;
import dmd.func;
import dmd.mtype;
+import dmd.target;
import dmd.tokens;
import dmd.statement;
import dmd.statementsem;
}
else version (IN_GCC)
{
- import dmd.iasmgcc;
+ version = Asm_GCC;
+}
+else version (IN_LLVM)
+{
+ version = Asm_GCC;
}
else
{
- import dmd.iasmdmd;
+ import dmd.iasm.dmdx86;
+ import dmd.iasm.dmdaarch64;
version = MARS;
}
+version (Asm_GCC)
+{
+ import dmd.iasm.gcc;
+}
+
/************************ AsmStatement ***************************************/
Statement asmSemantic(AsmStatement s, Scope* sc)
}
auto ias = new InlineAsmStatement(s.loc, s.tokens);
ias.caseSensitive = s.caseSensitive;
- return inlineAsmSemantic(ias, sc);
+ return (target.isAArch64)
+ ? inlineAsmAArch64Semantic(ias, sc)
+ : inlineAsmSemantic(ias, sc); // X86_64
}
- else version (IN_GCC)
+ else version (Asm_GCC)
{
auto eas = new GccAsmStatement(s.loc, s.tokens);
return gccAsmSemantic(eas, sc);
version (NoBackend)
{
}
- else version (IN_GCC)
+ else version (Asm_GCC)
{
return gccAsmSemantic(ad, sc);
}
+++ /dev/null
-/**
- * Inline assembler for the GCC D compiler.
- *
- * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved
- * Authors: Iain Buclaw
- * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d)
- * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html
- * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d
- */
-
-module dmd.iasmgcc;
-
-import core.stdc.string;
-
-import dmd.arraytypes;
-import dmd.astcodegen;
-import dmd.dscope;
-import dmd.dsymbol;
-import dmd.errors;
-import dmd.errorsink;
-import dmd.expression;
-import dmd.expressionsem;
-import dmd.identifier;
-import dmd.globals;
-import dmd.location;
-import dmd.parse;
-import dmd.tokens;
-import dmd.statement;
-import dmd.statementsem;
-
-/***********************************
- * Parse and run semantic analysis on a GccAsmStatement.
- * Params:
- * s = gcc asm statement being parsed
- * sc = the scope where the asm statement is located
- * Returns:
- * the completed gcc asm statement, or null if errors occurred
- */
-public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
-{
- //printf("GccAsmStatement.semantic()\n");
- const bool doUnittests = global.params.parsingUnittestsRequired();
- scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
-
- // Make a safe copy of the token list before parsing.
- Token* toklist = null;
- Token **ptoklist = &toklist;
-
- for (Token* token = s.tokens; token; token = token.next)
- {
- *ptoklist = p.allocateToken();
- memcpy(*ptoklist, token, Token.sizeof);
- ptoklist = &(*ptoklist).next;
- *ptoklist = null;
- }
- p.token = *toklist;
- p.baseLoc.startLine = s.loc.linnum;
- p.linnum = s.loc.linnum;
-
- // Parse the gcc asm statement.
- const errors = global.errors;
- s = p.parseGccAsm(s);
- if (errors != global.errors)
- return null;
- s.stc = sc.stc;
-
- // Fold the instruction template string.
- s.insn = semanticString(sc, s.insn, "asm instruction template");
-
- if (s.labels && s.outputargs)
- p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints");
-
- // Analyse all input and output operands.
- if (s.args)
- {
- foreach (i; 0 .. s.args.length)
- {
- Expression e = (*s.args)[i];
- e = e.expressionSemantic(sc);
- // Check argument is a valid lvalue/rvalue.
- if (i < s.outputargs)
- e = e.modifiableLvalue(sc);
- else if (e.checkValue())
- e = ErrorExp.get();
- (*s.args)[i] = e;
-
- e = (*s.constraints)[i];
- e = e.expressionSemantic(sc);
- assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
- (*s.constraints)[i] = e;
- }
- }
-
- // Analyse all clobbers.
- if (s.clobbers)
- {
- foreach (i; 0 .. s.clobbers.length)
- {
- Expression e = (*s.clobbers)[i];
- e = e.expressionSemantic(sc);
- assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
- (*s.clobbers)[i] = e;
- }
- }
-
- // Analyse all goto labels.
- if (s.labels)
- {
- foreach (i; 0 .. s.labels.length)
- {
- Identifier ident = (*s.labels)[i];
- GotoStatement gs = new GotoStatement(s.loc, ident);
- if (!s.gotos)
- s.gotos = new GotoStatements();
- s.gotos.push(gs);
- gs.statementSemantic(sc);
- }
- }
-
- return s;
-}
-
-/***********************************
- * Run semantic analysis on an CAsmDeclaration.
- * Params:
- * ad = asm declaration
- * sc = the scope where the asm declaration is located
- */
-public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc)
-{
- import dmd.typesem : pointerTo;
- ad.code = semanticString(sc, ad.code, "asm definition");
- ad.code.type = ad.code.type.nextOf().pointerTo();
-
- // Asm definition always needs emitting into the root module.
- import dmd.dmodule : Module;
- if (sc._module && sc._module.isRoot())
- return;
- if (Module m = Module.rootModule)
- m.members.push(ad);
-}
-
-private:
-
-/***********************************
- * Parse an expression that evaluates to a string.
- * Grammar:
- * | AsmStringExpr:
- * | StringLiteral
- * | ( AssignExpression )
- * Params:
- * p = parser state
- * Returns:
- * the parsed string expression
- */
-Expression parseAsmString(Parser)(Parser p)
-{
- if (p.token.value == TOK.leftParenthesis)
- {
- p.nextToken();
- Expression insn = p.parseAssignExp();
- p.check(TOK.rightParenthesis);
- return insn;
- }
- else if (p.token.value != TOK.string_)
- {
- p.eSink.error(p.token.loc, "expected string literal or expression in parentheses");
- return ErrorExp.get();
- }
-
- return p.parsePrimaryExp();
-}
-
-/***********************************
- * Parse list of extended asm input or output operands.
- * Grammar:
- * | Operands:
- * | SymbolicName(opt) StringLiteral ( AssignExpression )
- * | SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
- * |
- * | SymbolicName:
- * | [ Identifier ]
- * Params:
- * p = parser state
- * s = asm statement to parse
- * Returns:
- * number of operands added to the gcc asm statement
- */
-int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
-{
- int numargs = 0;
-
- while (1)
- {
- Expression arg;
- Identifier name;
- Expression constraint;
-
- switch (p.token.value)
- {
- case TOK.semicolon:
- case TOK.colon:
- case TOK.endOfFile:
- return numargs;
-
- case TOK.leftBracket:
- if (p.peekNext() == TOK.identifier)
- {
- // Skip over opening `[`
- p.nextToken();
- // Store the symbolic name
- name = p.token.ident;
- p.nextToken();
- }
- else
- {
- p.eSink.error(s.loc, "expected identifier after `[`");
- goto Lerror;
- }
- // Look for closing `]`
- p.check(TOK.rightBracket);
- // Look for the string literal and fall through
- if (p.token.value == TOK.string_)
- goto case;
- else
- goto default;
-
- case TOK.string_:
- constraint = p.parsePrimaryExp();
- if (p.token.value != TOK.leftParenthesis)
- {
- arg = p.parseAssignExp();
- p.eSink.error(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars());
- }
- else
- {
- // Look for the opening `(`
- p.check(TOK.leftParenthesis);
- // Parse the assign expression
- arg = p.parseAssignExp();
- // Look for the closing `)`
- p.check(TOK.rightParenthesis);
- }
-
- if (!s.args)
- {
- s.names = new Identifiers();
- s.constraints = new Expressions();
- s.args = new Expressions();
- }
- s.names.push(name);
- s.args.push(arg);
- s.constraints.push(constraint);
- numargs++;
-
- if (p.token.value == TOK.comma)
- p.nextToken();
- break;
-
- default:
- p.eSink.error(p.token.loc, "expected constant string constraint for operand, not `%s`",
- p.token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p.token.value != TOK.rightCurly &&
- p.token.value != TOK.semicolon &&
- p.token.value != TOK.endOfFile)
- p.nextToken();
-
- return numargs;
-}
-
-/***********************************
- * Parse list of extended asm clobbers.
- * Grammar:
- * | Clobbers:
- * | StringLiteral
- * | StringLiteral , Clobbers
- * Params:
- * p = parser state
- * Returns:
- * array of parsed clobber expressions
- */
-Expressions* parseExtAsmClobbers(Parser)(Parser p)
-{
- Expressions* clobbers;
-
- while (1)
- {
- Expression clobber;
-
- switch (p.token.value)
- {
- case TOK.semicolon:
- case TOK.colon:
- case TOK.endOfFile:
- return clobbers;
-
- case TOK.string_:
- clobber = p.parsePrimaryExp();
- if (!clobbers)
- clobbers = new Expressions();
- clobbers.push(clobber);
-
- if (p.token.value == TOK.comma)
- p.nextToken();
- break;
-
- default:
- p.eSink.error(p.token.loc, "expected constant string constraint for clobber name, not `%s`",
- p.token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p.token.value != TOK.rightCurly &&
- p.token.value != TOK.semicolon &&
- p.token.value != TOK.endOfFile)
- p.nextToken();
-
- return clobbers;
-}
-
-/***********************************
- * Parse list of extended asm goto labels.
- * Grammar:
- * | GotoLabels:
- * | Identifier
- * | Identifier , GotoLabels
- * Params:
- * p = parser state
- * Returns:
- * array of parsed goto labels
- */
-Identifiers* parseExtAsmGotoLabels(Parser)(Parser p)
-{
- Identifiers* labels;
-
- while (1)
- {
- switch (p.token.value)
- {
- case TOK.semicolon:
- case TOK.endOfFile:
- return labels;
-
- case TOK.identifier:
- if (!labels)
- labels = new Identifiers();
- labels.push(p.token.ident);
-
- if (p.nextToken() == TOK.comma)
- p.nextToken();
- break;
-
- default:
- p.eSink.error(p.token.loc, "expected identifier for goto label name, not `%s`",
- p.token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p.token.value != TOK.rightCurly &&
- p.token.value != TOK.semicolon &&
- p.token.value != TOK.endOfFile)
- p.nextToken();
-
- return labels;
-}
-
-/***********************************
- * Parse a gcc asm statement.
- * There are three forms of inline asm statements, basic, extended, and goto.
- * Grammar:
- * | AsmInstruction:
- * | BasicAsmInstruction
- * | ExtAsmInstruction
- * | GotoAsmInstruction
- * |
- * | BasicAsmInstruction:
- * | AsmStringExpr
- * |
- * | ExtAsmInstruction:
- * | AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt)
- * |
- * | GotoAsmInstruction:
- * | AsmStringExpr : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
- * Params:
- * p = parser state
- * s = asm statement to parse
- * Returns:
- * the parsed gcc asm statement
- */
-GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
-{
- s.insn = p.parseAsmString();
- if (s.insn.isErrorExp())
- return s;
-
- if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
- goto Ldone;
-
- // No semicolon followed after instruction template, treat as extended asm.
- foreach (section; 0 .. 4)
- {
- p.check(TOK.colon);
-
- final switch (section)
- {
- case 0:
- s.outputargs = p.parseExtAsmOperands(s);
- break;
-
- case 1:
- p.parseExtAsmOperands(s);
- break;
-
- case 2:
- s.clobbers = p.parseExtAsmClobbers();
- break;
-
- case 3:
- s.labels = p.parseExtAsmGotoLabels();
- break;
- }
-
- if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
- goto Ldone;
- }
-Ldone:
- p.check(TOK.semicolon);
-
- return s;
-}
-
-unittest
-{
- import dmd.mtype : TypeBasic;
-
- if (!global.errorSink)
- global.errorSink = new ErrorSinkCompiler;
-
- const errors = global.startGagging();
- scope(exit) global.endGagging(errors);
-
- // If this check fails, then Type._init() was called before reaching here,
- // and the entire chunk of code that follows can be removed.
- assert(ASTCodegen.Type.tint32 is null);
- // Minimally initialize the cached types in ASTCodegen.Type, as they are
- // dependencies for some fail asm tests to succeed.
- ASTCodegen.Type.stringtable._init();
- scope(exit)
- {
- ASTCodegen.Type.deinitialize();
- ASTCodegen.Type.tint32 = null;
- }
- scope tint32 = new TypeBasic(ASTCodegen.Tint32);
- ASTCodegen.Type.tint32 = tint32;
-
- // Imitates asmSemantic if version = IN_GCC.
- static int semanticAsm(Token* tokens)
- {
- const errors = global.errors;
- scope gas = new GccAsmStatement(Loc.initial, tokens);
- const bool doUnittests = false;
- scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests);
- p.token = *tokens;
- p.parseGccAsm(gas);
- return global.errors - errors;
- }
-
- // Imitates parseStatement for asm statements.
- static void parseAsm(string input, bool expectError)
- {
- // Generate tokens from input test.
- const bool doUnittests = false;
- scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests);
- p.nextToken();
-
- Token* toklist = null;
- Token** ptoklist = &toklist;
- p.check(TOK.asm_);
- p.check(TOK.leftCurly);
- while (1)
- {
- if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
- break;
- if (p.token.value == TOK.colonColon)
- {
- *ptoklist = p.allocateToken();
- memcpy(*ptoklist, &p.token, Token.sizeof);
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
-
- *ptoklist = p.allocateToken();
- memcpy(*ptoklist, &p.token, Token.sizeof);
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
- }
- else
- {
- *ptoklist = p.allocateToken();
- memcpy(*ptoklist, &p.token, Token.sizeof);
- ptoklist = &(*ptoklist).next;
- }
- *ptoklist = null;
- p.nextToken();
- }
- p.check(TOK.rightCurly);
-
- auto res = semanticAsm(toklist);
- // Checks for both unexpected passes and failures.
- assert((res == 0) != expectError);
- }
-
- /// Assembly Tests, all should pass.
- /// Note: Frontend is not initialized, use only strings and identifiers.
- immutable string[] passAsmTests = [
- // Basic asm statement
- q{ asm { "nop";
- } },
-
- // Extended asm statement
- q{ asm { "cpuid"
- : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
- : "a" (input);
- } },
-
- // Assembly with symbolic names
- q{ asm { "bts %[base], %[offset]"
- : [base] "+rm" (*ptr),
- : [offset] "Ir" (bitnum);
- } },
-
- // Assembly with clobbers
- q{ asm { "cpuid"
- : "=a" (a)
- : "a" (input)
- : "ebx", "ecx", "edx";
- } },
-
- // Goto asm statement
- q{ asm { "jmp %l0"
- :
- :
- :
- : Ljmplabel;
- } },
-
- // Any CTFE-able string allowed as instruction template.
- q{ asm { (generateAsm);
- } },
-
- // Likewise mixins, permissible so long as the result is a string.
- q{ asm { (mixin(`"repne"`, `~ "scasb"`));
- } },
-
- // :: token tests
- q{ asm { "" : : : "memory"; } },
- q{ asm { "" :: : "memory"; } },
- q{ asm { "" : :: "memory"; } },
- q{ asm { "" ::: "memory"; } },
- ];
-
- immutable string[] failAsmTests = [
- // Found 'h' when expecting ';'
- q{ asm { ""h;
- } },
-
- // https://issues.dlang.org/show_bug.cgi?id=20592
- q{ asm { "nop" : [name] string (expr); } },
-
- // Expression expected, not ';'
- q{ asm { ""[;
- } },
-
- // Expression expected, not ':'
- q{ asm { ""
- :
- : "g" (a ? b : : c);
- } },
-
- // Found ',' when expecting ':'
- q{ asm { "", "";
- } },
-
- // https://issues.dlang.org/show_bug.cgi?id=20593
- q{ asm { "instruction" : : "operand" 123; } },
-
- // https://github.com/dlang/dmd/issues/21298
- q{ asm { 1; } },
- q{ asm { int; } },
- q{ asm { : "=r" (i); } },
- ];
-
- foreach (test; passAsmTests)
- parseAsm(test, false);
-
- foreach (test; failAsmTests)
- parseAsm(test, true);
-}
{ "keys" },
{ "values" },
{ "rehash" },
+ { "dup" },
{ "future", "__future" },
{ "property" },
{ "FpopBack", "popBack" },
// For internal functions
- { "aaLen", "_aaLen" },
- { "aaKeys", "_aaKeys" },
- { "aaValues", "_aaValues" },
- { "aaRehash", "_aaRehash" },
- { "_aaAsStruct" },
+ { "_d_aaGetY" },
+ { "_d_aaGetRvalueX" },
+ { "_d_aaDel" },
+ { "_d_aaEqual" },
+ { "_d_aaIn" },
+ { "_d_aaNew" },
+ { "_d_aaLen" },
+ { "_d_aaApply" },
+ { "_d_aaApply2" },
{ "monitorenter", "_d_monitorenter" },
{ "monitorexit", "_d_monitorexit" },
{ "criticalenter", "_d_criticalenter2" },
{ "_d_newarrayTTrace" },
{ "_d_newarraymTX" },
{ "_d_newarraymTXTrace" },
+ { "_d_arrayliteralTX" },
+ { "_d_arrayliteralTXTrace" },
{ "_d_assert_fail" },
- { "dup" },
- { "_aaApply" },
- { "_aaApply2" },
{ "_d_arrayctor" },
{ "_d_arraysetctor" },
{ "_d_arraysetassign" },
{ "_d_arrayassign_l" },
{ "_d_arrayassign_r" },
+ { "_d_cast" },
{ "imported" },
{ "InterpolationHeader" },
{ "_d_arrayappendcTXTrace" },
{ "_d_arraycatnTX" },
{ "_d_arraycatnTXTrace" },
+ { "_d_assocarrayliteralTX" },
// varargs implementation
{ "stdc" },
* Identifier (inside Identifier.idPool) with deterministic name based
* on the source location.
*/
- extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, string parent = "")
+ extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, const void* parent = null)
{
// generate `<prefix>_L<line>_C<col>`
auto sl = SourceLoc(loc);
* directly, but that would unnecessary lengthen symbols names. See issue:
* https://issues.dlang.org/show_bug.cgi?id=23722
*/
- static struct Key { string locKey; string prefix; string parent; }
+ static struct Key { string locKey; string prefix; const(void)* parent; }
__gshared uint[Key] counters;
- string locKey = cast(string) (sl.filename ~ idBuf[]);
+ const locKey = cast(string) (sl.filename ~ idBuf[]);
+ const key = Key(locKey, prefix, parent);
static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
{
// 2.082+
- counters.update(Key(locKey, prefix, parent),
+ counters.update(key,
() => 1u, // insertion
(ref uint counter) // update
{
}
else
{
- const key = Key(locKey, prefix, parent);
if (auto pCounter = key in counters)
{
idBuf.writestring("_");
const char *kind() const override;
Visibility visible() override;
Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees
- Dsymbol *toAlias() override;
bool overloadInsert(Dsymbol *s) override;
void accept(Visitor *v) override { v->visit(this); }
auto t = e.type.toBasetype();
if (auto ta = t.isTypeDArray())
{
- if (!checkAddressable(e, sc))
+ if (!checkAddressable(e, sc, "take address of"))
return ErrorExp.get();
e = e.castTo(sc, ta.next.pointerTo());
}
else if (auto ts = t.isTypeSArray())
{
- if (!checkAddressable(e, sc))
+ if (!checkAddressable(e, sc, "take address of"))
return ErrorExp.get();
e = e.castTo(sc, ts.next.pointerTo());
}
auto vd = s.isVarDeclaration(); // new declaration
auto vd2 = s2.isVarDeclaration(); // existing declaration
- if (vd && vd.isCmacro())
+ if (vd && vd.isCmacro)
return s2;
- assert(!(vd2 && vd2.isCmacro()));
+ assert(!(vd2 && vd2.isCmacro));
if (vd && vd2)
{
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.expression;
+import dmd.expressionsem : getConstInitializer;
import dmd.func;
import dmd.hdrgen;
import dmd.mangle;
}
break;
}
- stringbuffer.writeByte(c);
+ stringbuffer.writeByte(cast(char)c);
}
}
if (n & 1)
{
error("odd number (%d) of hex characters in hex string", n);
- stringbuffer.writeByte(v);
+ stringbuffer.writeByte(cast(char)v);
}
t.setString(stringbuffer);
stringPostfix(t);
if (n & 1)
{
v = (v << 4) | c;
- stringbuffer.writeByte(v);
+ stringbuffer.writeByte(cast(char)v);
}
else
v = c;
}
break;
}
- stringbuffer.writeByte(c);
+ stringbuffer.writeByte(cast(char)c);
}
}
import dmd.attrib;
import dmd.declaration;
import dmd.dsymbol;
-import dmd.dsymbolsem : isGNUABITag;
+import dmd.dsymbolsem : isGNUABITag, toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
/*************************************************************
+ * Mangle type of function. writing it to `buf`
+ * Params:
+ * t = function type
+ * ta = consult original function type for attributes
+ * modMask = type modifiers
+ * tret = function return type
+ * buf = sink for mangling characters
+ * backref = back reference
*/
void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, ref OutBuffer buf, ref Backref backref)
{
foreach (idx, param; t.parameterList)
mangleParameter(param, buf, backref);
//if (buf.data[buf.length - 1] == '@') assert(0);
- buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list
+ buf.writeByte(cast(ubyte)('Z' - t.parameterList.varargs)); // mark end of arg list
if (tret !is null)
mangleType(tret, 0, buf, backref);
t.inuse--;
while (mul >= base)
{
auto dig = cast(ubyte)(pos / mul);
- buf.writeByte('A' + dig);
+ buf.writeByte(cast(char)('A' + dig));
pos -= dig * mul;
mul /= base;
}
- buf.writeByte('a' + cast(ubyte)pos);
+ buf.writeByte(cast(char)('a' + pos));
}
#pragma once
+#include <stdint.h>
+
#include "dsymbol.h"
struct ModuleDeclaration;
PKGpackage // already determined that's an actual package
};
-enum class Edition : unsigned char
+enum class Edition : uint16_t
{
- none = 0u,
- legacy = 1u,
- v2024 = 2u,
- latest = 2u,
+ v2023 = 2023,
+ v2024,
+ v2025,
};
class Package : public ScopeDsymbol
SearchOptFlags searchCacheFlags; // cached flags
d_bool insearch;
+ d_bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd?
+
// module from command line we're imported from,
// i.e. a module that will be taken all the
// way to an object file
size_t namelen; // length of module name in characters
static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
- static const char *find(const char *filename);
static Module *load(Loc loc, Identifiers *packages, Identifier *ident);
const char *kind() const override;
bool isCoreModule(Identifier *ident);
// Back end
-
- int doppelganger; // sub-module
Symbol *cov; // private uint[] __coverage;
DArray<unsigned> covb; // bit array of valid code line numbers
- Symbol *sictor; // module order independent constructor
- Symbol *sctor; // module constructor
- Symbol *sdtor; // module destructor
- Symbol *ssharedctor; // module shared constructor
- Symbol *sshareddtor; // module shared destructor
- Symbol *stest; // module unit test
-
Symbol *sfilename; // symbol for filename
+ bool hasCDtor;
void *ctfe_cov; // stores coverage information from ctfe
import dmd.enumsem;
import dmd.errors;
import dmd.expression;
-import dmd.dsymbolsem : determineSize;
-import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.visitor;
enum LOGDOTEXP = 0; // log ::dotExp()
-enum LOGDEFAULTINIT = 0; // log ::defaultInit()
-enum SIZE_INVALID = (~cast(uinteger_t)0); // error return from size() functions
+enum SIZE_INVALID = (~cast(ulong)0); // error return from size() functions
static if (__VERSION__ < 2095)
{
return false;
}
- /**************************
- * When T is mutable,
- * Given:
- * T a, b;
- * Can we bitwise assign:
- * a = b;
- * ?
- */
- bool isAssignable()
- {
- return true;
- }
-
/**************************
* Returns true if T can be converted to boolean value.
*/
return s;
}
- /***************************************
- * Use when we prefer the default initializer to be a literal,
- * rather than a global immutable variable.
- */
- Expression defaultInitLiteral(Loc loc)
- {
- static if (LOGDEFAULTINIT)
- {
- printf("Type::defaultInitLiteral() '%s'\n", toChars());
- }
- return defaultInit(this, loc);
- }
-
/***************************************
* Return !=0 if the type or any of its subtypes is wild.
*/
* Return the mask that an integral type will
* fit into.
*/
- extern (D) final uinteger_t sizemask()
+ extern (D) final ulong sizemask()
{
- uinteger_t m;
+ ulong m;
switch (toBasetype().ty)
{
case Tbool:
return this;
}
- override Expression defaultInitLiteral(Loc loc)
- {
- return ErrorExp.get();
- }
-
override void accept(Visitor v)
{
v.visit(this);
return false;
}
- override Expression defaultInitLiteral(Loc loc)
- {
- //printf("TypeVector::defaultInitLiteral()\n");
- assert(basetype.ty == Tsarray);
- Expression e = basetype.defaultInitLiteral(loc);
- auto ve = new VectorExp(loc, e, this);
- ve.type = this;
- ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc));
- return ve;
- }
-
TypeBasic elementType()
{
assert(basetype.ty == Tsarray);
return next.alignment();
}
- override Expression defaultInitLiteral(Loc loc)
- {
- static if (LOGDEFAULTINIT)
- {
- printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
- }
- size_t d = cast(size_t)dim.toInteger();
- Expression elementinit;
- if (next.ty == Tvoid)
- elementinit = tuns8.defaultInitLiteral(loc);
- else
- elementinit = next.defaultInitLiteral(loc);
- auto elements = new Expressions(d);
- foreach (ref e; *elements)
- e = null;
- auto ae = new ArrayLiteralExp(loc, this, elementinit, elements);
- return ae;
- }
-
override bool hasUnsafeBitpatterns()
{
return next.hasUnsafeBitpatterns();
return linkage == LINK.d && parameterList.varargs == VarArg.variadic;
}
- /*********************************
- * Append error message to buf.
- * Input:
- * buf = message sink
- * format = printf format
- */
- extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...)
- {
- if (global.gag && !global.params.v.showGaggedErrors)
- return;
- va_list ap;
- va_start(ap, format);
- buf.vprintf(format, ap);
- va_end(ap);
- }
-
- /********************************
- * Convert an `argumentList`, which may contain named arguments, into
- * a list of arguments in the order of the parameter list.
- *
- * Params:
- * argumentList = array of function arguments
- * buf = if not null, append error message to it
- * Returns: re-ordered argument list, or `null` on error
- */
- extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf)
- {
- Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null;
- ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null;
- const nParams = parameterList.length(); // cached because O(n)
- auto newArgs = new Expressions(nParams);
- newArgs.zero();
- size_t ci = 0;
- bool hasNamedArgs = false;
- const bool isVariadic = parameterList.varargs != VarArg.none;
- foreach (i, arg; args)
- {
- if (!arg)
- {
- ci++;
- continue;
- }
- auto name = i < names.length ? names[i].name : null;
- if (name)
- {
- hasNamedArgs = true;
- const pi = findParameterIndex(name);
- if (pi == -1)
- {
- if (buf)
- getMatchError(*buf, "no parameter named `%s`", name.toChars());
- return null;
- }
- ci = pi;
- }
- if (ci >= newArgs.length)
- {
- if (!isVariadic)
- {
- // Without named args, let the caller diagnose argument overflow
- if (hasNamedArgs && buf)
- getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars());
- return null;
- }
- while (ci >= newArgs.length)
- newArgs.push(null);
- }
-
- if ((*newArgs)[ci])
- {
- if (buf)
- getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars());
- return null;
- }
- (*newArgs)[ci++] = arg;
- }
- foreach (i, arg; (*newArgs)[])
- {
- if (arg || parameterList[i].defaultArg)
- continue;
-
- if (isVariadic && i + 1 == newArgs.length)
- continue;
-
- // dtemplate sets `defaultArg=null` to avoid semantic on default arguments,
- // don't complain about missing arguments in that case
- if (this.incomplete)
- continue;
-
- if (buf)
- getMatchError(*buf, "missing argument for parameter #%d: `%s`",
- i + 1, parameterToChars(parameterList[i], this, false));
- return null;
- }
- // strip trailing nulls from default arguments
- size_t e = newArgs.length;
- while (e > 0 && (*newArgs)[e - 1] is null)
- {
- --e;
- }
- newArgs.setDim(e);
- return newArgs;
- }
-
/// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise.
bool iswild() const pure nothrow @safe @nogc
{
{
v.visit(this);
}
-
- /**
- * Look for the index of parameter `ident` in the parameter list
- *
- * Params:
- * ident = identifier of parameter to search for
- * Returns: index of parameter with name `ident` or -1 if not found
- */
- private extern(D) ptrdiff_t findParameterIndex(Identifier ident)
- {
- foreach (i, p; this.parameterList)
- {
- if (p.ident == ident)
- return i;
- }
- return -1;
- }
}
/***********************************************************
return sym.alignment;
}
- /***************************************
- * Use when we prefer the default initializer to be a literal,
- * rather than a global immutable variable.
- */
- override Expression defaultInitLiteral(Loc loc)
- {
- static if (LOGDEFAULTINIT)
- {
- printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
- }
- sym.size(loc);
- if (sym.sizeok != Sizeok.done)
- return ErrorExp.get();
-
- auto structelems = new Expressions(sym.nonHiddenFields());
- uint offset = 0;
- foreach (j; 0 .. structelems.length)
- {
- VarDeclaration vd = sym.fields[j];
- Expression e;
- if (vd.inuse)
- {
- error(loc, "circular reference to `%s`", vd.toPrettyChars());
- return ErrorExp.get();
- }
- if (vd.offset < offset || vd.type.size() == 0)
- e = null;
- else if (vd._init)
- {
- if (vd._init.isVoidInitializer())
- e = null;
- else
- e = vd.getConstInitializer(false);
- }
- else
- e = vd.type.defaultInitLiteral(loc);
- if (e && e.op == EXP.error)
- return e;
- if (e)
- offset = vd.offset + cast(uint)vd.type.size();
- (*structelems)[j] = e;
- }
- auto structinit = new StructLiteralExp(loc, sym, structelems);
-
- /* Copy from the initializer symbol for larger symbols,
- * otherwise the literals expressed as code get excessively large.
- */
- if (size(this, loc) > target.ptrsize * 4 && !needsNested())
- structinit.useStaticInit = true;
-
- structinit.type = this;
- return structinit;
- }
-
- override bool isAssignable()
- {
- bool assignable = true;
- uint offset = ~0; // dead-store initialize to prevent spurious warning
-
- sym.determineSize(sym.loc);
-
- /* If any of the fields are const or immutable,
- * then one cannot assign this struct.
- */
- for (size_t i = 0; i < sym.fields.length; i++)
- {
- VarDeclaration v = sym.fields[i];
- //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind());
- if (i == 0)
- {
- }
- else if (v.offset == offset)
- {
- /* If any fields of anonymous union are assignable,
- * then regard union as assignable.
- * This is to support unsafe things like Rebindable templates.
- */
- if (assignable)
- continue;
- }
- else
- {
- if (!assignable)
- return false;
- }
- assignable = v.type.isMutable() && v.type.isAssignable();
- offset = v.offset;
- //printf(" -> assignable = %d\n", assignable);
- }
-
- return assignable;
- }
-
override bool isBoolean()
{
return false;
return memType().isString();
}
- override bool isAssignable()
- {
- return memType().isAssignable();
- }
-
override bool needsDestruction()
{
return memType().needsDestruction();
extern (D) this(Type t1)
{
super(Ttuple);
- arguments = new Parameters();
- arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null));
+ arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null));
}
extern (D) this(Type t1, Type t2)
{
super(Ttuple);
- arguments = new Parameters();
- arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null));
- arguments.push(new Parameter(Loc.initial, STC.none, t2, null, null, null));
+ arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null),
+ new Parameter(Loc.initial, STC.none, t2, null, null, null));
}
static TypeTuple create() @safe
dg("@nogc");
if (tf.isProperty)
dg("@property");
+
+ /* The following is more or less like dmd.hdrgen.stcToBuffer(), in the future
+ * it should be merged. The idea is consistent ordering
+ */
+ STC stc;
if (tf.isRef)
- dg("ref");
+ stc |= STC.ref_;
if (tf.isReturn && !tf.isReturnInferred)
- dg("return");
+ stc |= STC.return_;
if (tf.isScopeQual && !tf.isScopeInferred)
- dg("scope");
+ stc |= STC.scope_;
+ if (tf.isReturnScope)
+ stc |= STC.returnScope;
+ final switch (buildScopeRef(stc))
+ {
+ case ScopeRef.None: break;
+ case ScopeRef.Scope: dg("scope"); break;
+ case ScopeRef.Return: dg("return"); break;
+ case ScopeRef.ReturnScope: dg("return"); dg("scope"); break;
+ case ScopeRef.ReturnRef: dg("return"); dg("ref"); break;
+ case ScopeRef.Ref: dg("ref"); break;
+ case ScopeRef.RefScope: dg("ref"); dg("scope"); break;
+ case ScopeRef.ReturnRef_Scope: dg("return"); dg("ref"); dg("scope"); break;
+ case ScopeRef.Ref_ReturnScope: dg("ref"); dg("return"); dg("scope"); break;
+ }
+
if (tf.isLive)
dg("@live");
{
Type *typeSemantic(Type *t, Loc loc, Scope *sc);
Type *merge(Type *type);
+ Expression *defaultInitLiteral(Type *t, Loc loc);
}
enum class TY : uint8_t
virtual bool isUnsigned();
virtual bool isScopeClass();
virtual bool isString();
- virtual bool isAssignable();
virtual bool isBoolean();
bool isConst() const { return (mod & MODconst) != 0; }
bool isImmutable() const { return (mod & MODimmutable) != 0; }
virtual ClassDeclaration *isClassHandle();
virtual structalign_t alignment();
- virtual Expression *defaultInitLiteral(Loc loc);
virtual int hasWild() const;
virtual bool hasVoidInitPointers();
virtual bool hasUnsafeBitpatterns();
const char *kind() override;
TypeError *syntaxCopy() override;
- Expression *defaultInitLiteral(Loc loc) override;
void accept(Visitor *v) override { v->visit(this); }
};
bool isScalar() override;
bool isUnsigned() override;
bool isBoolean() override;
- Expression *defaultInitLiteral(Loc loc) override;
TypeBasic *elementType();
void accept(Visitor *v) override { v->visit(this); }
unsigned alignsize() override;
bool isString() override;
structalign_t alignment() override;
- Expression *defaultInitLiteral(Loc loc) override;
bool hasUnsafeBitpatterns() override;
bool hasVoidInitPointers() override;
bool hasInvariant() override;
unsigned alignsize() override;
TypeStruct *syntaxCopy() override;
structalign_t alignment() override;
- Expression *defaultInitLiteral(Loc loc) override;
- bool isAssignable() override;
bool isBoolean() override;
bool needsDestruction() override;
bool needsCopyOrPostblit() override;
bool isUnsigned() override;
bool isBoolean() override;
bool isString() override;
- bool isAssignable() override;
bool needsDestruction() override;
bool needsCopyOrPostblit() override;
bool needsNested() override;
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
-import dmd.globals;
import dmd.identifier;
import dmd.location;
import core.stdc.stdio;
import dmd.aggregate;
+import dmd.arraytypes;
import dmd.astenums;
-import dmd.declaration;
import dmd.common.outbuffer;
+import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
-import dmd.dtemplate : isDsymbol;
import dmd.dsymbol : PASS;
+import dmd.dtemplate : isDsymbol;
import dmd.errors;
import dmd.escape;
import dmd.expression;
+import dmd.expressionsem;
import dmd.func;
import dmd.globals;
+import dmd.id;
+import dmd.identifier;
import dmd.init;
import dmd.location;
import dmd.mtype;
import dmd.semantic2;
import dmd.semantic3;
import dmd.tokens;
+import dmd.typesem : unqualify;
import dmd.visitor;
import dmd.visitor.postorder;
override void visit(ArrayLiteralExp e)
{
- if (e.type.toBasetype().isTypeSArray() || !e.elements || !e.elements.length || e.onstack)
+ const dim = e.elements ? e.elements.length : 0;
+ if (e.type.toBasetype().isTypeSArray() || dim == 0 || e.onstack)
return;
if (setGC(e, "this array literal"))
return;
return;
}
+ if (!global.params.useGC)
+ {
+ if (!checkOnly)
+ {
+ version (IN_GCC)
+ error(e.loc, "this array literal requires the GC and cannot be used with `???`");
+ else
+ error(e.loc, "this array literal requires the GC and cannot be used with `-betterC`");
+ }
+ err = true;
+ return;
+ }
+
+ if (!lowerArrayLiteral(e, sc))
+ {
+ err = true;
+ return;
+ }
+
f.printGCUsage(e.loc, "array literal may cause a GC allocation");
}
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
import dmd.dtemplate;
import dmd.errors;
import dmd.escape;
import dmd.expression;
-
import dmd.func;
-import dmd.globals;
import dmd.hdrgen;
import dmd.identifier;
import dmd.init;
checkObErrors(obstate);
}
+private:
+
alias ObNodes = Array!(ObNode*);
alias StmtState = dmd.stmtstate.StmtState!ObNode;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
-import dmd.globals;
-import dmd.gluelayer;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
{
extern(D) final this()
{
- ObjcGlue.initialize();
+
}
override void setObjc(ClassDeclaration cd)
{
VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
- ObjcGlue.initialize();
ObjcSelector._init();
}
import dmd.expressionsem;
import dmd.func;
import dmd.funcsem;
-import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
{
Expression e = new StringExp(Loc.initial, EXPtoString(stripAssignOp(op)));
e = e.expressionSemantic(sc);
- auto tiargs = new Objects();
- tiargs.push(e);
+ auto tiargs = new Objects(e);
return tiargs;
}
// Convert to IndexExp
if (ae.arguments.length == 1)
- return new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]).expressionSemantic(sc);
+ {
+ auto idxexp = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
+ idxexp.modifiable = ae.modifiable;
+ return idxexp.expressionSemantic(sc);
+ }
}
break;
}
return build_overload(e.loc, sc, e.e1, null, fd);
}
}
- auto tiargs = new Objects();
- tiargs.push(e.to);
+ auto tiargs = new Objects(e.to);
return dotTemplateCall(e.e1, Id.opCast, tiargs).expressionSemantic(sc);
}
// Didn't find it. Forward to aliasthis
// Try opBinary and opBinaryRight
Dsymbol s = search_function(ad1, Id.opBinary);
- if (s && !s.isTemplateDeclaration())
+
+ if (s && !(s.isTemplateDeclaration() || s.isOverloadSet))
{
error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
return ErrorExp.get();
}
Dsymbol s_r = search_function(ad2, Id.opBinaryRight);
- if (s_r && !s_r.isTemplateDeclaration())
+ if (s_r && !(s_r.isTemplateDeclaration() || s_r.isOverloadSet()))
{
error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
return ErrorExp.get();
{
if (auto ae = e.e1.isArrayExp())
{
+ markArrayExpModifiable(ae);
+
ae.e1 = ae.e1.expressionSemantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);
Expression ae1old = ae.e1;
if (Expression result = e.binSemanticProp(sc))
return result;
+ if (auto result = rewriteIndexAssign(e, sc, aliasThisStop))
+ return result;
+
// Don't attempt 'alias this' if an error occurred
if (e.e1.type.isTypeError() || e.e2.type.isTypeError())
return ErrorExp.get();
AggregateDeclaration ad1 = isAggregate(e.e1.type);
Dsymbol s = search_function(ad1, Id.opOpAssign);
- if (s && !s.isTemplateDeclaration())
+ if (s && !(s.isTemplateDeclaration() || s.isOverloadSet()))
{
error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars());
return ErrorExp.get();
if (s)
{
functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null);
- if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
return ErrorExp.get();
}
FuncDeclaration lastf = m.lastf;
if (s_r)
{
functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null);
- if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
return ErrorExp.get();
}
if (m.count > 1)
return fd;
if (TemplateDeclaration td = s2.isTemplateDeclaration())
return td;
+ if (OverloadSet os = s2.isOverloadSet())
+ return os;
}
return null;
}
void visitEqual(EqualExp e)
{
- //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
+ //printf("EqualExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (auto lowering = e.lowering)
+ {
+ optimize(lowering, result, keepLvalue);
+ return;
+ }
+
if (binOptimize(e, WANTvalue))
return;
+
Expression e1 = fromConstInitializer(result, e.e1);
Expression e2 = fromConstInitializer(result, e.e2);
if (e1.op == EXP.error)
md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
+ if (token.value == TOK.int32Literal)
+ {
+ auto edition = token.intvalue;
+ if (edition < Edition.min || Edition.max < edition)
+ {
+ error("module edition %lld must be in the range %d ... %d", edition, Edition.min, Edition.max);
+ edition = edition.min;
+ }
+ mod.edition = cast(Edition)edition;
+ nextToken();
+ }
+
if (token.value != TOK.semicolon)
error("`;` expected following module declaration instead of `%s`", token.toChars());
nextToken();
if (auto id = (*exps)[0].isIdentifierExp())
if (id.ident == Id.__edition_latest_do_not_use)
{
- mod.edition = Edition.latest;
+ mod.edition = Edition.max; // latest edition
continue;
}
return orig;
}
+ if (0) // replace this with -preview=return in next PR
+ switch (orig & (STC.out_ | STC.ref_ | STC.scope_ | STC.return_))
+ {
+ case STC.return_ | STC.ref_:
+ if (!(orig & STC.returnRef))
+ error("`return` `ref` attribute pair must be written as '`return ref`'");
+ break;
+
+ case STC.return_ | STC.out_:
+ if (!(orig & STC.returnRef))
+ error("`return` `out` attribute pair must be written as '`return out`'");
+ break;
+
+ case STC.return_ | STC.scope_:
+ if (!(orig & STC.returnScope))
+ error("`return` `scope` attribute pair must be written as '`return scope`'");
+ break;
+
+ case STC.return_ | STC.ref_ | STC.scope_:
+ if (!(orig & (STC.returnRef | STC.returnScope)))
+ error("`return` `ref` `scope` attribute triple must be written as '`return ref`' and '`scope`' or '`return scope`' and '`ref`'");
+ break;
+
+ case STC.return_ | STC.out_ | STC.scope_:
+ if (!(orig & (STC.returnRef | STC.returnScope)))
+ error("`return` `out` `scope` attribute triple must be written as '`return out`' and '`scope`' or '`return scope`' and '`out`'");
+ break;
+
+ default:
+ break;
+ }
+
checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest);
checkConflictSTCGroup(STC.gshared | STC.shared_);
checkConflictSTCGroup!true(STC.safeGroup);
*/
private STC parsePostfix(STC storageClass, AST.Expressions** pudas)
{
+ const STC prefix = storageClass;
while (1)
{
STC stc;
break;
case TOK.return_:
+ {
stc = STC.return_;
- if (peekNext() == TOK.scope_)
- stc |= STC.returnScope; // recognize `return scope`
+ TOK next = peekNext();
+ if (next == TOK.scope_) // recognize the `return scope` pair
+ {
+ stc |= STC.scope_ | STC.returnScope;
+ nextToken(); // consume it
+ }
+ else if (next == TOK.ref_) // recognize the `return ref` pair
+ {
+ stc |= STC.ref_ | STC.returnRef;
+ nextToken(); // consume it
+ }
+ else if (prefix & STC.ref_) // https://dlang.org/spec/function.html#struct-return-methods
+ stc |= STC.returnRef; // treat member function `ref int fp() return;` as `int fp() return ref;`
break;
+ }
case TOK.scope_:
stc = STC.scope_;
nextToken();
continue;
}
+
+ /* This is so we can enforce `return ref`, but not foul things up with an extra `ref`
+ * which is not accounted for in the semantic routines. The problem comes about because
+ * there's no way to attach `return ref` to the implicit `this` parameter but not the return value.
+ */
+ if (!(prefix & STC.ref_) && storageClass & STC.ref_ && storageClass & STC.returnRef)
+ storageClass &= ~STC.ref_;
+
return storageClass;
}
storageClass = appendStorageClass(storageClass, stc);
STC varargsStc;
// Attributes allowed for ...
- enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope;
+ enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef;
check(TOK.leftParenthesis);
while (1)
goto L2;
case TOK.return_:
+ {
stc = STC.return_;
- if (peekNext() == TOK.scope_)
- stc |= STC.returnScope;
+ TOK next = peekNext();
+ if (next == TOK.scope_) // recognize the `return scope` pair
+ {
+ stc |= STC.scope_ | STC.returnScope;
+ nextToken(); // consume it
+ }
+ else if (next == TOK.ref_) // recognize the `return ref` pair
+ {
+ stc |= STC.ref_ | STC.returnRef;
+ nextToken(); // consume it
+ }
+ else if (next == TOK.out_) // recognize the `return out` pair
+ {
+ stc |= STC.out_ | STC.returnRef;
+ nextToken(); // consume it
+ }
goto L2;
+ }
L2:
storageClass = appendStorageClass(storageClass, stc);
continue;
if (width)
{
if (_init)
- error("initializer not allowed for bit-field declaration");
+ error("initializer not allowed for bitfield declaration");
if (storage_class)
- error("storage class not allowed for bit-field declaration");
+ error("storage class not allowed for bitfield declaration");
s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
}
else
error("matching `}` expected, not end of file");
break;
- case TOK.colonColon: // treat as two separate : tokens for iasmgcc
- *ptoklist = allocateToken();
- **ptoklist = this.token;
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
-
- *ptoklist = allocateToken();
- **ptoklist = this.token;
- (*ptoklist).value = TOK.colon;
- ptoklist = &(*ptoklist).next;
-
- *ptoklist = null;
- nextToken();
- continue;
-
default:
*ptoklist = allocateToken();
**ptoklist = this.token;
return e;
}
- private AST.Expression parseCondExp()
+ AST.Expression parseCondExp()
{
auto e = parseOrOrExp();
if (token.value == TOK.question)
if (data.ptr && data.ptr != &smallarray[0])
mem.xfree(data.ptr);
}
+
+ // this is using a template constraint because of ambiguity with this(size_t) when T is
+ // int, and c++ header generation doesn't accept wrapping this in static if
+ extern(D) this()(T[] elems ...) pure nothrow if (is(T == struct) || is(T == class))
+ {
+ this(elems.length);
+ foreach(i; 0 .. elems.length)
+ {
+ this[i] = elems[i];
+ }
+ }
+
///returns elements comma separated in []
extern(D) const(char)[] toString() const
{
b.popFront();
assert(b == expected[]);
}
+
+
+/// Test Array array constructor
+pure nothrow unittest
+{
+ //check to make sure that this works with the aliases in arraytypes.d
+ import dmd.rootobject;
+ alias Objects = Array!RootObject;
+
+ auto ro1 = new RootObject();
+ auto ro2 = new RootObject();
+
+ auto aoo = new Objects(ro1, ro2);
+ assert((*aoo)[0] is ro1);
+ assert((*aoo)[1] is ro2);
+}
assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc");
}
- // Split a path and append the results to `array`
+ /****
+ * Splits a delimiter-separated path string (e.g., from an environment variable like `PATH`)
+ * into individual path fragments and appends them to the given array.
+ *
+ * This is a convenience wrapper around `splitPath` that collects all fragments
+ * into an `Array!(const(char)*)`.
+ *
+ * Params:
+ * path = A null-terminated string containing path fragments separated by platform-specific delimiters.
+ * array = The array to which each extracted fragment will be appended.
+ *
+ * See_Also:
+ * `splitPath`
+ */
extern (C++) static void appendSplitPath(const(char)* path, ref Strings array)
{
int sink(const(char)* p) nothrow
* sink = send the path pieces here, end when sink() returns !=0
* path = the path to split up.
*/
- static void splitPath(int delegate(const(char)*) nothrow sink, const(char)* path)
+ static void splitPath(scope int delegate(const(char)*) nothrow sink, const(char)* path)
{
if (!path)
return;
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
-// https://sites.google.com/site/murmurhash/
+// https://github.com/aappleby/smhasher/
uint calcHash(scope const(char)[] data) @nogc nothrow pure @safe
{
return calcHash(cast(const(ubyte)[])data);
import dmd.expression;
import dmd.func;
import dmd.funcsem : isRootTraitsCompilesScope;
-import dmd.globals : FeatureState, global;
+import dmd.globals : FeatureState;
import dmd.id;
import dmd.identifier;
import dmd.location;
if (ad.sizeok != Sizeok.done)
ad.determineSize(ad.loc);
- import dmd.globals : FeatureState;
const hasPointers = v.type.hasPointers();
if (hasPointers)
{
ForeachStatement *fes; // if nested function for ForeachStatement, this is it
Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
Dsymbol *inunion; // !=null if processing members of a union
- d_bool nofree; // true if shouldn't free it
- d_bool inLoop; // true if inside a loop (where constructor calls aren't allowed)
- d_bool inDefaultArg; // true if inside a default argument (where __FILE__, etc are evaluated at the call site)
- int intypeof; // in typeof(exp)
VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init
ErrorSink *eSink; // sink for error messages
PragmaDeclaration *inlining; // inlining strategy for functions
Visibility visibility; // visibility for class members
- int explicitVisibility; // set if in an explicit visibility attribute
StorageClass stc; // storage class
DeprecatedDeclaration *depdecl; // customized deprecation message
- uint16_t flags;
+ uint16_t flags, flags2;
uint16_t previews; // state of preview switches
bool ctor() const;
*/
void lowerStaticAAs(VarDeclaration vd, Scope* sc)
{
- if (vd.storage_class & STC.manifest)
- return;
if (auto ei = vd._init.isExpInitializer())
- lowerStaticAAs(ei.exp, sc);
+ {
+ scope v = new StaticAAVisitor(sc, vd.storage_class);
+ ei.exp.accept(v);
+ }
}
/**
*/
void lowerStaticAAs(Expression e, Scope* sc)
{
- scope v = new StaticAAVisitor(sc);
+ scope v = new StaticAAVisitor(sc, STC.none);
e.accept(v);
}
{
alias visit = SemanticTimeTransitiveVisitor.visit;
Scope* sc;
+ STC storage_class;
- this(Scope* sc) scope @safe
+ this(Scope* sc, STC storage_class) scope @safe
{
this.sc = sc;
+ this.storage_class = storage_class;
}
override void visit(AssocArrayLiteralExp aaExp)
{
- if (!verifyHookExist(aaExp.loc, *sc, Id._aaAsStruct, "initializing static associative arrays", Id.object))
- return;
-
- Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty);
- hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object);
- hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id._aaAsStruct);
- auto arguments = new Expressions();
- arguments.push(aaExp);
- Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments);
-
- sc = sc.startCTFE();
- loweredExp = loweredExp.expressionSemantic(sc);
- loweredExp = resolveProperties(sc, loweredExp);
- sc = sc.endCTFE();
- loweredExp = loweredExp.ctfeInterpret();
-
- aaExp.lowering = loweredExp;
-
- semanticTypeInfo(sc, loweredExp.type);
+ if (!aaExp.lowering)
+ expressionSemantic(aaExp, sc);
+ assert(aaExp.lowering);
+ if (!(storage_class & STC.manifest)) // manifest constants create runtime copies
+ if (!aaExp.loweringCtfe)
+ {
+ aaExp.loweringCtfe = aaExp.lowering.ctfeInterpret();
+ aaExp.loweringCtfe.accept(this); // lower AAs in keys and values
+ }
+ semanticTypeInfo(sc, aaExp.lowering.type);
}
// https://issues.dlang.org/show_bug.cgi?id=24602
/**
- * Performs the semantic3 stage, which deals with function bodies.
+ * Performs the semantic3 stage of semantic analysis, which finalizes
+ * function bodies and late semantic checks for templates, mixins,
+ * aggregates, and special members.
*
* Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
sc2.linkage = funcdecl.isCsymbol() ? LINK.c : LINK.d;
sc2.stc &= STC.flowThruFunction;
sc2.visibility = Visibility(Visibility.Kind.public_);
- sc2.explicitVisibility = 0;
+ sc2.explicitVisibility = false;
sc2.aligndecl = null;
if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure)
{
// functions may be widely used by dmd-compiled projects.
// It also gives more time for the implementation of dual-context
// functions to be reworked as a frontend-only feature.
- if (funcdecl.hasDualContext())
+ if (funcdecl.hasDualContext)
{
.deprecation(funcdecl.loc, "%s `%s` function requires a dual-context, which is deprecated", funcdecl.kind, funcdecl.toPrettyChars);
if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null)
if (!funcdecl.fbody)
funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements());
- if (funcdecl.isNaked())
+ if (funcdecl.isNaked)
{
fpreinv = null; // can't accommodate with no stack frame
fpostinv = null;
exp = exp.implicitCastTo(sc2, tret);
exp = exp.toLvalue(sc2, "`ref` return");
+ checkAddressable(exp, sc2, "`ref` return");
checkReturnEscapeRef(*sc2, exp, false);
exp = exp.optimize(WANTvalue, /*keepLvalue*/ true);
}
/* https://issues.dlang.org/show_bug.cgi?id=10789
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
- if (!funcdecl.isNRVO())
+ if (!funcdecl.isNRVO)
exp = doCopyOrMove(sc2, exp, f.next, true, true);
if (tret.hasPointers())
{
// 'this' is the monitor
vsync = new VarExp(funcdecl.loc, funcdecl.vthis);
- if (funcdecl.hasDualContext())
+ if (funcdecl.hasDualContext)
{
vsync = new PtrExp(funcdecl.loc, vsync);
vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0);
}
}
- if (funcdecl.isNaked() && (funcdecl.fensures || funcdecl.frequires))
+ if (funcdecl.isNaked && (funcdecl.fensures || funcdecl.frequires))
.error(funcdecl.loc, "%s `%s` naked assembly functions with contracts are not supported", funcdecl.kind, funcdecl.toPrettyChars);
sc2.ctorflow.callSuper = CSX.none;
auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id));
auto handler = new CompoundStatement(ctor.loc, ss, ts);
- auto catches = new Catches();
auto ctch = new Catch(ctor.loc, getException(), id, handler);
- catches.push(ctch);
+ auto catches = new Catches(ctch);
ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches);
}
(ad.type && ad.type.ty != Terror))
{
// Evaluate: RTinfo!type
- auto tiargs = new Objects();
- tiargs.push(ad.type);
+ auto tiargs = new Objects(ad.type);
auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
Scope* sc3 = ti.tempdecl._scope.startCTFE();
sd.semanticTypeInfoMembers();
ad.semanticRun = PASS.semantic3done;
}
+
+ override void visit(TypeInfoAssociativeArrayDeclaration ti)
+ {
+ if (ti.semanticRun >= PASS.semantic3)
+ return;
+ ti.semanticRun = PASS.semantic3;
+
+ auto t = ti.tinfo.isTypeAArray();
+ Loc loc = t.loc;
+ auto sc2 = sc ? sc : ti._scope;
+
+ auto makeDotExp(Identifier hook)
+ {
+ // always called with naked types
+ auto tiargs = new Objects(t.index, t.next);
+
+ Expression id = new IdentifierExp(loc, Id.empty);
+ id = new DotIdExp(loc, id, Id.object);
+ id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray);
+ return new DotTemplateInstanceExp(loc, id, hook, tiargs);
+ }
+
+ void notTemplateFunction(Loc loc, Identifier id)
+ {
+ error(loc, "`%s` isn't a template function", id.toChars());
+ }
+
+ // generate ti.entry
+ auto tempinst = makeDotExp(Id.Entry);
+ auto e = expressionSemantic(tempinst, sc2);
+ assert(e.type);
+ ti.entry = e.type;
+ if (auto ts = ti.entry.isTypeStruct())
+ {
+ ts.sym.requestTypeInfo = true;
+ if (auto tmpl = ts.sym.isInstantiated())
+ tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+ }
+ semanticTypeInfo(sc2, ti.entry); // might get deferred
+
+ // generate ti.xtoHash
+ auto aaGetHash = Identifier.idPool("aaGetHash");
+ auto hashinst = makeDotExp(aaGetHash);
+ e = expressionSemantic(hashinst, sc2);
+ if (!e.isErrorExp())
+ {
+ if (!e.isVarExp() || !e.type.isTypeFunction())
+ notTemplateFunction(e.loc, aaGetHash);
+ else
+ {
+ ti.xtoHash = e.isVarExp().var;
+ if (auto tmpl = ti.xtoHash.parent.isTemplateInstance())
+ tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+ }
+ }
+
+ // generate ti.xopEqual
+ auto aaOpEqual = Identifier.idPool("aaOpEqual");
+ auto equalinst = makeDotExp(aaOpEqual);
+ e = expressionSemantic(equalinst, sc2);
+ if (!e.isErrorExp())
+ {
+ if (!e.isVarExp() || !e.type.isTypeFunction())
+ notTemplateFunction(e.loc, aaOpEqual);
+ else
+ {
+ ti.xopEqual = e.isVarExp().var;
+ if (auto tmpl = ti.xopEqual.parent.isTemplateInstance())
+ tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+ }
+ }
+ visit(cast(ASTCodegen.TypeInfoDeclaration)ti);
+ }
}
+/// Helper for semantic3 analysis of functions.
+/// This struct is part of a WIP refactoring to simplify large `visit(FuncDeclaration)` logic.
private struct FuncDeclSem3
{
// The FuncDeclaration subject to Semantic analysis
}
}
+/***************************************
+ * Search sd for a member function of the form:
+ * `extern (D) string toString();`
+ * Params:
+ * sd = struct declaration to search
+ * Returns:
+ * FuncDeclaration of `toString()` if found, `null` if not
+ */
+FuncDeclaration search_toString(StructDeclaration sd)
+{
+ Dsymbol s = search_function(sd, Id.tostring);
+ FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
+ if (!fd)
+ return null;
+
+ __gshared TypeFunction tftostring;
+ if (!tftostring)
+ {
+ tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
+ tftostring = tftostring.merge().toTypeFunction();
+ }
+ return fd.overloadExactMatch(tftostring);
+}
+
+/**
+ * Ensures special members of a struct are fully analysed
+ * before the backend emits TypeInfo.
+ *
+ * Handles late semantic analysis for members like `opEquals`, `opCmp`,
+ * `toString`, `toHash`, postblit, and destructor.
+ */
void semanticTypeInfoMembers(StructDeclaration sd)
{
if (sd.xeq &&
}
}
-/***********************************************
- * Check that the function contains any closure.
- * If it's @nogc, report suitable errors.
- * This is mostly consistent with FuncDeclaration::needsClosure().
+/**
+ * Determine whether the given function will need to allocate a _closure_ and
+ * verify that such an allocation is allowed under the current compilation
+ * settings.
*
- * Returns:
- * true if any errors occur.
+ * Whenever an error is emitted, every nested function that actually closes
+ * over a variable is listed in a supplemental diagnostic, together with the
+ * location of the captured variable’s declaration. (This extra walk is
+ * skipped when the compiler is gagged.)
+ *
+ * See_Also:
+ * $(UL
+ * $(LI `FuncDeclaration.needsClosure`)
+ * $(LI `FuncDeclaration.setGC`)
+ * $(LI `FuncDeclaration.printGCUsage`)
+ * )
*/
extern (D) bool checkClosure(FuncDeclaration fd)
{
import dmd.expressionsem;
import dmd.func;
import dmd.funcsem;
-import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
* e0 = a new side effect part will be appended to it.
* e = original expression
* alwaysCopy = if true, build new temporary variable even if e is trivial.
+ * stc = storage class flags to add to new temporary variable
* Returns:
* When e is trivial and alwaysCopy == false, e itself is returned.
* Otherwise, a new VarExp is returned.
* e's lvalue-ness will be handled well by STC.ref_ or STC.rvalue.
*/
Expression extractSideEffect(Scope* sc, const char[] name,
- ref Expression e0, Expression e, bool alwaysCopy = false)
+ ref Expression e0, Expression e, bool alwaysCopy = false, STC stc = STC.exptemp)
{
//printf("extractSideEffect(e: %s)\n", e.toChars());
(sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e)))
return e;
- auto vd = copyToTemp(STC.none, name, e);
- vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue;
+ stc |= (e.isLvalue() ? STC.ref_ : STC.rvalue);
+ auto vd = copyToTemp(stc, name, e);
e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd)
.expressionSemantic(sc));
extern (D) this(Loc loc, Expression exp)
{
- Expressions* exps = new Expressions();
- exps.push(exp);
+ Expressions* exps = new Expressions(exp);
this(loc, exps);
}
*/
extern (C++) class AsmStatement : Statement
{
- Token* tokens;
- bool caseSensitive; // for register names
+ Token* tokens; // linked list of tokens for one instruction or pseudo op
+ static struct BitFields
+ {
+ bool caseSensitive; // for register names, and only turned on when doing MASM style inline asm
+ bool isVolatile; // ImportC asm "volatile"
+ bool isInline; // ImportC asm "inline"
+ }
+ import dmd.common.bitfields;
+ mixin(generateBitFields!(BitFields, ubyte));
extern (D) this(Loc loc, Token* tokens) @safe
{
extern (C++) final class InlineAsmStatement : AsmStatement
{
void* asmcode;
+ ulong regs; // mask of registers modified (must match regm_t in back end)
uint asmalign; // alignment of this statement
- uint regs; // mask of registers modified (must match regm_t in back end)
bool refparam; // true if function parameter is referenced
- bool naked; // true if function is to be naked
+ bool naked; // true if function is to be naked (no prolog/epilog)
extern (D) this(Loc loc, Token* tokens) @safe
{
{
public:
Token *tokens;
- d_bool caseSensitive; // for register names
+private:
+ uint8_t bitFields;
+public:
+ bool caseSensitive() const; // for register names
+ bool caseSensitive(bool v);
+ bool isVolatile() const; // importC asm volatile
+ bool isVolatile(bool v);
+ bool isInline() const; // importC asm inline
+ bool isInline(bool v);
AsmStatement *syntaxCopy() override;
void accept(Visitor *v) override { v->visit(this); }
{
public:
void *asmcode;
- unsigned asmalign; // alignment of this statement
- unsigned regs; // mask of registers modified (must match regm_t in back end)
+ unsigned long long regs; // mask of registers modified (must match regm_t in back end)
+ unsigned asmalign; // alignment of this statement
d_bool refparam; // true if function parameter is referenced
d_bool naked; // true if function is to be naked
handler = new CompoundStatement(Loc.initial, handler, ts);
}
- auto catches = new Catches();
auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
ctch.internalCatch = true;
- catches.push(ctch);
+ auto catches = new Catches(ctch);
Statement st = new TryCatchStatement(Loc.initial, _body, catches);
if (sfinally)
* } finally { v2.~this(); }
* } finally { v1.~this(); }
*/
- auto ainit = new Statements();
- ainit.push(fs._init);
+ auto ainit = new Statements(fs._init);
fs._init = null;
ainit.push(fs);
Statement s = new CompoundStatement(fs.loc, ainit);
Type tindex = (*fs.parameters)[0].type;
if (!tindex.isIntegral())
{
- error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars());
+ error(fs.loc, "foreach: index cannot be of non-integral type `%s`", tindex.toChars());
return retError();
}
/* What cases to deprecate implicit conversions for:
Expression ve = new VarExp(loc, vd);
ve.type = tfront;
- auto exps = new Expressions();
- exps.push(ve);
+ auto exps = new Expressions(ve);
int pos = 0;
while (exps.length < dim)
{
*/
//printf("ForeachRangeStatement::semantic() %p\n", fs);
+
+ if (fs.param.storageClass & STC.manifest)
+ {
+ error(fs.loc, "cannot declare `enum` loop variables for non-unrolled foreach");
+ }
+ if (fs.param.storageClass & STC.alias_)
+ {
+ error(fs.loc, "cannot declare `alias` loop variables for non-unrolled foreach");
+ }
+
auto loc = fs.loc;
fs.lwr = fs.lwr.expressionSemantic(sc);
fs.lwr = resolveProperties(sc, fs.lwr);
auto arguments = new Expressions();
arguments.push(ss.condition);
- auto compileTimeArgs = new Objects();
// The type & label no.
- compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
+ auto compileTimeArgs = new Objects(new TypeExp(ss.loc, ss.condition.type.nextOf()));
// The switch labels
foreach (caseString; *csCopy)
*/
Scope* sc2 = sc.push();
sc2.eSink = global.errorSinkNull;
- bool err = checkReturnEscapeRef(*sc2, rs.exp, true);
+ bool addressable = checkAddressable(rs.exp, sc2, "`ref` return");
+ bool escapes = checkReturnEscapeRef(*sc2, rs.exp, true);
sc2.pop();
- if (err)
+ if (!addressable)
+ turnOffRef(() { checkAddressable(rs.exp, sc, "`ref` return"); });
+ else if (escapes)
turnOffRef(() { checkReturnEscapeRef(*sc, rs.exp, false); });
else if (!rs.exp.type.constConv(tf.next))
turnOffRef(
auto tmp = copyToTemp(STC.none, "__sync", ss.exp);
tmp.dsymbolSemantic(sc);
- auto cs = new Statements();
- cs.push(new ExpStatement(ss.loc, tmp));
-
- auto args = new Parameters();
- args.push(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type, null, null, null));
+ auto cs = new Statements(new ExpStatement(ss.loc, tmp));
+ auto args = new Parameters(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type,
+ null, null, null));
FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
Expression tmpExp = new VarExp(ss.loc, tmp);
- auto cs = new Statements();
- cs.push(new ExpStatement(ss.loc, tmp));
+ auto cs = new Statements(new ExpStatement(ss.loc, tmp));
/* This is just a dummy variable for "goto skips declaration" error.
* Backend optimizer could remove this unused variable.
v.dsymbolSemantic(sc);
cs.push(new ExpStatement(ss.loc, v));
- auto enterArgs = new Parameters();
- enterArgs.push(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
+ auto enterArgs = new Parameters(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
Expression e = new AddrExp(ss.loc, tmpExp);
e.type = Type.tvoid; // do not run semantic on e
cs.push(new ExpStatement(ss.loc, e));
- auto exitArgs = new Parameters();
- exitArgs.push(new Parameter(Loc.initial, STC.none, t, null, null, null));
+ auto exitArgs = new Parameters(new Parameter(Loc.initial, STC.none, t, null, null, null));
FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
e = new CallExp(ss.loc, fdexit, tmpExp);
exp = exp.checkGC(sc);
if (exp.isErrorExp())
return false;
- if (!exp.type.isNaked())
+ if (!exp.type.isNaked)
{
// @@@DEPRECATED_2.112@@@
// Deprecated in 2.102, change into an error & return false in 2.112
FuncDeclaration fdapply;
TypeDelegate dgty;
- auto params = new Parameters();
- params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
- auto dgparams = new Parameters();
- dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
+ auto params = new Parameters(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
+ auto dgparams = new Parameters(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
if (dim == 2)
dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
{
auto taa = tab.isTypeAArray();
- Expression ec;
const dim = fs.parameters.length;
// Check types
Parameter p = (*fs.parameters)[0];
Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
{
- error(fs.loc, "`foreach`: index must be type `%s`, not `%s`",
- ti.toChars(), ta.toChars());
+ error(fs.loc, "`foreach`: index parameter `%s%s` must be type `%s`, not `%s`",
+ isRef ? "ref ".ptr : "".ptr, p.toChars(), ti.toChars(), ta.toChars());
return null;
}
p = (*fs.parameters)[1];
Type taav = taa.nextOf();
if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
{
- error(fs.loc, "`foreach`: value must be type `%s`, not `%s`",
- taav.toChars(), ta.toChars());
+ error(fs.loc, "`foreach`: value parameter `%s%s` must be type `%s`, not `%s`",
+ isRef ? "ref ".ptr : "".ptr, p.toChars(), taav.toChars(), ta.toChars());
return null;
}
/* Call:
- * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
- * _aaApply(aggr, keysize, flde)
- *
- * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
- * _aaApply2(aggr, keysize, flde)
+ * int _d_aaApply(V[K] aa, int delegate(V*))
+ * or
+ * int _d_aaApply2(V[K] aa, int delegate(K*, V*))
*/
- __gshared FuncDeclaration* fdapply = [null, null];
- __gshared TypeDelegate* fldeTy = [null, null];
- ubyte i = (dim == 2 ? 1 : 0);
- if (!fdapply[i])
- {
- auto params = new Parameters();
- params.push(new Parameter(Loc.initial, STC.none, Type.tvoid.pointerTo(), null, null, null));
- params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null));
- auto dgparams = new Parameters();
- dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
- if (dim == 2)
- dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
- fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
- params.push(new Parameter(Loc.initial, STC.none, fldeTy[i], null, null, null));
- fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
- }
-
- auto exps = new Expressions();
- exps.push(fs.aggr);
- auto keysize = taa.index.size();
- if (keysize == SIZE_INVALID)
- return null;
- assert(keysize < keysize.max - target.ptrsize);
- keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
- // paint delegate argument to the type runtime expects
- Expression fexp = flde;
- if (!fldeTy[i].equals(flde.type))
- {
- fexp = new CastExp(fs.loc, flde, flde.type);
- fexp.type = fldeTy[i];
- }
- exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
- exps.push(fexp);
- ec = new VarExp(Loc.initial, fdapply[i], false);
- ec = new CallExp(fs.loc, ec, exps);
- ec.type = Type.tint32; // don't run semantic() on ec
- return ec;
+ auto loc = fs.loc;
+ Identifier hook = dim == 2 ? Id._d_aaApply2 : Id._d_aaApply;
+ Expression func = new IdentifierExp(loc, Id.empty);
+ func = new DotIdExp(loc, func, Id.object);
+ auto tiargs = new Objects(taa.index.substWildTo(MODFlags.const_),
+ taav.substWildTo(MODFlags.const_),
+ flde.type.substWildTo(MODFlags.const_));
+ func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+ auto arguments = new Expressions(fs.aggr, flde);
+ auto call = new CallExp(loc, func, arguments);
+ return call;
}
private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc)
// Construct a switch statement around the return value
// of the apply function.
Statement s;
- auto a = new Statements();
// default: break; takes care of cases 0 and 1
s = new BreakStatement(Loc.initial, null);
s = new DefaultStatement(Loc.initial, s);
- a.push(s);
+ auto a = new Statements(s);
// cases 2...
foreach (i, c; *cases)
*/
private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
{
+ auto tab = fs.aggr.type.toBasetype();
+ auto taa = tab.isTypeAArray();
+ const isStaticOrDynamicArray = tab.isTypeSArray() || tab.isTypeDArray();
+
auto params = new Parameters();
foreach (i, p; *fs.parameters)
{
p.type = p.type.typeSemantic(fs.loc, sc);
p.type = p.type.addStorageClass(p.storageClass);
+ p.type = p.type.substWildTo(MODFlags.const_);
if (tfld)
{
Parameter param = tfld.parameterList[i];
LcopyArg:
id = Identifier.generateId("__applyArg", cast(int)i);
- Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
- auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
+ // Make sure `p.type` matches the signature expected by the druntime helpers.
+ const isIndexParam = i == 0 && fs.parameters.length == 2;
+ Type userType = p.type;
+ Expression initExp = new IdentifierExp(fs.loc, id);
+ if (taa)
+ {
+ p.type = isIndexParam ? taa.index : taa.nextOf();
+ p.type = p.type.substWildTo(MODFlags.const_);
+ }
+ else if (isStaticOrDynamicArray && isIndexParam)
+ {
+ p.type = Type.tsize_t;
+ // If the user used an incompatible index type (e.g., `int` instead
+ // of `size_t` - which is deprecated nowadays), then add a cast.
+ if (!p.type.implicitConvTo(userType))
+ initExp = new CastExp(fs.loc, initExp, userType);
+ }
+
+ Initializer initializer = new ExpInitializer(fs.loc, initExp);
+ auto v = new VarDeclaration(fs.loc, userType, p.ident, initializer);
v.storage_class |= STC.temp | (stc & STC.scope_);
Statement s = new ExpStatement(fs.loc, v);
fs._body = new CompoundStatement(fs.loc, s, fs._body);
// reference .object.Throwable
c.type = getThrowable();
}
- else if (!c.type.isNaked() && !c.type.isConst())
+ else if (!c.type.isNaked && !c.type.isConst())
{
// @@@DEPRECATED_2.115@@@
// Deprecated in 2.105, change into an error & uncomment assign in 2.115
const bool skipCheck = isStatic && needExpansion;
if (!skipCheck && (dim < 1 || dim > 2))
{
- error(fs.loc, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
+ error(fs.loc, "only one (element) or two (index, element) arguments allowed for sequence `foreach`, not %llu",
+ ulong(dim));
return returnEarly();
}
const bool skip = isStatic && needExpansion;
if (!skip && dim == 2)
{
- // Declare key
+ // Declare index
if (p.isReference() || p.isLazy())
{
- error(fs.loc, "no storage class for key `%s`", p.ident.toChars());
+ error(fs.loc, "invalid storage class `%s` for index `%s`",
+ stcToString(p.storageClass).ptr, p.ident.toChars());
return returnEarly();
}
if (!p.type.isIntegral())
{
- error(fs.loc, "foreach: key cannot be of non-integral type `%s`",
+ error(fs.loc, "foreach: index cannot be of non-integral type `%s`",
p.type.toChars());
return returnEarly();
}
if (storageClass & (STC.out_ | STC.lazy_) ||
storageClass & STC.ref_ && !te)
{
- error(fs.loc, "no storage class for value `%s`", ident.toChars());
+ error(fs.loc, "invalid storage class `%s` for element `%s`",
+ stcToString(p.storageClass).ptr, ident.toChars());
return false;
}
Declaration var;
var = new AliasDeclaration(loc, ident, t);
if (paramtype)
{
- error(fs.loc, "cannot specify element type for symbol `%s`", fs.toChars());
+ error(fs.loc, "cannot specify element type for symbol `%s`", ident.toChars());
+ return false;
+ }
+ if (storageClass & STC.manifest)
+ {
+ error(fs.loc, "invalid storage class `enum` for element `%s`",
+ ident.toChars());
return false;
}
}
{
static auto errorStatements()
{
- auto a = new Statements();
- a.push(new ErrorStatement());
+ auto a = new Statements(new ErrorStatement());
return a;
}
toCBuffer(s, &buf, &hgs);
printf("tm ==> s = %s\n", buf.peekChars());
}
- auto a = new Statements();
- a.push(s);
+ auto a = new Statements(s);
return a;
case STMT.Forwarding:
else
s = cs.elsebody;
- auto a = new Statements();
- a.push(s);
+ auto a = new Statements(s);
return a;
case STMT.StaticForeach:
{
return result;
}
- result = new Statements();
- result.push(s);
+ result = new Statements(s);
return result;
}
else
BitFieldStyle bitFieldStyle; /// different C compilers do it differently
/**
- * Indicates whether the specified bit-field contributes to the alignment
+ * Indicates whether the specified bitfield contributes to the alignment
* of the containing aggregate.
* E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length)
- * bit-fields.
+ * bitfields.
*/
extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd);
}
class FuncDeclaration;
class Parameter;
+namespace dmd
+{
+ bool isDiscardable(TemplateInstance*);
+ bool needsCodegen(TemplateInstance*);
+}
+
class Tuple final : public RootObject
{
public:
virtual bool declareParameter(Scope *sc) = 0;
virtual void print(RootObject *oarg, RootObject *oded) = 0;
virtual RootObject *specialization() = 0;
- virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0;
virtual bool hasDefaultArg() = 0;
DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; }
bool declareParameter(Scope *sc) override final;
void print(RootObject *oarg, RootObject *oded) override final;
RootObject *specialization() override final;
- RootObject *defaultArg(Loc instLoc, Scope *sc) override final;
bool hasDefaultArg() override final;
void accept(Visitor *v) override { v->visit(this); }
};
bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
- RootObject *defaultArg(Loc instLoc, Scope *sc) override;
bool hasDefaultArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
- RootObject *defaultArg(Loc instLoc, Scope *sc) override;
bool hasDefaultArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
bool declareParameter(Scope *sc) override;
void print(RootObject *oarg, RootObject *oded) override;
RootObject *specialization() override;
- RootObject *defaultArg(Loc instLoc, Scope *sc) override;
bool hasDefaultArg() override;
void accept(Visitor *v) override { v->visit(this); }
};
unsigned char inuse; // for recursive expansion detection
TemplateInstance *syntaxCopy(Dsymbol *) override;
- Dsymbol *toAlias() override final; // resolve real symbol
const char *kind() const override;
const char* toPrettyCharsHelper() override final;
Identifier *getIdent() override final;
- bool isDiscardable();
- bool needsCodegen();
-
void accept(Visitor *v) override { v->visit(this); }
};
TemplateParameter *isTemplateParameter(RootObject *o);
bool isError(const RootObject *const o);
void printTemplateStats(bool listInstances, ErrorSink* eSink);
+ void printInstantiationTrace(TemplateInstance *ti);
}
alias funcLeastAsSpecialized = dmd.funcsem.leastAsSpecialized;
+enum LOG = false;
+
/************************************
* Perform semantic analysis on template.
* Params:
tempdecl.semanticRun = PASS.semanticdone;
}
+/*******************************************
+ * Match to a particular TemplateParameter.
+ * Input:
+ * instLoc location that the template is instantiated.
+ * tiargs[] actual arguments to template instance
+ * i i'th argument
+ * parameters[] template parameters
+ * dedtypes[] deduced arguments to template instance
+ * *psparam set to symbol declared and initialized to dedtypes[i]
+ */
+MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgParameter()
+ {
+ RootObject oarg;
+
+ if (i < tiargs.length)
+ oarg = (*tiargs)[i];
+ else
+ {
+ // Get default argument instead
+ oarg = tp.defaultArg(instLoc, sc);
+ if (!oarg)
+ {
+ assert(i < dedtypes.length);
+ // It might have already been deduced
+ oarg = dedtypes[i];
+ if (!oarg)
+ return matchArgNoMatch();
+ }
+ }
+ return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam);
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ /* The rest of the actual arguments (tiargs[]) form the match
+ * for the variadic parameter.
+ */
+ assert(i + 1 == dedtypes.length); // must be the last one
+ Tuple ovar;
+
+ if (Tuple u = isTuple(dedtypes[i]))
+ {
+ // It has already been deduced
+ ovar = u;
+ }
+ else if (i + 1 == tiargs.length && isTuple((*tiargs)[i]))
+ ovar = isTuple((*tiargs)[i]);
+ else
+ {
+ ovar = new Tuple();
+ //printf("ovar = %p\n", ovar);
+ if (i < tiargs.length)
+ {
+ //printf("i = %d, tiargs.length = %d\n", i, tiargs.length);
+ ovar.objects.setDim(tiargs.length - i);
+ foreach (j, ref obj; ovar.objects)
+ obj = (*tiargs)[i + j];
+ }
+ }
+ return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam);
+ }
+
+ if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+
+ return matchArgParameter();
+}
+
+MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ //printf("\tm = %d\n", MATCH.nomatch);
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgType(TemplateTypeParameter ttp)
+ {
+ //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ if (!ta)
+ {
+ //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
+ return matchArgNoMatch();
+ }
+ //printf("ta is %s\n", ta.toChars());
+
+ if (ttp.specType)
+ {
+ if (!ta || ta == TemplateTypeParameter.tdummy)
+ return matchArgNoMatch();
+
+ //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars());
+ MATCH m2 = deduceType(ta, sc, ttp.specType, *parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ {
+ //printf("\tfailed deduceType\n");
+ return matchArgNoMatch();
+ }
+
+ if (m2 < m)
+ m = m2;
+ if (dedtypes[i])
+ {
+ Type t = cast(Type)dedtypes[i];
+
+ if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357
+ return matchArgNoMatch();
+
+ /* This is a self-dependent parameter. For example:
+ * template X(T : T*) {}
+ * template X(T : S!T, alias S) {}
+ */
+ //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
+ ta = t;
+ }
+ }
+ else
+ {
+ if (dedtypes[i])
+ {
+ // Must match already deduced type
+ Type t = cast(Type)dedtypes[i];
+
+ if (!t.equals(ta))
+ {
+ //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ // So that matches with specializations are better
+ m = MATCH.convert;
+ }
+ }
+ dedtypes[i] = ta;
+
+ if (psparam)
+ *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta);
+ //printf("\tm = %d\n", m);
+ return ttp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgValue(TemplateValueParameter tvp)
+ {
+ //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars());
+ MATCH m = MATCH.exact;
+
+ Expression ei = isExpression(oarg);
+ Type vt;
+
+ if (!ei && oarg)
+ {
+ Dsymbol si = isDsymbol(oarg);
+ FuncDeclaration f = si ? si.isFuncDeclaration() : null;
+ if (!f || !f.fbody || f.needThis())
+ return matchArgNoMatch();
+
+ ei = new VarExp(tvp.loc, f);
+ ei = ei.expressionSemantic(sc);
+
+ /* If a function is really property-like, and then
+ * it's CTFEable, ei will be a literal expression.
+ */
+ const olderrors = global.startGagging();
+ ei = resolveProperties(sc, ei);
+ ei = ei.ctfeInterpret();
+ if (global.endGagging(olderrors) || ei.op == EXP.error)
+ return matchArgNoMatch();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14520
+ * A property-like function can match to both
+ * TemplateAlias and ValueParameter. But for template overloads,
+ * it should always prefer alias parameter to be consistent
+ * template match result.
+ *
+ * template X(alias f) { enum X = 1; }
+ * template X(int val) { enum X = 2; }
+ * int f1() { return 0; } // CTFEable
+ * int f2(); // body-less function is not CTFEable
+ * enum x1 = X!f1; // should be 1
+ * enum x2 = X!f2; // should be 1
+ *
+ * e.g. The x1 value must be same even if the f1 definition will be moved
+ * into di while stripping body code.
+ */
+ m = MATCH.convert;
+ }
+
+ if (ei && ei.op == EXP.variable)
+ {
+ // Resolve const variables that we had skipped earlier
+ ei = ei.ctfeInterpret();
+ }
+
+ //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty);
+ vt = tvp.valType.typeSemantic(tvp.loc, sc);
+ //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars());
+ //printf("vt = %s\n", vt.toChars());
+
+ if (ei.type)
+ {
+ MATCH m2 = ei.implicitConvTo(vt);
+ //printf("m: %d\n", m);
+ if (m2 < m)
+ m = m2;
+ if (m == MATCH.nomatch)
+ return matchArgNoMatch();
+ ei = ei.implicitCastTo(sc, vt);
+ ei = ei.ctfeInterpret();
+ }
+
+ if (tvp.specValue)
+ {
+ if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies &&
+ TemplateValueParameter.edummies[cast(void*)ei.type] == ei))
+ return matchArgNoMatch();
+
+ Expression e = tvp.specValue;
+
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.implicitCastTo(sc, vt);
+ e = e.ctfeInterpret();
+
+ ei = ei.syntaxCopy();
+ sc = sc.startCTFE();
+ ei = ei.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ ei = ei.implicitCastTo(sc, vt);
+ ei = ei.ctfeInterpret();
+ //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars());
+ //printf("\te : %s, %s\n", e.toChars(), e.type.toChars());
+ if (!ei.equals(e))
+ return matchArgNoMatch();
+ }
+ else
+ {
+ if (dedtypes[i])
+ {
+ // Must match already deduced value
+ Expression e = cast(Expression)dedtypes[i];
+ if (!ei || !ei.equals(e))
+ return matchArgNoMatch();
+ }
+ }
+ dedtypes[i] = ei;
+
+ if (psparam)
+ {
+ Initializer _init = new ExpInitializer(tvp.loc, ei);
+ Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init);
+ sparam.storage_class = STC.manifest;
+ *psparam = sparam;
+ }
+ return tvp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgAlias(TemplateAliasParameter tap)
+ {
+ //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
+ Expression ea = isExpression(oarg);
+ if (ea)
+ {
+ if (auto te = ea.isThisExp())
+ sa = te.var;
+ else if (auto se = ea.isSuperExp())
+ sa = se.var;
+ else if (auto se = ea.isScopeExp())
+ sa = se.sds;
+ }
+ if (sa)
+ {
+ if ((cast(Dsymbol)sa).isAggregateDeclaration())
+ m = MATCH.convert;
+
+ /* specType means the alias must be a declaration with a type
+ * that matches specType.
+ */
+ if (tap.specType)
+ {
+ tap.specType = typeSemantic(tap.specType, tap.loc, sc);
+ Declaration d = (cast(Dsymbol)sa).isDeclaration();
+ if (!d)
+ return matchArgNoMatch();
+ if (!d.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ sa = oarg;
+ if (ea)
+ {
+ if (tap.specType)
+ {
+ if (!ea.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else if (ta && ta.ty == Tinstance && !tap.specAlias)
+ {
+ /* Specialized parameter should be preferred
+ * match to the template type parameter.
+ * template X(alias a) {} // a == this
+ * template X(alias a : B!A, alias B, A...) {} // B!A => ta
+ */
+ }
+ else if (sa && sa == TemplateTypeParameter.tdummy)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=2025
+ * Aggregate Types should preferentially
+ * match to the template type parameter.
+ * template X(alias a) {} // a == this
+ * template X(T) {} // T => sa
+ */
+ }
+ else if (ta && ta.ty != Tident)
+ {
+ /* Match any type that's not a TypeIdentifier to alias parameters,
+ * but prefer type parameter.
+ * template X(alias a) { } // a == ta
+ *
+ * TypeIdentifiers are excluded because they might be not yet resolved aliases.
+ */
+ m = MATCH.convert;
+ }
+ else
+ return matchArgNoMatch();
+ }
+
+ if (tap.specAlias)
+ {
+ if (sa == TemplateAliasParameter.sdummy)
+ return matchArgNoMatch();
+ // check specialization if template arg is a symbol
+ Dsymbol sx = isDsymbol(sa);
+ if (sa != tap.specAlias && sx)
+ {
+ Type talias = isType(tap.specAlias);
+ if (!talias)
+ return matchArgNoMatch();
+
+ TemplateInstance ti = sx.isTemplateInstance();
+ if (!ti && sx.parent)
+ {
+ ti = sx.parent.isTemplateInstance();
+ if (ti && ti.name != sx.ident)
+ return matchArgNoMatch();
+ }
+ if (!ti)
+ return matchArgNoMatch();
+
+ Type t = new TypeInstance(Loc.initial, ti);
+ MATCH m2 = deduceType(t, sc, talias, *parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ // check specialization if template arg is a type
+ else if (ta)
+ {
+ if (Type tspec = isType(tap.specAlias))
+ {
+ MATCH m2 = ta.implicitConvTo(tspec);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ else
+ {
+ error(tap.loc, "template parameter specialization for a type must be a type and not `%s`",
+ tap.specAlias.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ }
+ else if (dedtypes[i])
+ {
+ // Must match already deduced symbol
+ RootObject si = dedtypes[i];
+ if (!sa || si != sa)
+ return matchArgNoMatch();
+ }
+ dedtypes[i] = sa;
+
+ if (psparam)
+ {
+ if (Dsymbol s = isDsymbol(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, s);
+ }
+ else if (Type t = isType(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, t);
+ }
+ else
+ {
+ assert(ea);
+
+ // Declare manifest constant
+ Initializer _init = new ExpInitializer(tap.loc, ea);
+ auto v = new VarDeclaration(tap.loc, null, tap.ident, _init);
+ v.storage_class = STC.manifest;
+ v.dsymbolSemantic(sc);
+ *psparam = v;
+ }
+ }
+ return tap.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars());
+ Tuple ovar = isTuple(oarg);
+ if (!ovar)
+ return MATCH.nomatch;
+ if (dedtypes[i])
+ {
+ Tuple tup = isTuple(dedtypes[i]);
+ if (!tup)
+ return MATCH.nomatch;
+ if (!match(tup, ovar))
+ return MATCH.nomatch;
+ }
+ dedtypes[i] = ovar;
+
+ if (psparam)
+ *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects);
+ return ttp.dependent ? MATCH.exact : MATCH.convert;
+ }
+
+ if (auto ttp = tp.isTemplateTypeParameter())
+ return matchArgType(ttp);
+ if (auto tvp = tp.isTemplateValueParameter())
+ return matchArgValue(tvp);
+ if (auto tap = tp.isTemplateAliasParameter())
+ return matchArgAlias(tap);
+ if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+ assert(0);
+}
+
+/**********************************************
+ * Confirm s is a valid template, then store it.
+ * Input:
+ * sc
+ * s candidate symbol of template. It may be:
+ * TemplateDeclaration
+ * FuncDeclaration with findTemplateDeclRoot() != NULL
+ * OverloadSet which contains candidates
+ * Returns:
+ * true if updating succeeds.
+ */
+bool updateTempDecl(TemplateInstance ti, Scope* sc, Dsymbol s)
+{
+ if (!s)
+ return ti.tempdecl !is null;
+
+ Identifier id = ti.name;
+ s = s.toAlias();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (OverloadSet os = s.isOverloadSet())
+ {
+ s = null;
+ foreach (s2; os.a)
+ {
+ if (FuncDeclaration f = s2.isFuncDeclaration())
+ s2 = f.findTemplateDeclRoot();
+ else
+ s2 = s2.isTemplateDeclaration();
+ if (s2)
+ {
+ if (s)
+ {
+ ti.tempdecl = os;
+ return true;
+ }
+ s = s2;
+ }
+ }
+ if (!s)
+ {
+ .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars, id.toChars());
+ return false;
+ }
+ }
+
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ ti.tempdecl = od; // TODO: more strict check
+ return true;
+ }
+
+ /* It should be a TemplateDeclaration, not some other symbol
+ */
+ if (FuncDeclaration f = s.isFuncDeclaration())
+ ti.tempdecl = f.findTemplateDeclRoot();
+ else
+ ti.tempdecl = s.isTemplateDeclaration();
+
+ // We're done
+ if (ti.tempdecl)
+ return true;
+
+ // Error already issued, just return `false`
+ if (!s.parent && global.errors)
+ return false;
+
+ if (!s.parent && s.getType())
+ {
+ Dsymbol s2 = s.getType().toDsymbol(sc);
+ if (!s2)
+ {
+ .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), s.getType.kind());
+ return false;
+ }
+ // because s can be the alias created for a TemplateParameter
+ const AliasDeclaration ad = s.isAliasDeclaration();
+ version (none)
+ {
+ if (ad && ad.isAliasedTemplateParameter())
+ printf("`%s` is an alias created from a template parameter\n", s.toChars());
+ }
+ if (!ad || !ad.isAliasedTemplateParameter())
+ s = s2;
+ }
+
+ TemplateInstance ti2 = s.parent ? s.parent.isTemplateInstance() : null;
+
+ /* This avoids the VarDeclaration.toAlias() which runs semantic() too soon
+ */
+ static bool matchId(TemplateInstance _ti, Identifier id)
+ {
+ if (_ti.aliasdecl && _ti.aliasdecl.isVarDeclaration())
+ return _ti.aliasdecl.isVarDeclaration().ident == id;
+ return _ti.toAlias().ident == id;
+ }
+
+ if (ti2 && (ti2.name == s.ident || matchId(ti2, s.ident)) && ti2.tempdecl)
+ {
+ /* This is so that one can refer to the enclosing
+ * template, even if it has the same name as a member
+ * of the template, if it has a !(arguments)
+ */
+ TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration();
+ assert(td);
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ ti.tempdecl = td;
+ return true;
+ }
+ else
+ {
+ .error(ti.loc, "%s `%s` `%s` is not a template declaration, it is a %s",
+ ti.kind, ti.toPrettyChars, id.toChars(), s.kind());
+ return false;
+ }
+}
/***************************************
* Given that ti is an instance of this TemplateDeclaration,
return m;
}
-/****************************
- * Check to see if constraint is satisfied.
+/**
+ Returns: true if the instances' innards are discardable.
+
+ The idea of this function is to see if the template instantiation
+ can be 100% replaced with its eponymous member. All other members
+ can be discarded, even in the compiler to free memory (for example,
+ the template could be expanded in a region allocator, deemed trivial,
+ the end result copied back out independently and the entire region freed),
+ and can be elided entirely from the binary.
+
+ The current implementation affects code that generally looks like:
+
+ ---
+ template foo(args...) {
+ some_basic_type_or_string helper() { .... }
+ enum foo = helper();
+ }
+ ---
+
+ since it was the easiest starting point of implementation but it can and
+ should be expanded more later.
+*/
+bool isDiscardable(TemplateInstance ti)
+{
+ if (ti.aliasdecl is null)
+ return false;
+
+ auto v = ti.aliasdecl.isVarDeclaration();
+ if (v is null)
+ return false;
+
+ if (!(v.storage_class & STC.manifest))
+ return false;
+
+ // Currently only doing basic types here because it is the easiest proof-of-concept
+ // implementation with minimal risk of side effects, but it could likely be
+ // expanded to any type that already exists outside this particular instance.
+ if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null)))
+ return false;
+
+ // Static ctors and dtors, even in an eponymous enum template, are still run,
+ // so if any of them are in here, we'd better not assume it is trivial lest
+ // we break useful code
+ foreach(member; *ti.members)
+ {
+ if(member.hasStaticCtorOrDtor())
+ return false;
+ if(member.isStaticDtorDeclaration())
+ return false;
+ if(member.isStaticCtorDeclaration())
+ return false;
+ }
+
+ // but if it passes through this gauntlet... it should be fine. D code will
+ // see only the eponymous member, outside stuff can never access it, even through
+ // reflection; the outside world ought to be none the wiser. Even dmd should be
+ // able to simply free the memory of everything except the final result.
+
+ return true;
+}
+
+
+/***********************************************
+ * Returns true if this is not instantiated in non-root module, and
+ * is a part of non-speculative instantiatiation.
+ *
+ * Note: minst does not stabilize until semantic analysis is completed,
+ * so don't call this function during semantic analysis to return precise result.
*/
-bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+bool needsCodegen(TemplateInstance ti)
{
- /* Detect recursive attempts to instantiate this template declaration,
- * https://issues.dlang.org/show_bug.cgi?id=4072
- * void foo(T)(T x) if (is(typeof(foo(x)))) { }
- * static assert(!is(typeof(foo(7))));
- * Recursive attempts are regarded as a constraint failure.
- */
- /* There's a chicken-and-egg problem here. We don't know yet if this template
- * instantiation will be a local one (enclosing is set), and we won't know until
- * after selecting the correct template. Thus, function we're nesting inside
- * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel().
- * Workaround the problem by setting a flag to relax the checking on frame errors.
- */
+ //printf("needsCodegen() %s\n", toChars());
+
+ // minst is finalized after the 1st invocation.
+ // tnext is only needed for the 1st invocation and
+ // cleared for further invocations.
+ TemplateInstance tnext = ti.tnext;
+ TemplateInstance tinst = ti.tinst;
+ ti.tnext = null;
+
+ // Don't do codegen if the instance has errors,
+ // is a dummy instance (see evaluateConstraint),
+ // or is determined to be discardable.
+ if (ti.errors || ti.inst is null || ti.inst.isDiscardable())
+ {
+ ti.minst = null; // mark as speculative
+ return false;
+ }
- for (TemplatePrevious* p = td.previous; p; p = p.prev)
+ // This should only be called on the primary instantiation.
+ assert(ti is ti.inst);
+
+ /* Hack for suppressing linking errors against template instances
+ * of `_d_arrayliteralTX` (e.g. `_d_arrayliteralTX!(int[])`).
+ *
+ * This happens, for example, when a lib module is compiled with `preview=dip1000` and
+ * the array literal is placed on stack instead of using the lowering. In this case,
+ * if the root module is compiled without `preview=dip1000`, the compiler will consider
+ * the template already instantiated within the lib module, and thus skip the codegen for it
+ * in the root module object file, thinking that the linker will find the instance in the lib module.
+ *
+ * To bypass this edge case, we always do codegen for `_d_arrayliteralTX` template instances,
+ * even if an instance already exists in non-root module.
+ */
+ if (ti.inst && ti.inst.name == Id._d_arrayliteralTX)
{
- if (!arrayObjectMatch(*p.dedargs, *dedargs))
- continue;
- //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
- /* It must be a subscope of p.sc, other scope chains are not recursive
+ return true;
+ }
+
+ if (global.params.allInst)
+ {
+ // Do codegen if there is an instantiation from a root module, to maximize link-ability.
+ static ThreeState needsCodegenAllInst(TemplateInstance tithis, TemplateInstance tinst)
+ {
+ // Do codegen if `this` is instantiated from a root module.
+ if (tithis.minst && tithis.minst.isRoot())
+ return ThreeState.yes;
+
+ // Do codegen if the ancestor needs it.
+ if (tinst && tinst.inst && tinst.inst.needsCodegen())
+ {
+ tithis.minst = tinst.inst.minst; // cache result
+ assert(tithis.minst);
+ assert(tithis.minst.isRoot());
+ return ThreeState.yes;
+ }
+ return ThreeState.none;
+ }
+
+ if (const needsCodegen = needsCodegenAllInst(ti, tinst))
+ return needsCodegen == ThreeState.yes ? true : false;
+
+ // Do codegen if a sibling needs it.
+ for (; tnext; tnext = tnext.tnext)
+ {
+ const needsCodegen = needsCodegenAllInst(tnext, tnext.tinst);
+ if (needsCodegen == ThreeState.yes)
+ {
+ ti.minst = tnext.minst; // cache result
+ assert(ti.minst);
+ assert(ti.minst.isRoot());
+ return true;
+ }
+ else if (!ti.minst && tnext.minst)
+ {
+ ti.minst = tnext.minst; // cache result from non-speculative sibling
+ // continue searching
+ }
+ else if (needsCodegen != ThreeState.none)
+ break;
+ }
+
+ // Elide codegen because there's no instantiation from any root modules.
+ return false;
+ }
+
+ // Prefer instantiations from non-root modules, to minimize object code size.
+
+ /* If a TemplateInstance is ever instantiated from a non-root module,
+ * we do not have to generate code for it,
+ * because it will be generated when the non-root module is compiled.
+ *
+ * But, if the non-root 'minst' imports any root modules, it might still need codegen.
+ *
+ * The problem is if A imports B, and B imports A, and both A
+ * and B instantiate the same template, does the compilation of A
+ * or the compilation of B do the actual instantiation?
+ *
+ * See https://issues.dlang.org/show_bug.cgi?id=2500.
+ *
+ * => Elide codegen if there is at least one instantiation from a non-root module
+ * which doesn't import any root modules.
+ */
+ static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst)
+ {
+ // If the ancestor isn't speculative,
+ // 1. do codegen if the ancestor needs it
+ // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree)
+ if (tinst && tinst.inst)
+ {
+ tinst = tinst.inst;
+ const needsCodegen = tinst.needsCodegen(); // sets tinst.minst
+ if (tinst.minst) // not speculative
+ {
+ tithis.minst = tinst.minst; // cache result
+ return needsCodegen ? ThreeState.yes : ThreeState.no;
+ }
+ }
+
+ // Elide codegen if `this` doesn't need it.
+ if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports())
+ return ThreeState.no;
+
+ return ThreeState.none;
+ }
+
+ if (const needsCodegen = needsCodegenRootOnly(ti, tinst))
+ return needsCodegen == ThreeState.yes ? true : false;
+
+ // Elide codegen if a (non-speculative) sibling doesn't need it.
+ for (; tnext; tnext = tnext.tnext)
+ {
+ const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst
+ if (tnext.minst) // not speculative
+ {
+ if (needsCodegen == ThreeState.no)
+ {
+ ti.minst = tnext.minst; // cache result
+ assert(!ti.minst.isRoot() && !ti.minst.rootImports());
+ return false;
+ }
+ else if (!ti.minst)
+ {
+ ti.minst = tnext.minst; // cache result from non-speculative sibling
+ // continue searching
+ }
+ else if (needsCodegen != ThreeState.none)
+ break;
+ }
+ }
+
+ // Unless `this` is still speculative (=> all further siblings speculative too),
+ // do codegen because we found no guaranteed-codegen'd non-root instantiation.
+ return ti.minst !is null;
+}
+
+/****************************
+ * Check to see if constraint is satisfied.
+ */
+bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+{
+ /* Detect recursive attempts to instantiate this template declaration,
+ * https://issues.dlang.org/show_bug.cgi?id=4072
+ * void foo(T)(T x) if (is(typeof(foo(x)))) { }
+ * static assert(!is(typeof(foo(7))));
+ * Recursive attempts are regarded as a constraint failure.
+ */
+ /* There's a chicken-and-egg problem here. We don't know yet if this template
+ * instantiation will be a local one (enclosing is set), and we won't know until
+ * after selecting the correct template. Thus, function we're nesting inside
+ * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel().
+ * Workaround the problem by setting a flag to relax the checking on frame errors.
+ */
+
+ for (TemplatePrevious* p = td.previous; p; p = p.prev)
+ {
+ if (!arrayObjectMatch(*p.dedargs, *dedargs))
+ continue;
+ //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
+ /* It must be a subscope of p.sc, other scope chains are not recursive
* instantiations.
* the chain of enclosing scopes is broken by paramscope (its enclosing
* scope is _scope, but paramscope.callsc is the instantiating scope). So
return result;
}
+/****************************
+ * Destructively get the error message from the last constraint evaluation
+ * Params:
+ * td = TemplateDeclaration
+ * tip = tip to show after printing all overloads
+ */
+const(char)* getConstraintEvalError(TemplateDeclaration td, ref const(char)* tip)
+{
+ import dmd.staticcond;
+
+ // there will be a full tree view in verbose mode, and more compact list in the usual
+ const full = global.params.v.verbose;
+ uint count;
+ const msg = visualizeStaticCondition(td.constraint, td.lastConstraint, td.lastConstraintNegs[], full, count);
+ scope (exit)
+ {
+ td.lastConstraint = null;
+ td.lastConstraintTiargs = null;
+ td.lastConstraintNegs.setDim(0);
+ }
+ if (!msg)
+ return null;
+
+ OutBuffer buf;
+
+ assert(td.parameters && td.lastConstraintTiargs);
+ if (td.parameters.length > 0)
+ {
+ formatParamsWithTiargs(*td.parameters, *td.lastConstraintTiargs, td.isVariadic() !is null, buf);
+ buf.writenl();
+ }
+ if (!full)
+ {
+ // choosing singular/plural
+ const s = (count == 1) ?
+ " must satisfy the following constraint:" :
+ " must satisfy one of the following constraints:";
+ buf.writestring(s);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ }
+ else
+ {
+ buf.writestring(" whose parameters have the following constraints:");
+ buf.writenl();
+ const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
+ buf.writestring(sep);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ buf.writestring(sep);
+ tip = "not satisfied constraints are marked with `>`";
+ }
+ return buf.extractChars();
+}
+
+/**********************************
+ * Find the TemplateDeclaration that matches this TemplateInstance best.
+ *
+ * Params:
+ * ti = TemplateInstance
+ * sc = the scope this TemplateInstance resides in
+ * argumentList = function arguments in case of a template function
+ *
+ * Returns:
+ * `true` if a match was found, `false` otherwise
+ */
+bool findBestMatch(TemplateInstance ti, Scope* sc, ArgumentList argumentList)
+{
+ if (ti.havetempdecl)
+ {
+ TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+ assert(tempdecl._scope);
+ // Deduce tdtypes
+ ti.tdtypes.setDim(tempdecl.parameters.length);
+ if (!matchWithInstance(sc, tempdecl, ti, ti.tdtypes, argumentList, 2))
+ {
+ .error(ti.loc, "%s `%s` incompatible arguments for template instantiation",
+ ti.kind, ti.toPrettyChars);
+ return false;
+ }
+ // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
+ return true;
+ }
+
+ static if (LOG)
+ {
+ printf("TemplateInstance.findBestMatch()\n");
+ }
+
+ const errs = global.errors;
+ TemplateDeclaration td_last = null;
+ Objects dedtypes;
+
+ /* Since there can be multiple TemplateDeclaration's with the same
+ * name, look for the best match.
+ */
+ auto tovers = ti.tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+ {
+ TemplateDeclaration td_best;
+ TemplateDeclaration td_ambig;
+ MATCH m_best = MATCH.nomatch;
+
+ Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
+ overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ if (td == td_best) // skip duplicates
+ return 0;
+
+ //printf("td = %s\n", td.toPrettyChars());
+ // If more arguments than parameters,
+ // then this is no match.
+ if (td.parameters.length < ti.tiargs.length)
+ {
+ if (!td.isVariadic())
+ return 0;
+ }
+
+ dedtypes.setDim(td.parameters.length);
+ dedtypes.zero();
+ assert(td.semanticRun != PASS.initial);
+
+ MATCH m = matchWithInstance(sc, td, ti, dedtypes, argumentList, 0);
+ //printf("matchWithInstance = %d\n", m);
+ if (m == MATCH.nomatch) // no match at all
+ return 0;
+ if (m < m_best) goto Ltd_best;
+ if (m > m_best) goto Ltd;
+
+ // Disambiguate by picking the most specialized TemplateDeclaration
+ {
+ MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList);
+ MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList);
+ //printf("c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto Ltd;
+ if (c1 < c2) goto Ltd_best;
+ }
+
+ td_ambig = td;
+ return 0;
+
+ Ltd_best:
+ // td_best is the best match so far
+ td_ambig = null;
+ return 0;
+
+ Ltd:
+ // td is the new best match
+ td_ambig = null;
+ td_best = td;
+ m_best = m;
+ ti.tdtypes.setDim(dedtypes.length);
+ memcpy(ti.tdtypes.tdata(), dedtypes.tdata(), ti.tdtypes.length * (void*).sizeof);
+ return 0;
+ });
+
+ if (td_ambig)
+ {
+ .error(ti.loc, "%s `%s.%s` matches more than one template declaration:",
+ td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars());
+ .errorSupplemental(td_best.loc, "`%s`\nand:", td_best.toChars());
+ .errorSupplemental(td_ambig.loc, "`%s`", td_ambig.toChars());
+ return false;
+ }
+ if (td_best)
+ {
+ if (!td_last)
+ td_last = td_best;
+ else if (td_last != td_best)
+ {
+ ScopeDsymbol.multiplyDefined(ti.loc, td_last, td_best);
+ return false;
+ }
+ }
+ }
+
+ if (td_last)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=7469
+ * Normalize tiargs by using corresponding deduced
+ * template value parameters and tuples for the correct mangling.
+ *
+ * By doing this before hasNestedArgs, CTFEable local variable will be
+ * accepted as a value parameter. For example:
+ *
+ * void foo() {
+ * struct S(int n) {} // non-global template
+ * const int num = 1; // CTFEable local variable
+ * S!num s; // S!1 is instantiated, not S!num
+ * }
+ */
+ size_t dim = td_last.parameters.length - (td_last.isVariadic() ? 1 : 0);
+ for (size_t i = 0; i < dim; i++)
+ {
+ if (ti.tiargs.length <= i)
+ ti.tiargs.push(ti.tdtypes[i]);
+ assert(i < ti.tiargs.length);
+
+ auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
+ if (!tvp)
+ continue;
+ assert(ti.tdtypes[i]);
+ // tdtypes[i] is already normalized to the required type in matchArg
+
+ (*ti.tiargs)[i] = ti.tdtypes[i];
+ }
+ if (td_last.isVariadic() && ti.tiargs.length == dim && ti.tdtypes[dim])
+ {
+ Tuple va = isTuple(ti.tdtypes[dim]);
+ assert(va);
+ ti.tiargs.pushSlice(va.objects[]);
+ }
+ }
+ else if (ti.errors && ti.inst)
+ {
+ // instantiation was failed with error reporting
+ assert(global.errors);
+ return false;
+ }
+ else
+ {
+ auto tdecl = ti.tempdecl.isTemplateDeclaration();
+
+ if (errs != global.errors)
+ errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars());
+ else if (tdecl && !tdecl.overnext)
+ {
+ // Only one template, so we can give better error message
+ const(char)* msg = "does not match template declaration";
+ const(char)* tip;
+ OutBuffer buf;
+ HdrGenState hgs;
+ hgs.skipConstraints = true;
+ toCharsMaybeConstraints(tdecl, buf, hgs);
+ const tmsg = buf.peekChars();
+ const cmsg = tdecl.getConstraintEvalError(tip);
+ if (cmsg)
+ {
+ .error(ti.loc, "%s `%s` %s `%s`\n%s", ti.kind, ti.toPrettyChars, msg, tmsg, cmsg);
+ if (tip)
+ .tip(tip);
+ }
+ else
+ {
+ .error(ti.loc, "%s `%s` %s `%s`", ti.kind, ti.toPrettyChars, msg, tmsg);
+
+ if (tdecl.parameters.length == ti.tiargs.length)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7352
+ // print additional information, e.g. `foo` is not a type
+ foreach (i, param; *tdecl.parameters)
+ {
+ MATCH match = param.matchArg(ti.loc, sc, ti.tiargs, i, tdecl.parameters, dedtypes, null);
+ auto arg = (*ti.tiargs)[i];
+ auto sym = arg.isDsymbol;
+ auto exp = arg.isExpression;
+
+ if (exp)
+ exp = exp.optimize(WANTvalue);
+
+ if (match == MATCH.nomatch &&
+ ((sym && sym.isFuncDeclaration) ||
+ (exp && exp.isVarExp)))
+ {
+ if (param.isTemplateTypeParameter)
+ errorSupplemental(ti.loc, "`%s` is not a type", arg.toChars);
+ else if (auto tvp = param.isTemplateValueParameter)
+ errorSupplemental(ti.loc, "`%s` is not of a value of type `%s`",
+ arg.toChars, tvp.valType.toChars);
+
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ .error(ti.loc, "%s `%s` does not match any template declaration", ti.kind(), ti.toPrettyChars());
+ bool found;
+ overloadApply(ti.tempdecl, (s){
+ if (!found)
+ errorSupplemental(ti.loc, "Candidates are:");
+ found = true;
+ errorSupplemental(s.loc, "%s", s.toChars());
+ return 0;
+ });
+ }
+ return false;
+ }
+
+ /* The best match is td_last
+ */
+ ti.tempdecl = td_last;
+
+ static if (LOG)
+ {
+ printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+ }
+ return (errs == global.errors);
+}
+
/*******************************************
* Append to buf a textual representation of template parameters with their arguments.
* Params:
* variadic = if it's a variadic argument list
* buf = where the text output goes
*/
-void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf)
+private void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf)
{
buf.writestring(" with `");
return MATCH.nomatch;
}
+RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
+{
+ if (tp.isTemplateTupleParameter())
+ return null;
+ if (auto tpp = tp.isTemplateTypeParameter())
+ {
+ Type t = tpp.defaultType;
+ if (t)
+ {
+ t = t.syntaxCopy();
+ t = t.typeSemantic(tpp.loc, sc); // use the parameter loc
+ }
+ return t;
+ }
+ if (auto tap = tp.isTemplateAliasParameter())
+ {
+ RootObject da = tap.defaultAlias;
+ if (auto ta = isType(tap.defaultAlias))
+ {
+ switch (ta.ty)
+ {
+ // If the default arg is a template, instantiate for each type
+ case Tinstance:
+ // same if the default arg is a mixin, traits, typeof
+ // since the content might rely on a previous parameter
+ // (https://issues.dlang.org/show_bug.cgi?id=23686)
+ case Tmixin, Ttypeof, Ttraits :
+ da = ta.syntaxCopy();
+ break;
+ default:
+ }
+ }
+
+ RootObject o = aliasParameterSemantic(tap.loc, sc, da, null); // use the parameter loc
+ return o;
+ }
+ if (auto tvp = tp.isTemplateValueParameter())
+ {
+ Expression e = tvp.defaultValue;
+ if (!e)
+ return null;
+
+ e = e.syntaxCopy();
+ Scope* sc2 = sc.push();
+ sc2.inDefaultArg = true;
+ e = e.expressionSemantic(sc2);
+ sc2.pop();
+ if (e is null)
+ return null;
+ if (auto te = e.isTemplateExp())
+ {
+ assert(sc && sc.tinst);
+ if (te.td == sc.tinst.tempdecl)
+ {
+ // defaultValue is a reference to its template declaration
+ // i.e: `template T(int arg = T)`
+ // Raise error now before calling resolveProperties otherwise we'll
+ // start looping on the expansion of the template instance.
+ auto td = sc.tinst.tempdecl;
+ .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars);
+ return ErrorExp.get();
+ }
+ }
+ if ((e = resolveProperties(sc, e)) is null)
+ return null;
+ e = e.resolveLoc(instLoc, sc); // use the instantiated loc
+ e = e.optimize(WANTvalue);
+
+ return e;
+ }
+ assert(0);
+}
+
/*************************************************
* Match function arguments against a specific template function.
*
return o;
}
-/*************************************************
- * Given function arguments, figure out which template function
- * to expand, and return matching result.
- * Params:
- * m = matching result
- * dstart = the root of overloaded function templates
- * loc = instantiation location
- * sc = instantiation scope
- * tiargs = initial list of template arguments
- * tthis = if !NULL, the 'this' pointer argument
- * argumentList= arguments to function
- * errorHelper = delegate to send error message to if not null
+/**********************************
+ * Run semantic of tiargs as arguments of template.
+ * Input:
+ * loc
+ * sc
+ * 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.
*/
-void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
- Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
+bool TemplateInstance_semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null)
{
- version (none)
+ // Run semantic on each argument, place results in tiargs[]
+ //printf("+TemplateInstance.semanticTiargs()\n");
+ if (!tiargs)
+ return true;
+ bool err = false;
+
+ // The arguments are not treated as part of a default argument,
+ // because they are evaluated at compile time.
+ const inCondition = sc.condition;
+ sc = sc.push();
+ sc.inDefaultArg = false;
+
+ // https://issues.dlang.org/show_bug.cgi?id=24699
+ sc.condition = inCondition;
+
+ for (size_t j = 0; j < tiargs.length; j++)
{
- printf("functionResolve() dstart: %s (", dstart.toChars());
- for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
- {
- if (i) printf(", ");
- RootObject arg = (*tiargs)[i];
- printf("%s", arg.toChars());
- }
- printf(")(");
- for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
- {
- if (i) printf(", ");
- Expression arg = (*argumentList.arguments)[i];
- printf("%s %s", arg.type.toChars(), arg.toChars());
- //printf("\tty = %d\n", arg.type.ty);
- }
- printf(")\n");
- //printf("stc = %llx\n", dstart._scope.stc);
- //printf("match:t/f = %d/%d\n", ta_last, m.last);
- }
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+
+ //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
+ if (ta)
+ {
+ //printf("type %s\n", ta.toChars());
+
+ // It might really be an Expression or an Alias
+ ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0);
+ if (ea)
+ goto Lexpr;
+ if (sa)
+ goto Ldsym;
+ if (ta is null)
+ {
+ assert(global.errors);
+ ta = Type.terror;
+ }
+
+ Ltype:
+ if (TypeTuple tt = ta.isTypeTuple())
+ {
+ // Expand tuple
+ size_t dim = tt.arguments.length;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, arg; *tt.arguments)
+ {
+ if (flags & 2 && (arg.storageClass & STC.parameter))
+ tiargs.insert(j + i, arg);
+ else
+ tiargs.insert(j + i, arg.type);
+ }
+ }
+ j--;
+ continue;
+ }
+ if (ta.ty == Terror)
+ {
+ err = true;
+ continue;
+ }
+ (*tiargs)[j] = ta.merge2();
+ }
+ else if (ea)
+ {
+ Lexpr:
+ //printf("+[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
+ if (flags & 1) // only used by __traits
+ {
+ ea = ea.expressionSemantic(sc);
+
+ // must not interpret the args, excepting template parameters
+ if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter))
+ {
+ ea = ea.optimize(WANTvalue);
+ }
+ }
+ else
+ {
+ sc = sc.startCTFE();
+ ea = ea.expressionSemantic(sc);
+ sc = sc.endCTFE();
+
+ if (auto varExp = ea.isVarExp())
+ {
+ /* If the parameter is a function that is not called
+ * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
+ * then it is a dsymbol, not the return value of `func()`
+ */
+ Declaration vd = varExp.var;
+ if (auto fd = vd.isFuncDeclaration())
+ {
+ sa = fd;
+ goto Ldsym;
+ }
+ /* Otherwise skip substituting a const var with
+ * its initializer. The problem is the initializer won't
+ * match with an 'alias' parameter. Instead, do the
+ * const substitution in TemplateValueParameter.matchArg().
+ */
+ }
+ else if (definitelyValueParameter(ea))
+ {
+ if (ea.checkValue()) // check void expression
+ ea = ErrorExp.get();
+ const olderrs = global.errors;
+ ea = ea.ctfeInterpret();
+ if (global.errors != olderrs)
+ ea = ErrorExp.get();
+ }
+ }
+ //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
+ if (TupleExp te = ea.isTupleExp())
+ {
+ // Expand tuple
+ size_t dim = te.exps.length;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, exp; *te.exps)
+ tiargs.insert(j + i, exp);
+ }
+ j--;
+ continue;
+ }
+ if (ea.op == EXP.error)
+ {
+ err = true;
+ continue;
+ }
+ (*tiargs)[j] = ea;
+
+ if (ea.op == EXP.type)
+ {
+ ta = ea.type;
+ goto Ltype;
+ }
+ if (ea.op == EXP.scope_)
+ {
+ sa = ea.isScopeExp().sds;
+ goto Ldsym;
+ }
+ if (FuncExp fe = ea.isFuncExp())
+ {
+ /* A function literal, that is passed to template and
+ * already semanticed as function pointer, never requires
+ * outer frame. So convert it to global function is valid.
+ */
+ if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer)
+ {
+ // change to non-nested
+ fe.fd.tok = TOK.function_;
+ fe.fd.vthis = null;
+ }
+ else if (fe.td)
+ {
+ /* If template argument is a template lambda,
+ * get template declaration itself. */
+ //sa = fe.td;
+ //goto Ldsym;
+ }
+ }
+ if (ea.op == EXP.dotVariable && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = ea.isDotVarExp().var;
+ goto Ldsym;
+ }
+ if (auto te = ea.isTemplateExp())
+ {
+ sa = te.td;
+ goto Ldsym;
+ }
+ if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = ea.isDotTemplateExp().td;
+ goto Ldsym;
+ }
+ if (auto de = ea.isDotExp())
+ {
+ if (auto se = de.e2.isScopeExp())
+ {
+ sa = se.sds;
+ goto Ldsym;
+ }
+ }
+ }
+ else if (sa)
+ {
+ Ldsym:
+ //printf("dsym %s %s\n", sa.kind(), sa.toChars());
+ if (sa.errors)
+ {
+ err = true;
+ continue;
+ }
+
+ 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);
+ j--;
+ continue;
+ }
+ if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration())
+ {
+ FuncDeclaration f = fa.toAliasFunc();
+ if (!fa.hasOverloads && f.isUnique())
+ {
+ // Strip FuncAlias only when the aliased function
+ // does not have any overloads.
+ sa = f;
+ }
+ }
+ (*tiargs)[j] = sa;
+
+ TemplateDeclaration td = sa.isTemplateDeclaration();
+ if (td && td.semanticRun == PASS.initial && td.literal)
+ {
+ td.dsymbolSemantic(sc);
+ }
+ FuncDeclaration fd = sa.isFuncDeclaration();
+ if (fd)
+ functionSemantic(fd);
+ }
+ else if (isParameter(o))
+ {
+ }
+ else
+ {
+ assert(0);
+ }
+ //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
+ }
+ sc.pop();
+ version (none)
+ {
+ printf("-TemplateInstance.semanticTiargs()\n");
+ for (size_t j = 0; j < tiargs.length; j++)
+ {
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va);
+ }
+ }
+ return !err;
+}
+
+
+/*************************************************
+ * Given function arguments, figure out which template function
+ * to expand, and return matching result.
+ * Params:
+ * m = matching result
+ * dstart = the root of overloaded function templates
+ * loc = instantiation location
+ * sc = instantiation scope
+ * tiargs = initial list of template arguments
+ * tthis = if !NULL, the 'this' pointer argument
+ * argumentList= arguments to function
+ * errorHelper = delegate to send error message to if not null
+ */
+void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
+ Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
+{
+ version (none)
+ {
+ printf("functionResolve() dstart: %s (", dstart.toChars());
+ for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
+ {
+ if (i) printf(", ");
+ RootObject arg = (*tiargs)[i];
+ printf("%s", arg.toChars());
+ }
+ printf(")(");
+ for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
+ {
+ if (i) printf(", ");
+ Expression arg = (*argumentList.arguments)[i];
+ printf("%s %s", arg.type.toChars(), arg.toChars());
+ //printf("\tty = %d\n", arg.type.ty);
+ }
+ printf(")\n");
+ //printf("stc = %llx\n", dstart._scope.stc);
+ //printf("match:t/f = %d/%d\n", ta_last, m.last);
+ }
// results
int property = 0; // 0: uninitialized
result = null;
}
}
+
+/**
+ * Returns the common type of the 2 types.
+ */
+private Type rawTypeMerge(Type t1, Type t2)
+{
+ if (t1.equals(t2))
+ return t1;
+ if (t1.equivalent(t2))
+ return t1.castMod(MODmerge(t1.mod, t2.mod));
+
+ auto t1b = t1.toBasetype();
+ auto t2b = t2.toBasetype();
+ if (t1b.equals(t2b))
+ return t1b;
+ if (t1b.equivalent(t2b))
+ return t1b.castMod(MODmerge(t1b.mod, t2b.mod));
+
+ auto ty = implicitConvCommonTy(t1b.ty, t2b.ty);
+ if (ty != Terror)
+ return Type.basic[ty];
+
+ return null;
+}
+
+private auto X(T, U)(T m, U n)
+{
+ return (m << 4) | n;
+}
+
+private ubyte deduceWildHelper(Type t, Type* at, Type tparam)
+{
+ if ((tparam.mod & MODFlags.wild) == 0)
+ return 0;
+
+ *at = null;
+
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ {
+ ubyte wm = (t.mod & ~MODFlags.shared_);
+ if (wm == 0)
+ wm = MODFlags.mutable;
+ ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_);
+ *at = t.unqualify(m);
+ return wm;
+ }
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ {
+ *at = t.unqualify(tparam.mod & t.mod);
+ return MODFlags.wild;
+ }
+ default:
+ return 0;
+ }
+}
+
+private MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
+{
+ // 9*9 == 81 cases
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(0, 0):
+ case X(0, MODFlags.const_):
+ case X(0, MODFlags.wild):
+ case X(0, MODFlags.wildconst):
+ case X(0, MODFlags.shared_):
+ case X(0, MODFlags.shared_ | MODFlags.const_):
+ case X(0, MODFlags.shared_ | MODFlags.wild):
+ case X(0, MODFlags.shared_ | MODFlags.wildconst):
+ case X(0, MODFlags.immutable_):
+ // foo(U) T => T
+ // foo(U) const(T) => const(T)
+ // foo(U) inout(T) => inout(T)
+ // foo(U) inout(const(T)) => inout(const(T))
+ // foo(U) shared(T) => shared(T)
+ // foo(U) shared(const(T)) => shared(const(T))
+ // foo(U) shared(inout(T)) => shared(inout(T))
+ // foo(U) shared(inout(const(T))) => shared(inout(const(T)))
+ // foo(U) immutable(T) => immutable(T)
+ {
+ at = t;
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.immutable_):
+ // foo(const(U)) const(T) => T
+ // foo(inout(U)) inout(T) => T
+ // foo(inout(const(U))) inout(const(T)) => T
+ // foo(shared(U)) shared(T) => T
+ // foo(shared(const(U))) shared(const(T)) => T
+ // foo(shared(inout(U))) shared(inout(T)) => T
+ // foo(shared(inout(const(U)))) shared(inout(const(T))) => T
+ // foo(immutable(U)) immutable(T) => T
+ {
+ at = t.mutableOf().unSharedOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(const(U)) shared(const(T)) => shared(T)
+ // foo(inout(U)) shared(inout(T)) => shared(T)
+ // foo(inout(const(U))) shared(inout(const(T))) => shared(T)
+ {
+ at = t.mutableOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, 0):
+ case X(MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_):
+ // foo(const(U)) T => T
+ // foo(const(U)) inout(T) => T
+ // foo(const(U)) inout(const(T)) => T
+ // foo(const(U)) shared(inout(T)) => shared(T)
+ // foo(const(U)) shared(inout(const(T))) => shared(T)
+ // foo(const(U)) immutable(T) => T
+ // foo(shared(const(U))) immutable(T) => T
+ {
+ at = t.mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.const_, MODFlags.shared_):
+ // foo(const(U)) shared(T) => shared(T)
+ {
+ at = t;
+ return MATCH.constant;
+ }
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(shared(U)) shared(const(T)) => const(T)
+ // foo(shared(U)) shared(inout(T)) => inout(T)
+ // foo(shared(U)) shared(inout(const(T))) => inout(const(T))
+ {
+ at = t.unSharedOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_):
+ // foo(shared(const(U))) shared(T) => T
+ {
+ at = t.unSharedOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ // foo(inout(const(U))) immutable(T) => T
+ // foo(shared(const(U))) shared(inout(const(T))) => T
+ // foo(shared(inout(const(U)))) immutable(T) => T
+ // foo(shared(inout(const(U)))) shared(inout(T)) => T
+ {
+ at = t.unSharedOf().mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ // foo(shared(const(U))) shared(inout(T)) => T
+ {
+ at = t.unSharedOf().mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, 0):
+ case X(MODFlags.shared_, MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, 0):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, 0):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, 0):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, 0):
+ case X(MODFlags.immutable_, MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.shared_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(inout(U)) T => nomatch
+ // foo(inout(U)) const(T) => nomatch
+ // foo(inout(U)) inout(const(T)) => nomatch
+ // foo(inout(U)) immutable(T) => nomatch
+ // foo(inout(U)) shared(T) => nomatch
+ // foo(inout(U)) shared(const(T)) => nomatch
+ // foo(inout(U)) shared(inout(const(T))) => nomatch
+ // foo(inout(const(U))) T => nomatch
+ // foo(inout(const(U))) const(T) => nomatch
+ // foo(inout(const(U))) inout(T) => nomatch
+ // foo(inout(const(U))) shared(T) => nomatch
+ // foo(inout(const(U))) shared(const(T)) => nomatch
+ // foo(inout(const(U))) shared(inout(T)) => nomatch
+ // foo(shared(U)) T => nomatch
+ // foo(shared(U)) const(T) => nomatch
+ // foo(shared(U)) inout(T) => nomatch
+ // foo(shared(U)) inout(const(T)) => nomatch
+ // foo(shared(U)) immutable(T) => nomatch
+ // foo(shared(const(U))) T => nomatch
+ // foo(shared(const(U))) const(T) => nomatch
+ // foo(shared(const(U))) inout(T) => nomatch
+ // foo(shared(const(U))) inout(const(T)) => nomatch
+ // foo(shared(inout(U))) T => nomatch
+ // foo(shared(inout(U))) const(T) => nomatch
+ // foo(shared(inout(U))) inout(T) => nomatch
+ // foo(shared(inout(U))) inout(const(T)) => nomatch
+ // foo(shared(inout(U))) immutable(T) => nomatch
+ // foo(shared(inout(U))) shared(T) => nomatch
+ // foo(shared(inout(U))) shared(const(T)) => nomatch
+ // foo(shared(inout(U))) shared(inout(const(T))) => nomatch
+ // foo(shared(inout(const(U)))) T => nomatch
+ // foo(shared(inout(const(U)))) const(T) => nomatch
+ // foo(shared(inout(const(U)))) inout(T) => nomatch
+ // foo(shared(inout(const(U)))) inout(const(T)) => nomatch
+ // foo(shared(inout(const(U)))) shared(T) => nomatch
+ // foo(shared(inout(const(U)))) shared(const(T)) => nomatch
+ // foo(immutable(U)) T => nomatch
+ // foo(immutable(U)) const(T) => nomatch
+ // foo(immutable(U)) inout(T) => nomatch
+ // foo(immutable(U)) inout(const(T)) => nomatch
+ // foo(immutable(U)) shared(T) => nomatch
+ // foo(immutable(U)) shared(const(T)) => nomatch
+ // foo(immutable(U)) shared(inout(T)) => nomatch
+ // foo(immutable(U)) shared(inout(const(T))) => nomatch
+ return MATCH.nomatch;
+
+ default:
+ assert(0);
+ }
+}
+
+__gshared Expression emptyArrayElement = null;
+
+/*
+ * Returns `true` if `t` is a reference type, or an array of reference types.
+ */
+private bool isTopRef(Type t)
+{
+ auto tb = t.baseElemOf();
+ return tb.ty == Tclass ||
+ tb.ty == Taarray ||
+ tb.ty == Tstruct && tb.hasPointers();
+}
+
+/*
+ * Returns a valid `Loc` for semantic routines.
+ * Some paths require a location derived from the first
+ * template parameter when available.
+ */
+private Loc semanticLoc(scope ref TemplateParameters parameters)
+{
+ Loc loc;
+ if (parameters.length)
+ loc = parameters[0].loc;
+ return loc;
+}
+
+private MATCH deduceAliasThis(Type t, Scope* sc, Type tparam,
+ ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
+{
+ if (auto tc = t.isTypeClass())
+ {
+ if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT);
+ auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
+ return m;
+ }
+ }
+ }
+ else if (auto ts = t.isTypeStruct())
+ {
+ if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT);
+ auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
+ return m;
+ }
+ }
+ }
+ return MATCH.nomatch;
+}
+
+private MATCH deduceParentInstance(Scope* sc, Dsymbol sym, TypeInstance tpi,
+ ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
+{
+ if (tpi.idents.length)
+ {
+ RootObject id = tpi.idents[tpi.idents.length - 1];
+ if (id.dyncast() == DYNCAST.identifier && sym.ident.equals(cast(Identifier)id))
+ {
+ Type tparent = sym.parent.getType();
+ if (tparent)
+ {
+ tpi.idents.length--;
+ auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.length++;
+ return m;
+ }
+ }
+ }
+ return MATCH.nomatch;
+}
+
+private MATCH matchAll(TypeDeduced td, Type tt)
+{
+ MATCH match = MATCH.exact;
+ foreach (j, e; td.argexps)
+ {
+ assert(e);
+ if (e == emptyArrayElement)
+ continue;
+
+ Type t = tt.addMod(td.tparams[j].mod).substWildTo(MODFlags.const_);
+
+ MATCH m = e.implicitConvTo(t);
+ if (match > m)
+ match = m;
+ if (match == MATCH.nomatch)
+ break;
+ }
+ return match;
+}
+/****
+ * Given an identifier, figure out which TemplateParameter it is.
+ * Return IDX_NOTFOUND if not found.
+ */
+private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+{
+ for (size_t i = 0; i < parameters.length; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ if (tp.ident.equals(id))
+ return i;
+ }
+ return IDX_NOTFOUND;
+}
+
+private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+{
+ if (TypeIdentifier tident = tparam.isTypeIdentifier())
+ {
+ //printf("\ttident = '%s'\n", tident.toChars());
+ return templateIdentifierLookup(tident.ident, parameters);
+ }
+ return IDX_NOTFOUND;
+}
+/* These form the heart of template argument deduction.
+ * Given 'this' being the type argument to the template instance,
+ * it is matched against the template declaration parameter specialization
+ * 'tparam' to determine the type to be used for the parameter.
+ * Example:
+ * template Foo(T:T*) // template declaration
+ * Foo!(int*) // template instantiation
+ * Input:
+ * this = int*
+ * tparam = T*
+ * parameters = [ T:T* ] // Array of TemplateParameter's
+ * Output:
+ * dedtypes = [ int ] // Array of Expression/Type's
+ */
+MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam,
+ scope ref TemplateParameters parameters, scope ref Objects dedtypes,
+ scope uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+{
+ extern (C++) final class DeduceType : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ MATCH result;
+
+ extern (D) this() @safe
+ {
+ result = MATCH.nomatch;
+ }
+
+ override void visit(Type t)
+ {
+ if (!tparam)
+ goto Lnomatch;
+
+ if (t == tparam)
+ goto Lexact;
+
+ if (tparam.ty == Tident)
+ {
+ // Determine which parameter tparam is
+ size_t i = templateParameterLookup(tparam, ¶meters);
+ if (i == IDX_NOTFOUND)
+ {
+ if (!sc)
+ goto Lnomatch;
+
+ /* Need a loc to go with the semantic routine. */
+ Loc loc = semanticLoc(parameters);
+
+ /* BUG: what if tparam is a template instance, that
+ * has as an argument another Tident?
+ */
+ tparam = tparam.typeSemantic(loc, sc);
+ assert(tparam.ty != Tident);
+ result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
+ return;
+ }
+
+ TemplateParameter tp = parameters[i];
+
+ TypeIdentifier tident = tparam.isTypeIdentifier();
+ if (tident.idents.length > 0)
+ {
+ //printf("matching %s to %s\n", tparam.toChars(), t.toChars());
+ Dsymbol s = t.toDsymbol(sc);
+ for (size_t j = tident.idents.length; j-- > 0;)
+ {
+ RootObject id = tident.idents[j];
+ if (id.dyncast() == DYNCAST.identifier)
+ {
+ if (!s || !s.parent)
+ goto Lnomatch;
+ Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id);
+ if (!s2)
+ goto Lnomatch;
+ s2 = s2.toAlias();
+ //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars());
+ if (s != s2)
+ {
+ if (Type tx = s2.getType())
+ {
+ if (s != tx.toDsymbol(sc))
+ goto Lnomatch;
+ }
+ else
+ goto Lnomatch;
+ }
+ s = s.parent;
+ }
+ else
+ goto Lnomatch;
+ }
+ //printf("[e] s = %s\n", s?s.toChars():"(null)");
+ if (tp.isTemplateTypeParameter())
+ {
+ Type tt = s.getType();
+ if (!tt)
+ goto Lnomatch;
+ Type at = cast(Type)dedtypes[i];
+ if (at && at.ty == Tnone)
+ at = (cast(TypeDeduced)at).tded;
+ if (!at || tt.equals(at))
+ {
+ dedtypes[i] = tt;
+ goto Lexact;
+ }
+ }
+ if (tp.isTemplateAliasParameter())
+ {
+ Dsymbol s2 = cast(Dsymbol)dedtypes[i];
+ if (!s2 || s == s2)
+ {
+ dedtypes[i] = s;
+ goto Lexact;
+ }
+ }
+ goto Lnomatch;
+ }
+
+ // Found the corresponding parameter tp
+ /+
+ https://issues.dlang.org/show_bug.cgi?id=23578
+ To pattern match:
+ static if (is(S!int == S!av, alias av))
+
+ We eventually need to deduce `int` (Tint32 [0]) and `av` (Tident).
+ Previously this would not get pattern matched at all, but now we check if the
+ template parameter `av` came from.
+
+ This note has been left to serve as a hint for further explorers into
+ how IsExp matching works.
+ +/
+ if (auto ta = tp.isTemplateAliasParameter())
+ {
+ dedtypes[i] = t;
+ goto Lexact;
+ }
+ // (23578) - ensure previous behaviour for non-alias template params
+ if (!tp.isTemplateTypeParameter())
+ {
+ goto Lnomatch;
+ }
+
+ Type at = cast(Type)dedtypes[i];
+ Type tt;
+ if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
+ {
+ // type vs (none)
+ if (!at)
+ {
+ dedtypes[i] = tt;
+ *wm |= wx;
+ result = MATCH.constant;
+ return;
+ }
+
+ // type vs expressions
+ if (at.ty == Tnone)
+ {
+ auto xt = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ dedtypes[i] = tt;
+ if (result > MATCH.constant)
+ result = MATCH.constant; // limit level for inout matches
+ }
+ return;
+ }
+
+ // type vs type
+ if (tt.equals(at))
+ {
+ dedtypes[i] = tt; // Prefer current type match
+ goto Lconst;
+ }
+ if (tt.implicitConvTo(at.constOf()))
+ {
+ dedtypes[i] = at.constOf().mutableOf();
+ *wm |= MODFlags.const_;
+ goto Lconst;
+ }
+ if (at.implicitConvTo(tt.constOf()))
+ {
+ dedtypes[i] = tt.constOf().mutableOf();
+ *wm |= MODFlags.const_;
+ goto Lconst;
+ }
+ goto Lnomatch;
+ }
+ else if (MATCH m = deduceTypeHelper(t, tt, tparam))
+ {
+ // type vs (none)
+ if (!at)
+ {
+ dedtypes[i] = tt;
+ result = m;
+ return;
+ }
+
+ // type vs expressions
+ if (at.ty == Tnone)
+ {
+ auto xt = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ dedtypes[i] = tt;
+ }
+ return;
+ }
+
+ // type vs type
+ if (tt.equals(at))
+ {
+ goto Lexact;
+ }
+ if (tt.ty == Tclass && at.ty == Tclass)
+ {
+ result = tt.implicitConvTo(at);
+ return;
+ }
+ if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant)
+ {
+ goto Lexact;
+ }
+ }
+ goto Lnomatch;
+ }
+
+ if (tparam.ty == Ttypeof)
+ {
+ /* Need a loc to go with the semantic routine. */
+ Loc loc = semanticLoc(parameters);
+
+ tparam = tparam.typeSemantic(loc, sc);
+ }
+ if (t.ty != tparam.ty)
+ {
+ if (Dsymbol sym = t.toDsymbol(sc))
+ {
+ if (sym.isforwardRef() && !tparam.deco)
+ goto Lnomatch;
+ }
+
+ MATCH m = t.implicitConvTo(tparam);
+ if (m == MATCH.nomatch && !ignoreAliasThis)
+ {
+ m = deduceAliasThis(t, sc, tparam, parameters, dedtypes, wm);
+ }
+ result = m;
+ return;
+ }
+
+ if (t.nextOf())
+ {
+ if (tparam.deco && !tparam.hasWild())
+ {
+ result = t.implicitConvTo(tparam);
+ return;
+ }
+
+ Type tpn = tparam.nextOf();
+ if (wm && t.ty == Taarray && tparam.isWild())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12403
+ // In IFTI, stop inout matching on transitive part of AA types.
+ tpn = tpn.substWildTo(MODFlags.mutable);
+ }
+
+ result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm);
+ return;
+ }
+
+ Lexact:
+ result = MATCH.exact;
+ return;
+
+ Lnomatch:
+ result = MATCH.nomatch;
+ return;
+
+ Lconst:
+ result = MATCH.constant;
+ }
+
+ override void visit(TypeVector t)
+ {
+ if (auto tp = tparam.isTypeVector())
+ {
+ result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeDArray t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeSArray t)
+ {
+ // Extra check that array dimensions must match
+ if (!tparam)
+ {
+ visit(cast(Type)t);
+ return;
+ }
+
+ if (tparam.ty == Tarray)
+ {
+ MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
+ result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
+ return;
+ }
+
+ TemplateParameter tp = null;
+ Expression edim = null;
+ size_t i;
+ if (auto tsa = tparam.isTypeSArray())
+ {
+ if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter)
+ {
+ Identifier id = tsa.dim.isVarExp().var.ident;
+ i = templateIdentifierLookup(id, ¶meters);
+ assert(i != IDX_NOTFOUND);
+ tp = parameters[i];
+ }
+ else
+ edim = tsa.dim;
+ }
+ else if (auto taa = tparam.isTypeAArray())
+ {
+ i = templateParameterLookup(taa.index, ¶meters);
+ if (i != IDX_NOTFOUND)
+ tp = parameters[i];
+ else
+ {
+ Loc loc;
+ // The "type" (it hasn't been resolved yet) of the function parameter
+ // does not have a location but the parameter it is related to does,
+ // so we use that for the resolution (better error message).
+ if (inferStart < parameters.length)
+ {
+ TemplateParameter loctp = parameters[inferStart];
+ loc = loctp.loc;
+ }
+
+ Expression e;
+ Type tx;
+ Dsymbol s;
+ taa.index.resolve(loc, sc, e, tx, s);
+ edim = s ? getValue(s) : getValue(e);
+ }
+ }
+ if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) ||
+ (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger())
+ )
+ {
+ result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
+ return;
+ }
+
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeAArray t)
+ {
+ // Extra check that index type must match
+ if (tparam && tparam.ty == Taarray)
+ {
+ TypeAArray tp = tparam.isTypeAArray();
+ if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ // Extra check that function characteristics must match
+ if (!tparam)
+ return visit(cast(Type)t);
+
+ auto tp = tparam.isTypeFunction();
+ if (!tp)
+ {
+ visit(cast(Type)t);
+ return;
+ }
+
+ if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ foreach (fparam; *tp.parameterList.parameters)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=2579
+ // Apply function parameter storage classes to parameter types
+ fparam.type = fparam.type.addStorageClass(fparam.storageClass);
+ fparam.storageClass &= ~STC.TYPECTOR;
+
+ // https://issues.dlang.org/show_bug.cgi?id=15243
+ // Resolve parameter type if it's not related with template parameters
+ if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length]))
+ {
+ auto tx = fparam.type.typeSemantic(Loc.initial, sc);
+ if (tx.ty == Terror)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ fparam.type = tx;
+ }
+ }
+
+ const size_t nfargs = t.parameterList.length;
+ size_t nfparams = tp.parameterList.length;
+
+ if (!deduceFunctionTuple(t, tp, parameters, dedtypes, nfargs, nfparams))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ L2:
+ assert(nfparams <= tp.parameterList.length);
+ foreach (i, ap; tp.parameterList)
+ {
+ if (i == nfparams)
+ break;
+
+ Parameter a = t.parameterList[i];
+
+ if (!a.isCovariant(t.isRef, ap) ||
+ !deduceType(a.type, sc, ap.type, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeIdentifier t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tident)
+ {
+ TypeIdentifier tp = tparam.isTypeIdentifier();
+ for (size_t i = 0; i < t.idents.length; i++)
+ {
+ RootObject id1 = t.idents[i];
+ RootObject id2 = tp.idents[i];
+ if (!id1.equals(id2))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeInstance t)
+ {
+ // Extra check
+ if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl)
+ {
+ visit(cast(Type)t);
+ return;
+ }
+
+ TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ TypeInstance tp = tparam.isTypeInstance();
+
+ //printf("tempinst.tempdecl = %p\n", tempdecl);
+ //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl);
+ if (!tp.tempinst.tempdecl)
+ {
+ //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars());
+
+ /* Handle case of:
+ * template Foo(T : sa!(T), alias sa)
+ */
+ size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters);
+ if (i == IDX_NOTFOUND)
+ {
+ /* Didn't find it as a parameter identifier. Try looking
+ * it up and seeing if is an alias.
+ * https://issues.dlang.org/show_bug.cgi?id=1454
+ */
+ auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name);
+ Type tx;
+ Expression e;
+ Dsymbol s;
+ tid.resolve(tp.loc, sc, e, tx, s);
+ if (tx)
+ {
+ s = tx.toDsymbol(sc);
+ if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14290
+ // Try to match with ti.tempecl,
+ // only when ti is an enclosing instance.
+ Dsymbol p = sc.parent;
+ while (p && p != ti)
+ p = p.parent;
+ if (p)
+ s = ti.tempdecl;
+ }
+ }
+ if (s)
+ {
+ s = s.toAlias();
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (td.overroot)
+ td = td.overroot;
+ for (; td; td = td.overnext)
+ {
+ if (td == tempdecl)
+ goto L2;
+ }
+ }
+ }
+ goto Lnomatch;
+ }
+
+ TemplateParameter tpx = parameters[i];
+ if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null))
+ goto Lnomatch;
+ }
+ else if (tempdecl != tp.tempinst.tempdecl)
+ goto Lnomatch;
+
+ L2:
+ if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes))
+ goto Lnomatch;
+
+ visit(cast(Type)t);
+ return;
+
+ Lnomatch:
+ //printf("no match\n");
+ result = MATCH.nomatch;
+ }
+
+ override void visit(TypeStruct t)
+ {
+ /* If this struct is a template struct, and we're matching
+ * it against a template instance, convert the struct type
+ * to a template instance, too, and try again.
+ */
+ TemplateInstance ti = t.sym.parent.isTemplateInstance();
+
+ if (tparam && tparam.ty == Tinstance)
+ {
+ if (ti && ti.toAlias() == t.sym)
+ {
+ auto tx = new TypeInstance(Loc.initial, ti);
+ auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
+ // if we have a no match we still need to check alias this
+ if (m != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ }
+
+ TypeInstance tpi = tparam.isTypeInstance();
+ auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm);
+ if (m != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tstruct)
+ {
+ TypeStruct tp = tparam.isTypeStruct();
+
+ //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tenum)
+ {
+ TypeEnum tp = tparam.isTypeEnum();
+ if (t.sym == tp.sym)
+ visit(cast(Type)t);
+ else
+ result = MATCH.nomatch;
+ return;
+ }
+ Type tb = t.toBasetype();
+ if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray)
+ {
+ result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
+ if (result == MATCH.exact)
+ result = MATCH.convert;
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeClass t)
+ {
+ //printf("TypeClass.deduceType(this = %s)\n", t.toChars());
+
+ /* If this class is a template class, and we're matching
+ * it against a template instance, convert the class type
+ * to a template instance, too, and try again.
+ */
+ TemplateInstance ti = t.sym.parent.isTemplateInstance();
+
+ if (tparam && tparam.ty == Tinstance)
+ {
+ if (ti && ti.toAlias() == t.sym)
+ {
+ auto tx = new TypeInstance(Loc.initial, ti);
+ MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
+ // Even if the match fails, there is still a chance it could match
+ // a base class.
+ if (m != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ }
+
+ TypeInstance tpi = tparam.isTypeInstance();
+ auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm);
+ if (m != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+
+ // If it matches exactly or via implicit conversion, we're done
+ visit(cast(Type)t);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* There is still a chance to match via implicit conversion to
+ * a base class or interface. Because there could be more than one such
+ * match, we need to check them all.
+ */
+
+ int numBaseClassMatches = 0; // Have we found an interface match?
+
+ // Our best guess at dedtypes
+ auto best = new Objects(dedtypes.length);
+
+ ClassDeclaration s = t.sym;
+ while (s && s.baseclasses.length > 0)
+ {
+ // Test the base class
+ deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
+
+ // Test the interfaces inherited by the base class
+ foreach (b; s.interfaces)
+ {
+ deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
+ }
+ s = (*s.baseclasses)[0].sym;
+ }
+
+ if (numBaseClassMatches == 0)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ // If we got at least one match, copy the known types into dedtypes
+ memcpy(dedtypes.tdata(), best.tdata(), best.length * (void*).sizeof);
+ result = MATCH.convert;
+ return;
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tclass)
+ {
+ TypeClass tp = tparam.isTypeClass();
+
+ //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.deduceType(e = %s)\n", e.toChars());
+ size_t i = templateParameterLookup(tparam, ¶meters);
+ if (i == IDX_NOTFOUND || tparam.isTypeIdentifier().idents.length > 0)
+ {
+ if (e == emptyArrayElement && tparam.ty == Tarray)
+ {
+ Type tn = (cast(TypeNext)tparam).next;
+ result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
+ return;
+ }
+ e.type.accept(this);
+ return;
+ }
+
+ TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter();
+ if (!tp)
+ return; // nomatch
+
+ if (e == emptyArrayElement)
+ {
+ if (dedtypes[i])
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (tp.defaultType)
+ {
+ tp.defaultType.accept(this);
+ return;
+ }
+ }
+
+ Type at = cast(Type)dedtypes[i];
+ Type tt;
+ if (ubyte wx = deduceWildHelper(e.type, &tt, tparam))
+ {
+ *wm |= wx;
+ result = MATCH.constant;
+ }
+ else if (MATCH m = deduceTypeHelper(e.type, tt, tparam))
+ {
+ result = m;
+ }
+ else if (!isTopRef(e.type))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15653
+ * In IFTI, recognize top-qualifier conversions
+ * through the value copy, e.g.
+ * int --> immutable(int)
+ * immutable(string[]) --> immutable(string)[]
+ */
+ tt = e.type.mutableOf();
+ result = MATCH.convert;
+ }
+ else
+ return; // nomatch
+
+ // expression vs (none)
+ if (!at)
+ {
+ dedtypes[i] = new TypeDeduced(tt, e, tparam);
+ return;
+ }
+
+ TypeDeduced xt = null;
+ if (at.ty == Tnone)
+ {
+ xt = cast(TypeDeduced)at;
+ at = xt.tded;
+ }
+
+ // From previous matched expressions to current deduced type
+ MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+
+ // From current expressions to previous deduced type
+ Type pt = at.addMod(tparam.mod);
+ if (*wm)
+ pt = pt.substWildTo(*wm);
+ MATCH match2 = e.implicitConvTo(pt);
+
+ if (match1 > MATCH.nomatch && match2 > MATCH.nomatch)
+ {
+ if (at.implicitConvTo(tt) == MATCH.nomatch)
+ match1 = MATCH.nomatch; // Prefer at
+ else if (tt.implicitConvTo(at) == MATCH.nomatch)
+ match2 = MATCH.nomatch; // Prefer tt
+ else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod)
+ {
+ if (!tt.isMutable() && !at.isMutable())
+ tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod));
+ else if (tt.isMutable())
+ {
+ if (at.mod == 0) // Prefer unshared
+ match1 = MATCH.nomatch;
+ else
+ match2 = MATCH.nomatch;
+ }
+ else if (at.isMutable())
+ {
+ if (tt.mod == 0) // Prefer unshared
+ match2 = MATCH.nomatch;
+ else
+ match1 = MATCH.nomatch;
+ }
+ //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars());
+ }
+ else
+ {
+ match1 = MATCH.nomatch;
+ match2 = MATCH.nomatch;
+ }
+ }
+ if (match1 > MATCH.nomatch)
+ {
+ // Prefer current match: tt
+ if (xt)
+ xt.update(tt, e, tparam);
+ else
+ dedtypes[i] = tt;
+ result = match1;
+ return;
+ }
+ if (match2 > MATCH.nomatch)
+ {
+ // Prefer previous match: (*dedtypes)[i]
+ if (xt)
+ xt.update(e, tparam);
+ result = match2;
+ return;
+ }
+
+ /* Deduce common type
+ */
+ if (Type t = rawTypeMerge(at, tt))
+ {
+ if (xt)
+ xt.update(t, e, tparam);
+ else
+ dedtypes[i] = t;
+
+ pt = tt.addMod(tparam.mod);
+ if (*wm)
+ pt = pt.substWildTo(*wm);
+ result = e.implicitConvTo(pt);
+ return;
+ }
+
+ result = MATCH.nomatch;
+ }
+
+ private MATCH deduceEmptyArrayElement()
+ {
+ if (!emptyArrayElement)
+ {
+ emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy
+ emptyArrayElement.type = Type.tvoid;
+ }
+ assert(tparam.ty == Tarray);
+
+ Type tn = (cast(TypeNext)tparam).next;
+ return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
+ }
+
+ override void visit(NullExp e)
+ {
+ if (tparam.ty == Tarray && e.type.ty == Tnull)
+ {
+ // tparam:T[] <- e:null (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(StringExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.len).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20092
+ if (e.elements && e.elements.length && e.type.toBasetype().nextOf().ty == Tvoid)
+ {
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ if ((!e.elements || !e.elements.length) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray)
+ {
+ // tparam:T[] <- e:[] (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+
+ if (tparam.ty == Tarray && e.elements && e.elements.length)
+ {
+ Type tn = (cast(TypeDArray)tparam).next;
+ result = MATCH.exact;
+ if (e.basis)
+ {
+ MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm);
+ if (m < result)
+ result = m;
+ }
+ foreach (el; *e.elements)
+ {
+ if (result == MATCH.nomatch)
+ break;
+ if (!el)
+ continue;
+ MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
+ if (m < result)
+ result = m;
+ }
+ return;
+ }
+
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.elements.length).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (tparam.ty == Taarray && e.keys && e.keys.length)
+ {
+ TypeAArray taa = cast(TypeAArray)tparam;
+ result = MATCH.exact;
+ foreach (i, key; *e.keys)
+ {
+ MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm);
+ if (m1 < result)
+ result = m1;
+ if (result == MATCH.nomatch)
+ break;
+ MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm);
+ if (m2 < result)
+ result = m2;
+ if (result == MATCH.nomatch)
+ break;
+ }
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars());
+ if (e.td)
+ {
+ Type to = tparam;
+ if (!to.nextOf())
+ return;
+ auto tof = to.nextOf().isTypeFunction();
+ if (!tof)
+ return;
+
+ // Parameter types inference from 'tof'
+ assert(e.td._scope);
+ TypeFunction tf = e.fd.type.isTypeFunction();
+ //printf("\ttof = %s\n", tof.toChars());
+ //printf("\ttf = %s\n", tf.toChars());
+ const dim = tf.parameterList.length;
+
+ if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
+ return;
+
+ auto tiargs = new Objects();
+ tiargs.reserve(e.td.parameters.length);
+
+ foreach (tp; *e.td.parameters)
+ {
+ size_t u = 0;
+ foreach (i, p; tf.parameterList)
+ {
+ if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+ break;
+ ++u;
+ }
+ assert(u < dim);
+ Parameter pto = tof.parameterList[u];
+ if (!pto)
+ break;
+ Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774
+ if (reliesOnTemplateParameters(t, parameters[inferStart .. parameters.length]))
+ return;
+ t = t.typeSemantic(e.loc, sc);
+ if (t.ty == Terror)
+ return;
+ tiargs.push(t);
+ }
+
+ // Set target of return type inference
+ if (!tf.next && tof.next)
+ e.fd.treq = tparam;
+
+ auto ti = new TemplateInstance(e.loc, e.td, tiargs);
+ Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+
+ // Reset inference target for the later re-semantic
+ e.fd.treq = null;
+
+ if (ex.op == EXP.error)
+ return;
+ if (ex.op != EXP.function_)
+ return;
+ visit(ex.type);
+ return;
+ }
+
+ Type t = e.type;
+
+ if (t.ty == Tdelegate && tparam.ty == Tpointer)
+ return;
+
+ // Allow conversion from implicit function pointer to delegate
+ if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate)
+ {
+ TypeFunction tf = t.nextOf().isTypeFunction();
+ t = (new TypeDelegate(tf)).merge();
+ }
+ //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars());
+ visit(t);
+ }
+
+ override void visit(SliceExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
+ {
+ // Consider compile-time known boundaries
+ if (Type tsa = toStaticArrayType(e))
+ {
+ tsa.accept(this);
+ if (result > MATCH.convert)
+ result = MATCH.convert; // match with implicit conversion at most
+ return;
+ }
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+ }
+
+ scope DeduceType v = new DeduceType();
+ if (Type t = isType(o))
+ t.accept(v);
+ else if (Expression e = isExpression(o))
+ {
+ assert(wm);
+ e.accept(v);
+ }
+ else
+ assert(0);
+ return v.result;
+}
+
+
+/* Helper for TypeClass.deduceType().
+ * Classes can match with implicit conversion to a base class or interface.
+ * This is complicated, because there may be more than one base class which
+ * matches. In such cases, one or more parameters remain ambiguous.
+ * For example,
+ *
+ * interface I(X, Y) {}
+ * class C : I(uint, double), I(char, double) {}
+ * C x;
+ * foo(T, U)( I!(T, U) x)
+ *
+ * deduces that U is double, but T remains ambiguous (could be char or uint).
+ *
+ * Given a baseclass b, and initial deduced types 'dedtypes', this function
+ * tries to match tparam with b, and also tries all base interfaces of b.
+ * If a match occurs, numBaseClassMatches is incremented, and the new deduced
+ * types are ANDed with the current 'best' estimate for dedtypes.
+ */
+private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches)
+{
+ if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null)
+ {
+ // Make a temporary copy of dedtypes so we don't destroy it
+ auto tmpdedtypes = new Objects(dedtypes.length);
+ memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.length * (void*).sizeof);
+
+ auto t = new TypeInstance(Loc.initial, parti);
+ MATCH m = deduceType(t, sc, tparam, parameters, *tmpdedtypes);
+ if (m > MATCH.nomatch)
+ {
+ // If this is the first ever match, it becomes our best estimate
+ if (numBaseClassMatches == 0)
+ memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.length * (void*).sizeof);
+ else
+ for (size_t k = 0; k < tmpdedtypes.length; ++k)
+ {
+ // If we've found more than one possible type for a parameter,
+ // mark it as unknown.
+ if ((*tmpdedtypes)[k] != best[k])
+ best[k] = dedtypes[k];
+ }
+ ++numBaseClassMatches;
+ }
+ }
+
+ // Now recursively test the inherited interfaces
+ foreach (ref bi; b.baseInterfaces)
+ {
+ deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ }
+}
+
+/*
+ * Handle tuple matching for function parameters.
+ * If the last parameter of `tp` is a template tuple parameter,
+ * collect the corresponding argument types from `t`.
+ * Params:
+ * t = actual function type
+ * tp = template function type
+ * parameters = template parameters
+ * dedtypes = deduced types array
+ * nfargs = number of arguments in `t`
+ * nfparams = number of parameters in `tp` (updated on success)
+ * Returns: `true` on success, `false` on mismatch.
+ */
+private bool deduceFunctionTuple(TypeFunction t, TypeFunction tp,
+ ref TemplateParameters parameters, ref Objects dedtypes,
+ size_t nfargs, ref size_t nfparams)
+{
+ if (nfparams > 0 && nfargs >= nfparams - 1)
+ {
+ Parameter fparam = tp.parameterList[nfparams - 1];
+ assert(fparam && fparam.type);
+ if (fparam.type.ty == Tident)
+ {
+ TypeIdentifier tid = fparam.type.isTypeIdentifier();
+ if (tid.idents.length == 0)
+ {
+ size_t tupi = 0;
+ for (; tupi < parameters.length; ++tupi)
+ {
+ TemplateParameter tx = parameters[tupi];
+ TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+ if (tup && tup.ident.equals(tid.ident))
+ break;
+ }
+ if (tupi == parameters.length)
+ return nfargs == nfparams;
+
+ size_t tuple_dim = nfargs - (nfparams - 1);
+
+ RootObject o = dedtypes[tupi];
+ if (o)
+ {
+ Tuple tup = isTuple(o);
+ if (!tup || tup.objects.length != tuple_dim)
+ return false;
+ for (size_t i = 0; i < tuple_dim; ++i)
+ {
+ if (!t.parameterList[nfparams - 1 + i].type.equals(tup.objects[i]))
+ return false;
+ }
+ }
+ else
+ {
+ auto tup = new Tuple(tuple_dim);
+ for (size_t i = 0; i < tuple_dim; ++i)
+ tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
+ dedtypes[tupi] = tup;
+ }
+ --nfparams; // ignore tuple parameter for further deduction
+ return true;
+ }
+ }
+ }
+ return nfargs == nfparams;
+}
+
+/********************
+ * Match template `parameters` to the target template instance.
+ * Example:
+ * struct Temp(U, int Z) {}
+ * void foo(T)(Temp!(T, 3));
+ * foo(Temp!(int, 3)());
+ * Input:
+ * sc = context
+ * parameters = template params of foo -> [T]
+ * tiargs = <Temp!(int, 3)>.tiargs -> [int, 3]
+ * tdtypes = <Temp!(int, 3)>.tdtypes -> [int, 3]
+ * tempdecl = <struct Temp!(T, int Z)> -> [T, Z]
+ * tp = <Temp!(T, 3)>
+ * Output:
+ * dedtypes = deduced params of `foo(Temp!(int, 3)())` -> [int]
+ */
+private bool resolveTemplateInstantiation(Scope* sc, TemplateParameters* parameters, Objects* tiargs, Objects* tdtypes, TemplateDeclaration tempdecl, TypeInstance tp, Objects* dedtypes)
+{
+ for (size_t i = 0; 1; i++)
+ {
+ //printf("\ttest: tempinst.tiargs[%zu]\n", i);
+ RootObject o1 = null;
+ if (i < tiargs.length)
+ o1 = (*tiargs)[i];
+ else if (i < tdtypes.length && i < tp.tempinst.tiargs.length)
+ {
+ // Pick up default arg
+ o1 = (*tdtypes)[i];
+ }
+ else if (i >= tp.tempinst.tiargs.length)
+ break;
+ //printf("\ttest: o1 = %s\n", o1.toChars());
+ if (i >= tp.tempinst.tiargs.length)
+ {
+ size_t dim = tempdecl.parameters.length - (tempdecl.isVariadic() ? 1 : 0);
+ while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg()))
+ {
+ i++;
+ }
+ if (i >= dim)
+ break; // match if all remained parameters are dependent
+ return false;
+ }
+
+ RootObject o2 = (*tp.tempinst.tiargs)[i];
+ Type t2 = isType(o2);
+ //printf("\ttest: o2 = %s\n", o2.toChars());
+ size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.length - 1)
+ ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
+ if (j != IDX_NOTFOUND && j == parameters.length - 1 &&
+ (*parameters)[j].isTemplateTupleParameter())
+ {
+ /* Given:
+ * struct A(B...) {}
+ * alias A!(int, float) X;
+ * static if (is(X Y == A!(Z), Z...)) {}
+ * deduce that Z is a tuple(int, float)
+ */
+
+ /* Create tuple from remaining args
+ */
+ size_t vtdim = (tempdecl.isVariadic() ? tiargs.length : tdtypes.length) - i;
+ auto vt = new Tuple(vtdim);
+ for (size_t k = 0; k < vtdim; k++)
+ {
+ RootObject o;
+ if (k < tiargs.length)
+ o = (*tiargs)[i + k];
+ else // Pick up default arg
+ o = (*tdtypes)[i + k];
+ vt.objects[k] = o;
+ }
+
+ Tuple v = cast(Tuple)(*dedtypes)[j];
+ if (v)
+ {
+ if (!match(v, vt))
+ return false;
+ }
+ else
+ (*dedtypes)[j] = vt;
+ break;
+ }
+ else if (!o1)
+ break;
+
+ Type t1 = isType(o1);
+ Dsymbol s1 = isDsymbol(o1);
+ Dsymbol s2 = isDsymbol(o2);
+ Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
+ Expression e2 = isExpression(o2);
+ version (none)
+ {
+ Tuple v1 = isTuple(o1);
+ Tuple v2 = isTuple(o2);
+ if (t1)
+ printf("t1 = %s\n", t1.toChars());
+ if (t2)
+ printf("t2 = %s\n", t2.toChars());
+ if (e1)
+ printf("e1 = %s\n", e1.toChars());
+ if (e2)
+ printf("e2 = %s\n", e2.toChars());
+ if (s1)
+ printf("s1 = %s\n", s1.toChars());
+ if (s2)
+ printf("s2 = %s\n", s2.toChars());
+ if (v1)
+ printf("v1 = %s\n", v1.toChars());
+ if (v2)
+ printf("v2 = %s\n", v2.toChars());
+ }
+
+ if (t1 && t2)
+ {
+ if (!deduceType(t1, sc, t2, *parameters, *dedtypes))
+ return false;
+ }
+ else if (e1 && e2)
+ {
+ Le:
+ e1 = e1.ctfeInterpret();
+
+ /* If it is one of the template parameters for this template,
+ * we should not attempt to interpret it. It already has a value.
+ */
+ if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter))
+ {
+ /*
+ * (T:Number!(e2), int e2)
+ */
+ j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters);
+ if (j != IDX_NOTFOUND)
+ goto L1;
+ // The template parameter was not from this template
+ // (it may be from a parent template, for example)
+ }
+
+ e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417
+ e2 = e2.ctfeInterpret();
+
+ //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty);
+ //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty);
+ if (!e1.equals(e2))
+ {
+ if (!e2.implicitConvTo(e1.type))
+ return false;
+
+ e2 = e2.implicitCastTo(sc, e1.type);
+ e2 = e2.ctfeInterpret();
+ if (!e1.equals(e2))
+ return false;
+ }
+ }
+ else if (e1 && t2 && t2.ty == Tident)
+ {
+ j = templateParameterLookup(t2, parameters);
+ L1:
+ if (j == IDX_NOTFOUND)
+ {
+ t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
+ if (e2)
+ goto Le;
+ return false;
+ }
+ if (!(*parameters)[j].matchArg(sc, e1, j, parameters, *dedtypes, null))
+ return false;
+ }
+ else if (s1 && s2)
+ {
+ Ls:
+ if (!s1.equals(s2))
+ return false;
+ }
+ else if (s1 && t2 && t2.ty == Tident)
+ {
+ j = templateParameterLookup(t2, parameters);
+ if (j == IDX_NOTFOUND)
+ {
+ t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
+ if (s2)
+ goto Ls;
+ return false;
+ }
+ if (!(*parameters)[j].matchArg(sc, s1, j, parameters, *dedtypes, null))
+ return false;
+ }
+ else
+ return false;
+ }
+ return true;
+}
+
+private Expression getValue(ref Dsymbol s)
+{
+ if (s)
+ {
+ if (VarDeclaration v = s.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ return v.getConstInitializer();
+ }
+ }
+ return null;
+}
+
+/***********************
+ * Try to get value from manifest constant
+ */
+private Expression getValue(Expression e)
+{
+ if (!e)
+ return null;
+ if (auto ve = e.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ {
+ e = v.getConstInitializer();
+ }
+ }
+ }
+ return e;
+}
+
+Expression getExpression(RootObject o)
+{
+ auto s = isDsymbol(o);
+ return s ? .getValue(s) : .getValue(isExpression(o));
+}
if (c <= 0xFF)
{
if (isprint(c))
- buf.writeByte(c);
+ buf.writeByte(cast(char)c);
else
buf.printf("\\x%02x", c);
}
import dmd.root.speller;
import dmd.root.stringtable;
import dmd.target;
+import dmd.templatesem : TemplateInstance_semanticTiargs;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
sc.stc |= STC.deprecated_;
Scope* sc2 = sc.startCTFE();
scope(exit) { sc2.endCTFE(); }
- if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1))
+ if (!TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1))
{
sc.stc = save;
return ErrorExp.get();
* a symbol should not be folded to a constant.
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
*/
- if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
+ if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 2))
return ErrorExp.get();
if (dim != 1)
return dimError(1);
sc2.copyFlagsFrom(sc);
sc2.noAccessCheck = true;
sc2.ignoresymbolvisibility = true;
- bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
+ bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
return ErrorExp.get();
sc2.copyFlagsFrom(sc);
sc2.noAccessCheck = true;
sc2.ignoresymbolvisibility = true;
- bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
+ bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
return ErrorExp.get();
* a symbol should not be folded to a constant.
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
*/
- if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
+ if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 3))
return ErrorExp.get();
if (dim != 1)
ob1.push((*e.args)[0]);
Objects ob2;
ob2.push((*e.args)[1]);
- if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
+ if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob1, 0))
return ErrorExp.get();
- if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
+ if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob2, 0))
return ErrorExp.get();
if (ob1.length != ob2.length)
return False();
assert(ctor);
scope el = new IdentifierExp(Loc.initial, Id.p); // dummy lvalue
el.type = cast() ts;
- Expressions* args = new Expressions();
- args.push(el);
+ Expressions* args = new Expressions(el);
FuncDeclaration f = resolveFuncCall(Loc.initial, null, ctor, null, cast()ts, ArgumentList(args), FuncResolveFlag.quiet);
if (!f || f.storage_class & STC.disable)
return false;
return true;
}
+/**************************
+ * When T is mutable,
+ * Given:
+ * T a, b;
+ * Can we bitwise assign:
+ * a = b;
+ * ?
+ */
+bool isAssignable(Type t)
+{
+ if (auto te = t.isTypeEnum())
+ t = te.memType();
+ TypeStruct ts = t.isTypeStruct();
+ if (!ts)
+ return true;
+
+ bool assignable = true;
+ uint offset = ~0; // dead-store initialize to prevent spurious warning
+
+ auto sym = ts.sym;
+ sym.determineSize(sym.loc);
+
+ /* If any of the fields are const or immutable,
+ * then one cannot assign this struct.
+ */
+ for (size_t i = 0; i < sym.fields.length; i++)
+ {
+ VarDeclaration v = sym.fields[i];
+ //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind());
+ if (i == 0)
+ {
+ }
+ else if (v.offset == offset)
+ {
+ /* If any fields of anonymous union are assignable,
+ * then regard union as assignable.
+ * This is to support unsafe things like Rebindable templates.
+ */
+ if (assignable)
+ continue;
+ }
+ else
+ {
+ if (!assignable)
+ return false;
+ }
+ assignable = v.type.isMutable() && v.type.isAssignable();
+ offset = v.offset;
+ //printf(" -> assignable = %d\n", assignable);
+ }
+
+ return assignable;
+}
+
/************************************
* Determine mutability of indirections in (ref) t.
*
return false;
}
+/**
+ * Look for the index of parameter `ident` in the parameter list
+ *
+ * Params:
+ * tf = function type
+ * ident = identifier of parameter to search for
+ * Returns: index of parameter with name `ident` or -1 if not found
+ */
+private extern(D) ptrdiff_t findParameterIndex(TypeFunction tf, Identifier ident)
+{
+ foreach (i, p; tf.parameterList)
+ {
+ if (p.ident == ident)
+ return i;
+ }
+ return -1;
+}
+
+/*********************************
+ * Append error message to buf.
+ * Input:
+ * buf = message sink
+ * format = printf format
+ */
+private extern(C) void getMatchError(ref OutBuffer buf, const(char)* format, ...)
+{
+ if (global.gag && !global.params.v.showGaggedErrors)
+ return;
+ va_list ap;
+ va_start(ap, format);
+ buf.vprintf(format, ap);
+ va_end(ap);
+}
+
+/********************************
+ * Convert an `argumentList`, which may contain named arguments, into
+ * a list of arguments in the order of the parameter list.
+ *
+ * Params:
+ * tf = function type
+ * argumentList = array of function arguments
+ * buf = if not null, append error message to it
+ * Returns: re-ordered argument list, or `null` on error
+ */
+extern(D) Expressions* resolveNamedArgs(TypeFunction tf, ArgumentList argumentList, OutBuffer* buf)
+{
+ Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null;
+ ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null;
+ const nParams = tf.parameterList.length(); // cached because O(n)
+ auto newArgs = new Expressions(nParams);
+ newArgs.zero();
+ size_t ci = 0;
+ bool hasNamedArgs = false;
+ const bool isVariadic = tf.parameterList.varargs != VarArg.none;
+ foreach (i, arg; args)
+ {
+ if (!arg)
+ {
+ ci++;
+ continue;
+ }
+ auto name = i < names.length ? names[i].name : null;
+ if (name)
+ {
+ hasNamedArgs = true;
+ const pi = tf.findParameterIndex(name);
+ if (pi == -1)
+ {
+ if (buf)
+ getMatchError(*buf, "no parameter named `%s`", name.toChars());
+ return null;
+ }
+ ci = pi;
+ }
+ if (ci >= newArgs.length)
+ {
+ if (!isVariadic)
+ {
+ // Without named args, let the caller diagnose argument overflow
+ if (hasNamedArgs && buf)
+ getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars());
+ return null;
+ }
+ while (ci >= newArgs.length)
+ newArgs.push(null);
+ }
+
+ if ((*newArgs)[ci])
+ {
+ if (buf)
+ getMatchError(*buf, "parameter `%s` assigned twice", tf.parameterList[ci].toChars());
+ return null;
+ }
+ (*newArgs)[ci++] = arg;
+ }
+ foreach (i, arg; (*newArgs)[])
+ {
+ if (arg || tf.parameterList[i].defaultArg)
+ continue;
+
+ if (isVariadic && i + 1 == newArgs.length)
+ continue;
+
+ // dtemplate sets `defaultArg=null` to avoid semantic on default arguments,
+ // don't complain about missing arguments in that case
+ if (tf.incomplete)
+ continue;
+
+ if (buf)
+ getMatchError(*buf, "missing argument for parameter #%d: `%s`",
+ i + 1, parameterToChars(tf.parameterList[i], tf, false));
+ return null;
+ }
+ // strip trailing nulls from default arguments
+ size_t e = newArgs.length;
+ while (e > 0 && (*newArgs)[e - 1] is null)
+ {
+ --e;
+ }
+ newArgs.setDim(e);
+ return newArgs;
+}
+
/********************************
* 'args' are being matched to function type 'tf'
* Determine match level.
if (errorHelper)
{
if (u >= args.length)
- TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`",
- u + 1, parameterToChars(p, tf, false));
+ getMatchError(buf, "missing argument for parameter #%d: `%s`",
+ u + 1, parameterToChars(p, tf, false));
// If an error happened previously, `pMessage` was already filled
else if (buf.length == 0)
buf.writestring(tf.getParamError(args[u], p));
{
// all parameters had a match, but there are surplus args
OutBuffer buf2;
- TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length);
+ getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length);
errorHelper(buf2.extractChars());
return MATCH.nomatch;
}
if (!f)
return nocpctor();
- if (f.isDisabled() && !f.isGenerated())
+ if (f.isDisabled() && !f.isGenerated)
{
/* https://issues.dlang.org/show_bug.cgi?id=24301
* Copy constructor is explicitly disabled
bsafe ? "@safe ".ptr : nullptr,
bnogc ? "nogc" .ptr : nullptr);
}
- else if (f.isGenerated() && f.isDisabled())
+ else if (f.isGenerated && f.isDisabled())
{
/* https://issues.dlang.org/show_bug.cgi?id=23097
* Compiler generated copy constructor failed.
return MATCH.nomatch;
}
+ if (arg.isBitField())
+ {
+ if (pMessage) *pMessage = tf.getParamError(arg, p);
+ return MATCH.nomatch;
+ }
+
return m;
}
// when comparing the type with strcmp, we need to drop the qualifier
bool qual = !arg.type.mutableOf().equals(par.type.mutableOf()) &&
strcmp(arg.type.mutableOf().toChars(), par.type.mutableOf().toChars()) == 0;
- auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars();
OutBuffer buf;
- // only mention rvalue if it's relevant
- const rv = !arg.isLvalue() && par.isReference();
- buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
- rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at,
- parameterToChars(par, tf, qual));
+ // only mention rvalue or bitfield if it's relevant
+ if (arg.isBitField())
+ {
+ buf.printf("cannot pass bitfield argument `%s` to parameter `%s`",
+ arg.toErrMsg(), parameterToChars(par, tf, qual));
+ }
+ else
+ {
+ auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars();
+ const rv = !arg.isLvalue() && par.isReference() && !(par.storageClass & STC.constscoperef);
+ buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
+ rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at,
+ parameterToChars(par, tf, qual));
+ }
return buf.extractChars();
}
if (pMessage)
{
OutBuffer buf;
- TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu",
+ getMatchError(buf, "expected %llu variadic argument(s), not %zu",
sz, trailingArgs.length);
*pMessage = buf.extractChars();
}
}
}
+/// Compute cached type properties for `TypeStruct`
+void determineTypeProperties(StructDeclaration sd)
+{
+ import dmd.dsymbolsem : hasPointers;
+ if (sd.computedTypeProperties)
+ return;
+ foreach (vd; sd.fields)
+ {
+ if (vd.storage_class & STC.ref_ || vd.hasPointers())
+ {
+ sd.hasPointerField = true;
+ sd.hasUnsafeBitpatterns = true;
+ }
+
+ if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers())
+ sd.hasVoidInitPointers = true;
+
+ if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns())
+ sd.hasUnsafeBitpatterns = true;
+
+ if (!vd._init && vd.type.hasVoidInitPointers())
+ sd.hasVoidInitPointers = true;
+
+ if (vd.type.hasInvariant())
+ sd.hasFieldWithInvariant = true;
+ }
+ sd.computedTypeProperties = true;
+}
+
/***************************************
* Return !=0 if type has pointers that need to
* be scanned by the GC during a collection cycle.
// duplicate a part of StructDeclaration::semanticTypeInfoMembers
//printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash);
- if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
+ if (sd.xeq && sd.xeq.isGenerated && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
{
uint errors = global.startGagging();
sd.xeq.semantic3(sd.xeq._scope);
if (farg && (eparam.storageClass & STC.ref_))
{
- if (!farg.isLvalue())
+ if (!farg.isLvalue() || farg.isBitField())
eparam.storageClass &= ~STC.ref_; // value parameter
eparam.storageClass &= ~STC.auto_; // https://issues.dlang.org/show_bug.cgi?id=14656
eparam.storageClass |= STC.autoref;
return type;
goto default;
+ case Tfunction:
+ if (!type.nextOf()) // don't merge if return type is unknown
+ return type;
+ goto default;
+
default:
if (type.nextOf() && !type.nextOf().deco)
return type;
return t;
}
+private enum LOGDEFAULTINIT = 0;
+/***************************************
+ * Use when we prefer the default initializer to be a literal,
+ * rather than a global immutable variable.
+ */
+Expression defaultInitLiteral(Type t, Loc loc)
+{
+
+ if (t.isTypeError())
+ return ErrorExp.get();
+ if (auto ts = t.isTypeStruct())
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
+ }
+ ts.sym.size(loc);
+ if (ts.sym.sizeok != Sizeok.done)
+ return ErrorExp.get();
+
+ auto structelems = new Expressions(ts.sym.nonHiddenFields());
+ uint offset = 0;
+ foreach (j; 0 .. structelems.length)
+ {
+ VarDeclaration vd = ts.sym.fields[j];
+ Expression e;
+ if (vd.inuse)
+ {
+ error(loc, "circular reference to `%s`", vd.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (vd.offset < offset || vd.type.size() == 0)
+ e = null;
+ else if (vd._init)
+ {
+ if (vd._init.isVoidInitializer())
+ e = null;
+ else
+ e = vd.getConstInitializer(false);
+ }
+ else
+ e = vd.type.defaultInitLiteral(loc);
+ if (e && e.op == EXP.error)
+ return e;
+ if (e)
+ offset = vd.offset + cast(uint)vd.type.size();
+ (*structelems)[j] = e;
+ }
+ auto structinit = new StructLiteralExp(loc, ts.sym, structelems);
+
+ /* Copy from the initializer symbol for larger symbols,
+ * otherwise the literals expressed as code get excessively large.
+ */
+ if (size(ts, loc) > target.ptrsize * 4 && !ts.needsNested())
+ structinit.useStaticInit = true;
+
+ structinit.type = ts;
+ return structinit;
+ }
+ if (auto tv = t.isTypeVector())
+ {
+ //printf("TypeVector::defaultInitLiteral()\n");
+ assert(tv.basetype.ty == Tsarray);
+ Expression e = tv.basetype.defaultInitLiteral(loc);
+ auto ve = new VectorExp(loc, e, tv);
+ ve.type = tv;
+ ve.dim = cast(int)(tv.basetype.size(loc) / tv.elementType().size(loc));
+ return ve;
+ }
+ if (auto tsa = t.isTypeSArray())
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
+ }
+ size_t d = cast(size_t)tsa.dim.toInteger();
+ Expression elementinit;
+ if (tsa.next.ty == Tvoid)
+ elementinit = Type.tuns8.defaultInitLiteral(loc);
+ else
+ elementinit = tsa.next.defaultInitLiteral(loc);
+ auto elements = new Expressions(d);
+ foreach (ref e; *elements)
+ e = null;
+ auto ae = new ArrayLiteralExp(loc, tsa, elementinit, elements);
+ return ae;
+ }
+ return defaultInit(t, loc);
+}
/***************************************
* Calculate built-in properties which just the type is necessary.
*
{
e = mt.defaultInitLiteral(loc);
auto se = new StringExp(e.loc, ident.toString());
- auto tiargs = new Objects();
- tiargs.push(se);
+ auto tiargs = new Objects(se);
auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
dti.ti.tempdecl = td;
dti.dotTemplateSemanticProp(scope_, DotExpFlag.none);
}
if (ident == Id.length)
{
- __gshared FuncDeclaration fd_aaLen = null;
- if (fd_aaLen is null)
- {
- auto fparams = new Parameters();
- fparams.push(new Parameter(Loc.initial, STC.const_ | STC.scope_, mt, null, null, null));
- fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen);
- TypeFunction tf = fd_aaLen.type.toTypeFunction();
- tf.purity = PURE.const_;
- tf.isNothrow = true;
- tf.isNogc = false;
- }
- Expression ev = new VarExp(e.loc, fd_aaLen, false);
- e = new CallExp(e.loc, ev, e);
- e.type = fd_aaLen.type.toTypeFunction().next;
+ auto loc = e.loc;
+ Expression hookFunc = new IdentifierExp(loc, Id.empty);
+ hookFunc = new DotIdExp(loc, hookFunc, Id.object);
+ auto keytype = mt.index.substWildTo(MODFlags.const_);
+ auto valtype = mt.nextOf().substWildTo(MODFlags.const_);
+ auto tiargs = new Objects(keytype, valtype);
+ hookFunc = new DotTemplateInstanceExp(loc, hookFunc, Id._d_aaLen, tiargs);
+ Expression e = new CallExp(loc, hookFunc, e);
+ e = e.expressionSemantic(sc);
return e;
}
else
/* Rewrite e.ident as:
* e.opDispatch!("ident")
*/
- TemplateDeclaration td = fd.isTemplateDeclaration();
- if (!td)
- {
- .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s", fd.kind, fd.toPrettyChars, fd.kind());
- return returnExp(ErrorExp.get());
- }
+
auto se = new StringExp(e.loc, ident.toString());
- auto tiargs = new Objects();
- tiargs.push(se);
+ auto tiargs = new Objects(se);
auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
- dti.ti.tempdecl = td;
+
+ if (OverloadSet os = fd.isOverloadSet())
+ {
+ if (!findTempDecl(dti, sc))
+ {
+ .error(fd.loc, "Couldn't find template declaration for opDispatch");
+ return returnExp(ErrorExp.get());
+ }
+ }
+ else
+ {
+ TemplateDeclaration td = fd.isTemplateDeclaration();
+ if (!td)
+ {
+ .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s",
+ fd.kind, fd.toPrettyChars, fd.kind());
+ return returnExp(ErrorExp.get());
+ }
+ dti.ti.tempdecl = td;
+ }
+
/* opDispatch, which doesn't need IFTI, may occur instantiate error.
* e.g.
* template opDispatch(name) if (isValid!name) { ... }
Expression e1;
Type t;
/* returns: true to continue, false to return */
- if (f.hasDualContext())
+ if (f.hasDualContext)
{
if (f.followInstantiationContext(ad))
{
return *pt;
*pt = Type.terror;
- Module mConfig = Module.loadCoreStdcConfig();
+ Module mConfig = loadCoreStdcConfig();
if (!mConfig)
{
error(loc, "`core.stdc.config` is required for complex numbers");
*/
if (t1.linkage == LINK.cpp && cppCovariant)
{
- notcovariant |= tp1.isNaked() != tp2.isNaked();
+ notcovariant |= tp1.isNaked != tp2.isNaked;
if (auto tpn1 = tp1.nextOf())
- notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked();
+ notcovariant |= tpn1.isNaked != tp2.nextOf().isNaked;
}
}
}
/* https://issues.dlang.org/show_bug.cgi?id=23135
* extern(C++) mutable member functions are not covariant with const.
*/
- if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked())
+ if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked != t2.isNaked)
goto Lnotcovariant;
/* Can convert mutable to const
/* If haven't inferred the return type yet, can't infer storage classes
*/
- if (!tf.nextOf() || !tf.isNothrow())
+ if (!tf.nextOf() || !tf.isNothrow)
return stc;
tf.purityLevel();
t = t.addMod(MODFlags.shared_);
//printf("-Type.substWildTo t = %s\n", t.toChars());
- return t;
+ return t.merge();
}
if (!tf.iswild && !(tf.mod & MODFlags.wild))
import dmd.globals;
import dmd.location;
import dmd.mtype;
+import dmd.templatesem;
import dmd.typesem;
import core.stdc.stdio;
auto ti = TypeInfoAssociativeArrayDeclaration.create(t);
t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic
- Loc loc = t.loc;
- auto tiargs = new Objects();
- tiargs.push(t.index); // always called with naked types
- tiargs.push(t.next);
-
- Expression id = new IdentifierExp(loc, Id.empty);
- id = new DotIdExp(loc, id, Id.object);
- id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray);
- auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs);
- auto e = expressionSemantic(tempinst, sc);
- assert(e.type);
- ti.entry = e.type;
- if (auto ts = ti.entry.isTypeStruct())
- {
- ts.sym.requestTypeInfo = true;
- if (auto tmpl = ts.sym.isInstantiated())
- tmpl.minst = sc._module.importedFrom; // ensure it get's emitted
- }
- getTypeInfoType(loc, ti.entry, sc);
- assert(ti.entry.vtinfo);
-
+ ti._scope = sc;
+ sc.setNoFree();
+ Module.addDeferredSemantic3(ti);
return ti;
}
* e = expression tree to visit
* dgVar = call when a variable is declared
*/
-void foreachVar(Expression e, void delegate(VarDeclaration) dgVar)
+void foreachVar(Expression e, scope void delegate(VarDeclaration) dgVar)
{
if (!e)
return;
* dgVar = delegate to pass found VarDeclarations to
*/
void foreachExpAndVar(Statement s,
- void delegate(Expression) dgExp,
- void delegate(VarDeclaration) dgVar)
+ scope void delegate(Expression) dgExp,
+ scope void delegate(VarDeclaration) dgVar)
{
void visit(Statement s)
{
doCond(e.e1) || doCond(e.e2) || applyTo(e);
}
+ override void visit(CatExp e)
+ {
+ if (auto lowering = e.lowering)
+ {
+ doCond(lowering) || applyTo(e);
+ }
+ else
+ {
+ visit(cast(BinExp)e);
+ }
+ }
+
+ override void visit(CatAssignExp e)
+ {
+ if (auto lowering = e.lowering)
+ {
+ doCond(lowering) || applyTo(e);
+ }
+ else
+ {
+ visit(cast(BinExp)e);
+ }
+ }
+
+ override void visit(EqualExp e)
+ {
+ if (auto lowering = e.lowering)
+ {
+ doCond(lowering) || applyTo(e);
+ }
+ else
+ {
+ visit(cast(BinExp)e);
+ }
+ }
+
override void visit(AssertExp e)
{
//printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
/* For static and dynamic arrays, equality is defined as the lengths of
the arrays matching, and all the elements are equal. */
Type *t1elem = tb1->nextOf ()->toBasetype ();
- Type *t2elem = tb1->nextOf ()->toBasetype ();
- /* Check if comparisons of arrays can be optimized using memcmp.
+ /* Use lowering if it has already been handled by the front-end. */
+ if (e->lowering != NULL)
+ {
+ this->result_ = build_expr (e->lowering);
+ return;
+ }
+
+ /* For all other arrays, comparisons can be optimized using memcmp.
This will inline EQ expressions as:
e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0;
Or when generating a NE expression:
e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */
- if ((t1elem->isIntegral () || t1elem->ty == TY::Tvoid
- || (t1elem->ty == TY::Tstruct
- && !t1elem->isTypeStruct ()->sym->xeq))
- && t1elem->ty == t2elem->ty)
- {
- tree t1 = d_array_convert (e->e1);
- tree t2 = d_array_convert (e->e2);
- tree result;
-
- /* Make temporaries to prevent multiple evaluations. */
- tree t1saved = d_save_expr (t1);
- tree t2saved = d_save_expr (t2);
-
- /* Length of arrays, for comparisons done before calling memcmp. */
- tree t1len = d_array_length (t1saved);
- tree t2len = d_array_length (t2saved);
-
- /* Reference to array data. */
- tree t1ptr = d_array_ptr (t1saved);
- tree t2ptr = d_array_ptr (t2saved);
+ tree t1 = d_array_convert (e->e1);
+ tree t2 = d_array_convert (e->e2);
+ tree result;
- /* Compare arrays using memcmp if possible, otherwise for structs,
- each field is compared inline. */
- if (t1elem->ty != TY::Tstruct
- || identity_compare_p (t1elem->isTypeStruct ()->sym))
- {
- tree size =
- size_mult_expr (t1len, size_int (dmd::size (t1elem)));
+ /* Make temporaries to prevent multiple evaluations. */
+ tree t1saved = d_save_expr (t1);
+ tree t2saved = d_save_expr (t2);
- result = build_memcmp_call (t1ptr, t2ptr, size);
- result = build_boolop (code, result, integer_zero_node);
- }
- else
- {
- StructDeclaration *sd = t1elem->isTypeStruct ()->sym;
+ /* Length of arrays, for comparisons done before calling memcmp. */
+ tree t1len = d_array_length (t1saved);
+ tree t2len = d_array_length (t2saved);
- result = build_array_struct_comparison (code, sd, t1len,
- t1ptr, t2ptr);
- }
+ /* Reference to array data. */
+ tree t1ptr = d_array_ptr (t1saved);
+ tree t2ptr = d_array_ptr (t2saved);
- /* Check array length first before passing to memcmp.
- For equality expressions, this becomes:
- (e1.length == 0 || memcmp);
- Otherwise for inequality:
- (e1.length != 0 && memcmp); */
- tree tsizecmp = build_boolop (code, t1len, size_zero_node);
- if (e->op == EXP::equal)
- result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result);
- else
- result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result);
+ /* Compare arrays using memcmp if possible, otherwise for structs,
+ each field is compared inline. */
+ if (t1elem->ty != TY::Tstruct
+ || identity_compare_p (t1elem->isTypeStruct ()->sym))
+ {
+ tree size =
+ size_mult_expr (t1len, size_int (dmd::size (t1elem)));
- /* Finally, check if lengths of both arrays match if dynamic.
- The frontend should have already guaranteed that static arrays
- have same size. */
- if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray)
- gcc_assert (dmd::size (tb1) == dmd::size (tb2));
- else
- {
- tree tlencmp = build_boolop (code, t1len, t2len);
- if (e->op == EXP::equal)
- result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result);
- else
- result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result);
- }
+ result = build_memcmp_call (t1ptr, t2ptr, size);
+ result = build_boolop (code, result, integer_zero_node);
+ }
+ else
+ {
+ StructDeclaration *sd = t1elem->isTypeStruct ()->sym;
- /* Ensure left-to-right order of evaluation. */
- if (TREE_SIDE_EFFECTS (t2))
- result = compound_expr (t2saved, result);
+ result = build_array_struct_comparison (code, sd, t1len,
+ t1ptr, t2ptr);
+ }
- if (TREE_SIDE_EFFECTS (t1))
- result = compound_expr (t1saved, result);
+ /* Check array length first before passing to memcmp.
+ For equality expressions, this becomes:
+ (e1.length == 0 || memcmp);
+ Otherwise for inequality:
+ (e1.length != 0 && memcmp); */
+ tree tsizecmp = build_boolop (code, t1len, size_zero_node);
+ if (e->op == EXP::equal)
+ result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result);
+ else
+ result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result);
- this->result_ = result;
- }
+ /* Finally, check if lengths of both arrays match if dynamic.
+ The frontend should have already guaranteed that static arrays
+ have same size. */
+ if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray)
+ gcc_assert (dmd::size (tb1) == dmd::size (tb2));
else
{
- /* Use _adEq2() to compare each element. */
- Type *t1array = dmd::arrayOf (t1elem);
- tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3,
- d_array_convert (e->e1),
- d_array_convert (e->e2),
- build_typeinfo (e, t1array));
+ tree tlencmp = build_boolop (code, t1len, t2len);
+ if (e->op == EXP::equal)
+ result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result);
+ else
+ result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result);
+ }
- if (e->op == EXP::notEqual)
- result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
+ /* Ensure left-to-right order of evaluation. */
+ if (TREE_SIDE_EFFECTS (t2))
+ result = compound_expr (t2saved, result);
- this->result_ = result;
- }
+ if (TREE_SIDE_EFFECTS (t1))
+ result = compound_expr (t1saved, result);
+
+ this->result_ = result;
}
else if (TypeStruct *ts = tb1->isTypeStruct ())
{
}
else if (tb1->ty == TY::Taarray && tb2->ty == TY::Taarray)
{
- /* Use _aaEqual() for associative arrays. */
- tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3,
- build_typeinfo (e, tb1),
- build_expr (e->e1),
- build_expr (e->e2));
-
- if (e->op == EXP::notEqual)
- result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
-
- this->result_ = result;
+ /* Call to `_d_aaEqual' for associative arrays has already been handled
+ by the front-end. */
+ gcc_unreachable ();
}
else
{
exists in an associative array. The result is a pointer to the
element, or null if false. */
- void visit (InExp *e) final override
+ void visit (InExp *) final override
{
- Type *tb2 = e->e2->type->toBasetype ();
- Type *tkey = tb2->isTypeAArray ()->index->toBasetype ();
- tree key = convert_expr (build_expr (e->e1), e->e1->type, tkey);
-
- /* Build a call to _aaInX(). */
- this->result_ = build_libcall (LIBCALL_AAINX, e->type, 3,
- build_expr (e->e2),
- build_typeinfo (e, tkey),
- build_address (key));
+ /* Call to `_d_aaIn' has already been handled by the front-end. */
+ gcc_unreachable ();
}
/* Build a relational expression. The result type is bool. */
if (tb1->ty == TY::Taarray)
{
- /* Get the key for the associative array. */
- Type *tkey = tb1->isTypeAArray ()->index->toBasetype ();
- tree key = convert_expr (build_expr (e->e2), e->e2->type, tkey);
- libcall_fn libcall;
- tree tinfo, ptr;
-
- if (e->modifiable)
- {
- libcall = LIBCALL_AAGETY;
- ptr = build_address (build_expr (e->e1));
- tinfo = build_typeinfo (e, dmd::mutableOf (dmd::unSharedOf (tb1)));
- }
- else
- {
- libcall = LIBCALL_AAGETRVALUEX;
- ptr = build_expr (e->e1);
- tinfo = build_typeinfo (e, tkey);
- }
-
- /* Index the associative array. */
- tree result = build_libcall (libcall, dmd::pointerTo (e->type), 4,
- ptr, tinfo,
- size_int (dmd::size (tb1->nextOf ())),
- build_address (key));
-
- if (!e->indexIsInBounds && array_bounds_check ())
- {
- tree tassert = build_array_bounds_call (e->loc);
-
- result = d_save_expr (result);
- result = build_condition (TREE_TYPE (result),
- d_truthvalue_conversion (result),
- result, tassert);
- }
-
- this->result_ = indirect_ref (build_ctype (e->type), result);
+ /* Associative arrays have already been handled by the front-end. */
+ gcc_unreachable ();
}
else
{
{
Type *ebtype = e->e1->type->toBasetype ();
Type *tbtype = e->to->toBasetype ();
- tree result = build_expr (e->e1, this->constp_, this->literalp_);
- /* Just evaluate e1 if it has any side effects. */
- if (tbtype->ty == TY::Tvoid)
+ /* Use lowering if it has already been handled by the front-end. */
+ Expression *cast = (e->lowering != NULL) ? e->lowering : e->e1;
+ tree result = build_expr (cast, this->constp_, this->literalp_);
+
+ /* When expression has been rewritten or is a cast to `void', just evaluate
+ the result if it has any side effects. */
+ if (e->lowering != NULL || tbtype->ty == TY::Tvoid)
this->result_ = build_nop (build_ctype (tbtype), result);
else
this->result_ = convert_for_rvalue (result, ebtype, tbtype);
/* Check that the array is actually an associative array. */
if (e->e1->type->toBasetype ()->ty == TY::Taarray)
{
- Type *tb = e->e1->type->toBasetype ();
- Type *tkey = tb->isTypeAArray ()->index->toBasetype ();
- tree index = convert_expr (build_expr (e->e2), e->e2->type, tkey);
-
- this->result_ = build_libcall (LIBCALL_AADELX, Type::tbool, 3,
- build_expr (e->e1),
- build_typeinfo (e, tkey),
- build_address (index));
+ /* Call to `_d_aaDel' has already been handled by the front-end. */
+ gcc_unreachable ();
}
else
{
Taking the address of a struct literal is otherwise illegal. */
if (e->e1->op == EXP::structLiteral)
{
- StructLiteralExp *sle = e->e1->isStructLiteralExp ()->origin;
- gcc_assert (sle != NULL);
+ StructLiteralExp *sle = e->e1->isStructLiteralExp ();
+ if (!this->constp_)
+ {
+ gcc_assert (sle->origin != NULL);
+ sle = sle->origin;
+ }
/* Build the reference symbol, the decl is built first as the
initializer may have recursive references. */
}
else if (tb->ty == TY::Taarray)
{
- /* Allocating memory for a new associative array. */
- tree arg = build_typeinfo (e, e->newtype);
- tree mem = build_libcall (LIBCALL_AANEW, Type::tvoidptr, 1, arg);
-
- /* Return an associative array pointed to by MEM. */
- tree aatype = build_ctype (tb);
- vec <constructor_elt, va_gc> *ce = NULL;
- CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
-
- result = build_nop (build_ctype (e->type),
- build_padded_constructor (aatype, ce));
+ /* Allocating memory for a new associative array has already been
+ handled by the front-end. */
+ gcc_assert (e->lowering);
+ result = build_expr (e->lowering);
}
else
gcc_unreachable ();
else
{
/* Allocate space on the memory managed heap. */
- tree mem = build_libcall (LIBCALL_ARRAYLITERALTX,
- dmd::pointerTo (etype), 2,
- build_typeinfo (e, dmd::arrayOf (etype)),
- size_int (e->elements->length));
+ gcc_assert (e->lowering);
+ tree mem = build_nop (build_pointer_type (satype),
+ build_expr (e->lowering));
mem = d_save_expr (mem);
/* Now copy the constructor into memory. */
void visit (AssocArrayLiteralExp *e) final override
{
- if (this->constp_ && e->lowering != NULL)
+ if (this->constp_ && e->loweringCtfe != NULL)
{
/* When an associative array literal gets lowered, it's converted into a
struct literal suitable for static initialization. */
- this->result_ = build_expr (e->lowering, this->constp_, true);
- return ;
+ this->result_ = build_expr (e->loweringCtfe, this->constp_, true);
+ return;
}
- /* Want the mutable type for typeinfo reference. */
- Type *tb = dmd::mutableOf (e->type->toBasetype ());
-
/* Handle empty assoc array literals. */
- TypeAArray *ta = tb->isTypeAArray ();
if (e->keys->length == 0)
{
- this->result_ = build_padded_constructor (build_ctype (ta), NULL);
+ this->result_ = build_padded_constructor (build_ctype (e->type), NULL);
return;
}
- /* Build an expression that assigns all expressions in KEYS
- to a constructor. */
- Type *tkarray = dmd::sarrayOf (ta->index, e->keys->length);
- tree akeys = build_array_from_exprs (tkarray, e->keys, this->constp_);
- tree init = stabilize_expr (&akeys);
-
- /* Do the same with all expressions in VALUES. */
- Type *tvarray = dmd::sarrayOf (ta->next, e->values->length);
- tree avals = build_array_from_exprs (tvarray, e->values, this->constp_);
- init = compound_expr (init, stabilize_expr (&avals));
-
/* Generate: _d_assocarrayliteralTX (ti, keys, vals); */
- tree keys = d_array_value (build_ctype (dmd::arrayOf (ta->index)),
- size_int (e->keys->length),
- build_address (akeys));
- tree vals = d_array_value (build_ctype (dmd::arrayOf (ta->next)),
- size_int (e->values->length),
- build_address (avals));
-
- tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3,
- build_typeinfo (e, ta), keys, vals);
-
- /* Return an associative array pointed to by MEM. */
- tree aatype = build_ctype (ta);
- vec <constructor_elt, va_gc> *ce = NULL;
- CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
-
- tree result = build_nop (build_ctype (e->type),
- build_padded_constructor (aatype, ce));
- this->result_ = compound_expr (init, result);
+ gcc_assert (e->lowering);
+ this->result_ = build_expr (e->lowering);
}
/* Build a struct literal. */
/* Alias symbols aren't imported, but their targets are. */
void visit (AliasDeclaration *d) final override
{
- Dsymbol *dsym = d->toAlias ();
+ Dsymbol *dsym = dmd::toAlias (d);
if (dsym == d)
{
DEF_D_RUNTIME (CALLINTERFACEFINALIZER, "_d_callinterfacefinalizer", RT(VOID),
P1(VOIDPTR), 0)
-/* Used for casting to a class or interface. */
-DEF_D_RUNTIME (DYNAMIC_CAST, "_d_dynamic_cast", RT(OBJECT),
- P2(OBJECT, CLASSINFO), 0)
-DEF_D_RUNTIME (INTERFACE_CAST, "_d_interface_cast", RT(OBJECT),
- P2(OBJECT, CLASSINFO), 0)
-
-/* Used for allocating an array literal on the GC heap. */
-DEF_D_RUNTIME (ARRAYLITERALTX, "_d_arrayliteralTX", RT(VOIDPTR),
- P2(CONST_TYPEINFO, SIZE_T), 0)
-
-/* Used for value equality (x == y) and comparisons (x < y) of non-trivial
- arrays. Such as an array of structs or classes. */
-DEF_D_RUNTIME (ADEQ2, "_adEq2", RT(INT),
- P3(ARRAY_VOID, ARRAY_VOID, CONST_TYPEINFO), 0)
-
/* Used for (array.length = n) expressions. The `i' variant is for when the
initializer is nonzero. */
DEF_D_RUNTIME (ARRAYSETLENGTHT, "_d_arraysetlengthT", RT(ARRAY_VOID),
DEF_D_RUNTIME (ARRAYAPPENDWD, "_d_arrayappendwd", RT(ARRAY_VOID),
P2(ARRAYPTR_BYTE, DCHAR), 0)
-/* Used for allocating a new associative array. */
-DEF_D_RUNTIME (ASSOCARRAYLITERALTX, "_d_assocarrayliteralTX", RT(VOIDPTR),
- P3(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID), 0)
-DEF_D_RUNTIME (AANEW, "_aaNew", RT(VOIDPTR), P1(CONST_TYPEINFO), 0)
-
-/* Used for value equality of two associative arrays. */
-DEF_D_RUNTIME (AAEQUAL, "_aaEqual", RT(INT),
- P3(CONST_TYPEINFO, ASSOCARRAY, ASSOCARRAY), 0)
-
-/* Used to determine is a key exists in an associative array. */
-DEF_D_RUNTIME (AAINX, "_aaInX", RT(VOIDPTR),
- P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0)
-
-/* Used to retrieve a value from an associative array index by a key. The
- `Rvalue' variant returns null if the key is not found, where as aaGetY
- will create new key entry for assignment. */
-DEF_D_RUNTIME (AAGETY, "_aaGetY", RT(VOIDPTR),
- P4(POINTER_ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0)
-DEF_D_RUNTIME (AAGETRVALUEX, "_aaGetRvalueX", RT(VOIDPTR),
- P4(ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0)
-
-/* Used when calling delete on a key entry in an associative array. */
-DEF_D_RUNTIME (AADELX, "_aaDelX", RT(BOOL),
- P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0)
-
/* Used for throw() expressions. */
DEF_D_RUNTIME (THROW, "_d_throw", RT(VOID), P1(OBJECT), ECF_NORETURN)
DEF_D_RUNTIME (BEGIN_CATCH, "__gdc_begin_catch", RT(VOIDPTR), P1(VOIDPTR), 0)
void *__monitor;
TypeInfo value;
TypeInfo key;
- TypeInfo entry; */
+ TypeInfo entry;
+ bool function(in void*, in void*) xopEquals;
+ hash_t function(in void*) xtoHash; */
void visit (TypeInfoAssociativeArrayDeclaration *d) final override
{
this->layout_field (build_typeinfo (d->loc, d->entry));
else
this->layout_field (null_pointer_node);
+
+ /* bool function(in void*, in void*) xopEquals; */
+ tree xeq = (d->xopEqual) ? build_address (get_symbol_decl (d->xopEqual))
+ : null_pointer_node;
+ this->layout_field (xeq);
+
+ /* hash_t function (in void*) xtoHash; */
+ tree xhash = (d->xtoHash) ? build_address (get_symbol_decl (d->xtoHash))
+ : null_pointer_node;
+ this->layout_field (xhash);
}
/* Layout of TypeInfo_Vector is:
error_at (make_location_t (loc),
"%<object.TypeInfo%> cannot be used with %<-fno-rtti%>");
+ if (expr != NULL || !warned)
+ {
+ /* Print the location of where the error came from. */
+ if (sc && sc->tinst)
+ dmd::printInstantiationTrace (sc->tinst);
+
+ global.errors++;
+ }
+
warned = 1;
}
}
{
ident = Identifier::idPool ("TypeInfo_AssociativeArray");
make_internal_typeinfo (tk, ident, ptr_type_node, ptr_type_node,
+ ptr_type_node, ptr_type_node,
ptr_type_node, NULL);
}
t->vtinfo = sc && have_typeinfo_p (Type::typeinfoassociativearray)
/* For structs with a user defined postblit, copy constructor, or a
destructor, also set TREE_ADDRESSABLE on the type and all variants.
This will make the struct be passed around by reference. */
- if (!t->sym->isPOD ())
+ if (!dmd::isPOD (t->sym))
{
for (tree tv = t->ctype; tv != NULL_TREE; tv = TYPE_NEXT_VARIANT (tv))
{
{
asm
{
- ""h; // { dg-error "found 'h' when expecting ':'" }
+ ""h; // { dg-error "found 'h' when expecting ';'" }
}
}
"" : : "g" (1 ? 2 : 3);
"" : : "g" (1 ? 2 : :) 3;
// { dg-error "expression expected, not ':'" "" { target *-*-* } .-1 }
- // { dg-error "expected constant string constraint for operand" "" { target *-*-* } .-2 }
+ // { dg-error "found '3' when expecting ';'" "" { target *-*-* } .-2 }
}
}
void parse3()
{
- asm { "" [; } // { dg-error "found '\\\[' when expecting ':'" }
+ asm { "" [; } // { dg-error "found '\\\[' when expecting ';'" }
}
void parse4()
int expr;
asm
{
- "%name" : [name] string (expr); // { dg-error "expected constant string constraint for operand, not 'string'" }
+ "%name" : [name] string (expr); // { dg-error "expected string literal or expression in parentheses" }
}
}
{
asm
{
- "cpuid" : : "a" a; // { dg-error "'a' must be surrounded by parentheses" }
+ "cpuid" : : "a" a; // { dg-error "found 'a' when expecting '\\\('" }
}
}
module object;
+class Object {}
+class TypeInfo {}
+class TypeInfo_AssociativeArray : TypeInfo {} // { dg-note "defined here" }
+
extern(C) int main()
{
- int[int] aa; // { dg-error ".object.TypeInfo. could not be found" }
- aa[0] = 1; // { dg-error ".object.TypeInfo. could not be found, but is implicitly used" }
+ int[int] aa; // { dg-error "no property" }
+ aa[0] = 1; // { dg-error "'object._d_aaGetY' not found" }
return 0;
}
// { dg-options "-fno-rtti" }
// { dg-shouldfail "expressions depend on TypeInfo" }
-int* testInExp(int key, int[int] aa)
+module object;
+
+class Object {}
+class TypeInfo {}
+
+struct AA(K, V)
{
- return key in aa; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
+ this(int sz) nothrow
+ {
+ keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" }
+ }
+ TypeInfo keyTI;
}
-bool testAAEqual(int[string] aa1, int[string] aa2)
+auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key)
+{
+ auto aa = *(cast(inout(AA!(K, V))*)&a); // { dg-note "instantiated from here" }
+ return null;
+}
+
+int* testInExp(int key, int[int] aa)
{
- return aa1 == aa2; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
+ return key in aa; // { dg-note "instantiated from here" }
}
--- /dev/null
+// { dg-do compile }
+// { dg-options "-fno-rtti" }
+// { dg-shouldfail "expressions depend on TypeInfo" }
+
+module object;
+
+class Object {}
+class TypeInfo {}
+class TypeInfo_Array : TypeInfo {}
+
+struct AA(K, V)
+{
+ this(int sz) nothrow
+ {
+ keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" }
+ }
+ TypeInfo keyTI;
+}
+
+bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2)
+{
+ auto aa1 = *(cast(const(AA!(K, V))*)&a1); // { dg-note "instantiated from here" }
+ return false;
+}
+
+bool testAAEqual(int[immutable(char)[]] aa1, int[immutable(char)[]] aa2)
+{
+ return aa1 == aa2; // { dg-note "instantiated from here" }
+}
Bar14986 test14986()
{
- Foo14986[] types;
+ Foo14986[] types = [Foo14986(1)];
auto a1 = new void[types[0].tsize]; // TypeIdentifier::toExpression
auto a2 = new void[Id14986!types[0].tsize]; // TypeInstance::toExpression
{
this(int[1] x) scope {}
this(int[2] x) return scope {}
- this(int[3] x) scope return {}
+ this(int[3] x) return ref scope {}
this(int[4] x) return {}
@safe:
{
int i12344(int x) in(x > 0) out(result) {assert(result > 0);};
}
+
+debug enum issue21406 = 1;
--- /dev/null
+struct S(alias fun) {
+ int foo() {
+ int r = fun(0);
+ static foreach (i; 0..2)
+ r += (x => 2)(0);
+ return r;
+ }
+}
+
+int bar() {
+ int r;
+ static foreach (i; 0..2)
+ r += S!(x => 1)().foo();
+ return r;
+}
+
+// nm test21331.o | grep __lambda_L5_C19
--- /dev/null
+void registerAll() {
+ import test21359b;
+}
--- /dev/null
+struct SumType(Types...)
+{
+ static foreach (T; Types)
+ T values_;
+}
+
+SumType!(string[int]) convertObserver;
+
+void hackAroundLinkerError() {
+ auto t = typeid(const(immutable(char)[][]));
+}
--- /dev/null
+int f20184(void);
enum int4 F = D * E;
static assert(F.array == [1, 4, 9, 16]);
}
+
+// https://github.com/dlang/dmd/issues/20114
+
+int* find(int[] arr, int needle)
+{
+ foreach(ref a; arr)
+ if(a == needle)
+ return &a;
+ return null;
+}
+
+enum int[int] aa = [0: 0];
+enum int[] da = [0, 1, 2];
+static assert(0 in aa);
+static assert(&da[1]);
+static assert(find(da, 1));
+static assert(!find(da, 3));
int test1(mixin("int")* p)
{
- mixin("int")[] a;
- mixin("int[]") b;
- mixin("int[] c;");
+ mixin("int")[] a = [1];
+ mixin("int[]") b = [1];
+ mixin("int[] c = [1];");
mixin("*p = c[0];");
*p = mixin("c[0]");
return *p + a[0] + b[0] + c[0];
*(x)++=0;
(*(x)--)=0;
(*x++)=0; // ok
- int*[] z;
+ int a = 1, b = 2;
+ int*[] z = [&a, &b];
*(z[0])++=0; //ok
(y[0])--;
*x++=0;
{
int b;
(b)++;
- int[] a;
+ int[] a = [1];
b = (a)[0]++; //ok
(a[0])--;
b = (int).init; //ok
pure nothrow @nogc ref @safe shared(int)(return ref shared(C3) c)
---
*/
-ref shared(int) f(return shared ref int y)
+ref shared(int) f(return ref shared int y)
{
return y;
}
return &c.c2;
}
-ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+ref shared(int) test_dotvarexp_5(return ref shared C3 c)
{
return c.c1.c1.value;
}
void test14838() pure nothrow @nogc @safe
{
- C c;
+ scope C c = new C;
c.__xdtor(); // C.~this() will also be inferred to
// pure nothrow @nogc @safe
\r
void foo()\r
{\r
- S[] s;\r
+ S[] s = [S([],0)];\r
if (s[$-1] == S.init) {}\r
}\r
\r
void test20989() @safe
{
uint[] arr = [1, 2, 3];
+ uint[] arr2 = null;
assert(arr.ptr);
- assert(!arr.ptr);
+ assert(!arr2.ptr);
assert(arr.ptr is arr.ptr);
+ assert(arr2.ptr is arr2.ptr);
}
// https://issues.dlang.org/show_bug.cgi?id=21765
--- /dev/null
+// EXTRA_FILES: imports/h20184.h
+import imports.h20184;
+
+alias fun_signature = extern (C) int();
+static assert(is(typeof(f20184) : fun_signature));
--- /dev/null
+// REQUIRED_ARGS: -preview=bitfields
+
+struct A { int x : 16, y : 16; }
+
+void autoref_assign(A a)
+{
+ auto ref int x = a.x;
+}
+
+void f()(auto ref int);
+
+void autoref_param(A a)
+{
+ f(a.y);
+}
+
+auto ref int autoref_return(ref A a)
+{
+ return a.y;
+}
--- /dev/null
+#!/usr/bin/env bash
+
+objfile="${OUTPUT_BASE}${OBJ}"
+$DMD -c -m${MODEL} -of${objfile} ${EXTRA_FILES}/${TEST_NAME}.d
+
+nm ${objfile} | grep -E '16__lambda_L13_C17.+15__lambda_L5_C19'
+nm ${objfile} | grep -E '16__lambda_L13_C17.+17__lambda_L5_C19_1'
+nm ${objfile} | grep -E '18__lambda_L13_C17_1.+15__lambda_L5_C19'
+nm ${objfile} | grep -E '18__lambda_L13_C17_1.+17__lambda_L5_C19_1'
+
+rm_retry ${objfile}
--- /dev/null
+#!/usr/bin/env bash
+
+objfile="${OUTPUT_BASE}${OBJ}"
+$DMD -c -m${MODEL} -allinst -of${objfile} ${EXTRA_FILES}/${TEST_NAME}a.d -I${EXTRA_FILES}
+
+# This test is brittle and might break in the future.
+# For now, ensure a specific AA TypeInfo isn't defined multiple times, as happened with DMD v2.111.0.
+mangled_sym='_D14TypeInfo_HiAya6__initZ'
+num_typeinfo_syms=$(nm --defined-only ${objfile} | grep -F ${mangled_sym} | wc -l)
+if [[ "${num_typeinfo_syms}" -ne 1 ]]; then
+ echo "Expected 1 '${mangled_sym}' symbol definition but got: ${num_typeinfo_syms}"
+ exit 1
+fi
+
+rm_retry ${objfile}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21416
+
+struct S
+{
+ @disable this(ref S);
+}
+
+ref id(ref S s) => s;
+
+S* p;
+void foo(S s) { }
+
+void main()
+{
+ S s;
+ foo(__rvalue(id(s)));
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21476
+
+struct S21476
+{
+ string field;
+ this(ref return scope S21476);
+ this(return scope S21476);
+}
+
+void test21476()
+{
+ auto o = S21476("aoe");
+}
{
int* impl;
void* fun0() return scope {return impl;}
- void* fun1() scope return {return impl;}
+ void* fun1() scope return ref {return impl;}
void* fun2() return {return &this;}
}
// https://issues.dlang.org/show_bug.cgi?id=3004
/*
REQUIRED_ARGS: -ignore -v
-TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|\(imported|semantic|entry|library|function object|function core|\s*$)")
+TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|inline|.*_d_newarrayU|import|\(imported|semantic|entry|library|function object|function core|\s*$)")
TEST_OUTPUT:
---
pragma GNU_attribute (__error)
return;
}
}
+
+// https://github.com/dlang/dmd/issues/18018
+int main ()
+{
+ string[string] aa;
+ goto A; // line 4
+ aa["X"] = "Y"; // line 5
+A:
+ return 0;
+}
int i;
int* ptr;
- int* wannabeReturnRef() scope return
+ int* wannabeReturnRef() return ref scope
{
return &i;
}
static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system"));
static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system"));
- static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "return", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "return", "@system"));
+ static assert(__traits(getFunctionAttributes, S.refF) == tuple!("return", "ref", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("return", "ref", "@system"));
static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system"));
static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system"));
OUTPUT_FILES: compilable/vcg-ast.d.cg
EXTRA_FILES: imports/vcg_ast_import.d
TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg
+// size_t currently must be ulong in this test, not uint
+DISABLED: freebsd32 openbsd32 linux32 osx32 win32
*/
module vcg;
{
}
}
+
+import core.checkedint;
+
+// check inlining of checkedint with -wi
+T testCheckedSigned(T)(T x, T y)
+{
+ bool overflow;
+ T z = adds(x, y, overflow);
+ z = subs(z, x, overflow);
+ z = muls(z, x, overflow);
+ z = negs(z, overflow);
+ return z;
+}
+
+T testCheckedUnsigned(T)(T x, T y)
+{
+ bool overflow;
+ T z = addu(x, y, overflow);
+ z = subu(z, x, overflow);
+ z = mulu(z, x, overflow);
+ return z;
+}
+
+void testCkeckedInt()
+{
+ assert(testCheckedSigned!int(3,4) == -12);
+ assert(testCheckedSigned!long(3,4) == -12);
+ static if (is(cent))
+ assert(testCheckedSigned!cent(3,4) == -12);
+
+ bool overflow;
+ assert(mulu(cast(long)3, cast(uint)4, overflow) == 12);
+
+ assert(testCheckedUnsigned!uint(3,4) == 12);
+ assert(testCheckedUnsigned!ulong(3,4) == 12);
+ static if (is(ucent))
+ assert(testCheckedUnsigned!ucent(3,4) == 12);
+}
--- /dev/null
+/* TEST_OUTPUT:
+---
+fail_compilation\aa_assign.d(11): Error: associative arrays can only be assigned values with immutable keys, not `char[]`
+---
+*/
+
+void test_mutable_key()
+{
+ int[char[]] aa;
+ char[] str = "123".dup;
+ aa[str] = 3;
+ assert(str in aa);
+}
--- /dev/null
+/* TEST_OUTPUT:
+TRANSFORM_OUTPUT: remove_lines(called from here)
+---
+fail_compilation\aaerrors.d-mixin-29(29): Error: `assert(aai[1] == 0)` failed
+fail_compilation\aaerrors.d-mixin-30(30): Error: `assert((aai[1] = 1) == 0)` failed
+fail_compilation\aaerrors.d-mixin-31(31): Error: `assert(*(1 in aai) == 3)` failed
+fail_compilation\aaerrors.d-mixin-32(32): Error: `assert(aai.remove(2))` failed
+fail_compilation\aaerrors.d-mixin-33(33): Error: `assert(aai != [1:2])` failed
+fail_compilation\aaerrors.d-mixin-34(34): Error: `assert(aai == [1:3])` failed
+fail_compilation\aaerrors.d-mixin-41(41): Error: `assert(aas[1].x == 0)` failed
+fail_compilation\aaerrors.d-mixin-42(42): Error: `assert((aas[1] = 1).x == 0)` failed
+fail_compilation\aaerrors.d-mixin-43(43): Error: `assert((*(1 in aas)).x == 0)` failed
+---
+*/
+
+
+struct S
+{
+ int x;
+ this(int _x){ x = _x; }
+ ref S opAssign(int _x){ x = _x; return this; }
+}
+
+string gentest_ii(string expr)
+{
+ return "() { int[int] aai = [ 1 : 2 ]; assert(" ~ expr ~ ");\n return true; }()\n";
+}
+
+const ii1 = mixin(gentest_ii("aai[1] == 0"));
+const ii2 = mixin(gentest_ii("(aai[1] = 1) == 0"));
+const ii3 = mixin(gentest_ii("*(1 in aai) == 3"));
+const ii4 = mixin(gentest_ii("aai.remove(2)"));
+const ii5 = mixin(gentest_ii("aai != [1:2]"));
+const ii6 = mixin(gentest_ii("aai == [1:3]"));
+
+string gentest_is(string expr)
+{
+ return "() { S[int] aas = [ 1 : S(2) ]; assert(" ~ expr ~ ");\n return true; }()\n";
+}
+
+const is1 = mixin(gentest_is("aas[1].x == 0"));
+const is2 = mixin(gentest_is("(aas[1] = 1).x == 0"));
+const is3 = mixin(gentest_is("(1 in aas).x == 0"));
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
-fail_compilation/biterrors.d(103): Error: initializer not allowed for bit-field declaration
-fail_compilation/biterrors.d(104): Error: storage class not allowed for bit-field declaration
+fail_compilation/biterrors.d(103): Error: initializer not allowed for bitfield declaration
+fail_compilation/biterrors.d(104): Error: storage class not allowed for bitfield declaration
---
*/
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
-fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bit-field must be member of struct, union, or class
-fail_compilation/biterrors2.d(104): Error: bit-field `b` has zero width
-fail_compilation/biterrors2.d(105): Error: bit-field type `float` is not an integer type
+fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bitfield must be member of struct, union, or class
+fail_compilation/biterrors2.d(104): Error: bitfield `b` has zero width
+fail_compilation/biterrors2.d(105): Error: bitfield type `float` is not an integer type
---
*/
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
-fail_compilation/biterrors3.d(103): Error: storage class not allowed for bit-field declaration
+fail_compilation/biterrors3.d(103): Error: storage class not allowed for bitfield declaration
fail_compilation/biterrors3.d(106): Error: expected `,` or `=` after identifier, not `:`
fail_compilation/biterrors3.d(106): Error: found `:` when expecting `,`
fail_compilation/biterrors3.d(106): Error: found `3` when expecting `identifier`
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
-fail_compilation/biterrors4.d(109): Error: cannot take address of bit-field `a`
+fail_compilation/biterrors4.d(109): Error: cannot take address of bitfield `s.a`
---
*/
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
-fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S`
-fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i`
+fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not struct `biterrors5.S`
+fail_compilation/biterrors5.d(26): Error: bitfield symbol expected not variable `biterrors5.test0.i`
+fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.x`
+fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.y`
---
*/
i = __traits(getBitfieldOffset, S);
i = __traits(getBitfieldOffset, i);
}
+
+/****************************************/
+
+struct B { int x: 3; int y: 5; }
+
+void test1(int val, int choice, B a)
+{
+ (choice ? a.x : a.y) = val;
+}
/*
TEST_OUTPUT:
---
-fail_compilation/diag13320.d(14): Error: operator `++` not supported for `f` of type `Foo`
-fail_compilation/diag13320.d(9): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}`
+fail_compilation/diag13320.d(20): Error: operator `++` not supported for `f` of type `Foo`
+fail_compilation/diag13320.d(14): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}`
+fail_compilation/diag13320.d(21): Error: expression `f` of type `Foo` does not have a boolean value
+fail_compilation/diag13320.d(14): perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`
+fail_compilation/diag13320.d(22): Error: expression `Foo()` of type `E` does not have a boolean value
+fail_compilation/diag13320.d(14): perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`
---
*/
+
struct Foo {}
+enum E : Foo { a = Foo.init }
void main()
{
Foo f;
++f;
+ if (f) {}
+ assert(E.a);
}
/* TEST_OUTPUT:
---
-fail_compilation/diag16976.d(44): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(45): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(46): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(47): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(48): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(49): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(50): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(51): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(52): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(53): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(54): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(55): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(56): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(57): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(58): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(59): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(65): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(66): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(67): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(68): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(69): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(70): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(71): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(72): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(73): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(74): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(75): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(76): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(77): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(78): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(79): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(80): Error: foreach: key cannot be of non-integral type `float`
+fail_compilation/diag16976.d(44): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(45): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(46): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(47): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(48): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(49): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(50): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(51): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(52): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(53): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(54): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(55): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(56): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(57): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(58): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(59): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(65): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(66): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(67): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(68): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(69): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(70): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(71): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(72): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(73): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(74): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(75): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(76): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(77): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(78): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(79): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(80): Error: foreach: index cannot be of non-integral type `float`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time
-fail_compilation/diag7420.d(21): while evaluating: `static assert(x < 4)`
+fail_compilation/diag7420.d(20): Error: static variable `x` cannot be read at compile time
+fail_compilation/diag7420.d(20): while evaluating: `static assert(x < 4)`
+fail_compilation/diag7420.d(21): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(21): while evaluating: `static assert(y == "abc")`
fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(22): called from here: `__equals(y, "abc")`
-fail_compilation/diag7420.d(22): while evaluating: `static assert(y == "abc")`
+fail_compilation/diag7420.d(22): while evaluating: `static assert(cast(ubyte[])y != null)`
fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(ubyte[])y != null)`
+fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(int)y[0] == 1)`
fail_compilation/diag7420.d(24): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(24): while evaluating: `static assert(cast(int)y[0] == 1)`
-fail_compilation/diag7420.d(25): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(25): while evaluating: `static assert(y[0..1].length == 1u)`
+fail_compilation/diag7420.d(24): while evaluating: `static assert(y[0..1].length == 1u)`
---
*/
fail_compilation/discard_value.d(25): Error: the result of the equality expression `null !is null` is discarded
fail_compilation/discard_value.d(26): Error: the result of the equality expression `v == 0` is discarded
fail_compilation/discard_value.d(27): Error: the result of the equality expression `v == 0` is discarded
-fail_compilation/discard_value.d(28): Error: `!__equals("", "")` has no effect
+fail_compilation/discard_value.d(28): Error: the result of the equality expression `"" != ""` is discarded
fail_compilation/discard_value.d(29): Error: the result of the equality expression `"" == ""` is discarded
fail_compilation/discard_value.d(30): Error: the result of the equality expression `fun().i == 4` is discarded
fail_compilation/discard_value.d(30): note that `fun().i` may have a side effect
/*
TEST_OUTPUT:
---
-fail_compilation/fail13756.d(11): Error: `foreach`: index must be type `const(int)`, not `int`
+fail_compilation/fail13756.d(14): Error: `foreach`: index parameter `ref k` must be type `const(int)`, not `int`
+fail_compilation/fail13756.d(17): Error: cannot implicitly convert expression `__applyArg0` of type `int` to `string`
+fail_compilation/fail13756.d(19): Error: cannot implicitly convert expression `__applyArg1` of type `int` to `char`
+fail_compilation/fail13756.d(20): Error: `foreach`: value parameter `ref val` must be type `int`, not `dchar`
---
*/
foreach (ref int k, v; aa)
{
}
+ foreach (string key, val; aa) {}
+
+ foreach (key, char val; aa) {}
+ foreach (key, ref dchar val; aa) {}
}
--- /dev/null
+/* REQUIRED_ARGS: -preview=bitfields
+TEST_OUTPUT:
+---
+fail_compilation/fail20365.d(16): Error: bitfield `a.x` cannot be assigned to `ref x`
+fail_compilation/fail20365.d(23): Error: function `f` is not callable using argument types `(int)`
+fail_compilation/fail20365.d(23): cannot pass bitfield argument `a.y` to parameter `ref int`
+fail_compilation/fail20365.d(19): `fail20365.f(ref int)` declared here
+fail_compilation/fail20365.d(28): Error: cannot `ref` return bitfield `a.y`
+---
+*/
+
+struct A { int x : 16, y : 16; }
+
+void ref_assign(A a)
+{
+ ref int x = a.x;
+}
+
+void f(ref int);
+
+void ref_param(A a)
+{
+ f(a.y);
+}
+
+ref int ref_return(ref A a)
+{
+ return a.y;
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21414
+
+/* REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+---
+fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function
+---
+*/
+
+@safe:
+
+struct S
+{
+ int x;
+ this(int x)
+ {
+ this.x = x;
+ }
+ ~this() { }
+ this(S s) { }
+}
+
+void foo(S s, immutable(S) t)
+{
+ assert(t.x == 2);
+ s.x = 3;
+ assert(t.x == 2);
+}
+
+ref unsafeMove(T)(ref T arg) __rvalue => arg;
+
+ref id(T)(return ref T arg) => arg;
+
+void main()
+{
+ auto s = S(2);
+ foo(__rvalue(s), __rvalue(s));
+ auto t = S(2);
+ foo(unsafeMove(t), unsafeMove(t));
+ auto u = S(2);
+ foo(__rvalue(id(u)), __rvalue(id(u)));
+}
--- /dev/null
+// REQUIRED_ARGS: -preview=bitfields
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21660.d(29): Error: overlapping initialization for field `b` and `c`
+fail_compilation/fail21660.d(29): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
+fail_compilation/fail21660.d(30): Error: overlapping initialization for field `b` and `c`
+fail_compilation/fail21660.d(30): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `b`
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `d`
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `c` and `d`
+fail_compilation/fail21660.d(54): Error: overlapping initialization for field `a` and `d`
+---
+*/
+
+struct S
+{
+ uint a : 1;
+ union {
+ uint b : 2;
+ struct {
+ uint c : 3;
+ }
+ }
+}
+
+void testS()
+{
+ S s = S(1, 2, 3); // b + c overlap
+ S t = S(a:1, b:2, c:3); // b + c overlap
+ S u = S(a:1, c:3); // ok
+}
+
+union U
+{
+ union {
+ uint a : 5;
+ uint b : 4;
+ }
+ struct {
+ uint : 5;
+ uint c : 11;
+ }
+ struct {
+ uint : 4;
+ uint d : 12;
+ }
+}
+
+void testU()
+{
+ U s = U(1, 2, 3, 4); // a + b, a + d, c + d overlap
+ U t = U(a:1, c:3); // ok
+ U u = U(a:1, d:2); // a + d overlap
+ U v = U(b:1, c:3); // ok
+ U w = U(b:1, d:2); // ok
+}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21693.d(15): Error: function `fail` is not callable using argument types `(uint)`
+fail_compilation/fail21693.d(15): cannot pass rvalue argument `__rvalue(s.x)` of type `uint` to parameter `ref uint`
+fail_compilation/fail21693.d(11): `fail21693.fail(ref uint)` declared here
+fail_compilation/fail21693.d(16): Error: rvalue `__rvalue(s.x)` cannot be assigned to `ref x`
+---
+*/
+struct S { uint x; }
+void fail(ref uint);
+
+void test21693(S s)
+{
+ fail(__rvalue(s.x));
+ ref x = __rvalue(s.x);
+}
// EXTRA_FILES: imports/imp22749.c
/* TEST_OUTPUT:
---
-fail_compilation/fail22749.d(12): Error: cannot take address of bit-field `field`
+fail_compilation/fail22749.d(12): Error: cannot take address of bitfield `s.field`
---
*/
import imports.imp22749;
/*
TEST_OUTPUT:
---
-fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(19): Error: taking the address of local variable `i` is not allowed in a `@safe` function
---
*/
int n;
auto b = &(0 ? n : n);
}
+
+void f() @safe
+{
+ ref i = *new int;
+ auto b = &i;
+}
fail_compilation/fail6795.d(22): Error: cannot modify expression `[0][0]` because it is not an lvalue
fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue
fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue
-fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue
fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue
---
*/
+
void test_wrong_line_num()
{
enum int[1] sa = [0];
aa[0] /= 3;
auto ps = &sa[0];
- auto pa = &aa[0];
+ auto pa = &aa[0]; // ok with AA lowering, just as `pa = 0 in aa`
// https://issues.dlang.org/show_bug.cgi?id=24845
enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] }
// https://issues.dlang.org/show_bug.cgi?id=22108
-@safe ref int test(ref scope return int* p)
+@safe ref int test(return ref scope int* p)
{
return *p;
}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/foreach_seq.d(25): Error: only one (element) or two (index, element) arguments allowed for sequence `foreach`, not 3
+fail_compilation/foreach_seq.d(26): Error: invalid storage class `ref` for index `i`
+fail_compilation/foreach_seq.d(27): Error: foreach: index cannot be of non-integral type `void`
+fail_compilation/foreach_seq.d(28): Error: index type `bool` cannot cover index range 0..3
+fail_compilation/foreach_seq.d(29): Error: `foreach` loop variable cannot be both `enum` and `alias`
+fail_compilation/foreach_seq.d(30): Error: constant value `1` cannot be `ref`
+fail_compilation/foreach_seq.d(31): Error: invalid storage class `enum` for index `i`
+fail_compilation/foreach_seq.d(33): Error: invalid storage class `ref` for element `e`
+fail_compilation/foreach_seq.d(34): Error: symbol `object` cannot be `ref`
+fail_compilation/foreach_seq.d(35): Error: cannot specify element type for symbol `e`
+fail_compilation/foreach_seq.d(36): Error: cannot specify element type for symbol `object`
+fail_compilation/foreach_seq.d(37): Error: invalid storage class `enum` for element `e`
+---
+*/
+
+// test semantic errors on foreach parameters
+void main()
+{
+ alias AliasSeq(A...) = A;
+ alias seq = AliasSeq!(1, 2, 3);
+
+ foreach (a, b, c; seq) {}
+ foreach (ref i, e; seq) {}
+ foreach (void i, e; seq) {}
+ foreach (bool i, e; seq) {}
+ foreach (enum alias e; seq) {}
+ foreach (ref enum e; seq) {}
+ foreach (ref enum i, e; seq) {}
+
+ foreach (ref e; AliasSeq!int) {}
+ foreach (ref e; AliasSeq!object) {}
+ foreach (int e; AliasSeq!int) {}
+ foreach (int e; AliasSeq!object) {}
+ foreach (enum e; AliasSeq!int) {}
+}
TEST_OUTPUT:
---
fail_compilation/ice10938.d(14): Error: no property `opts` for `this` of type `ice10938.C`
-fail_compilation/ice10938.d(19): Error: forward reference to inferred return type of function call `this.opDispatch()`
+fail_compilation/ice10938.d(19): Error: can't infer return type in function `opDispatch`
fail_compilation/ice10938.d(14): Error: template instance `ice10938.C.opDispatch!"opts"` error instantiating
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/ice13644.d(22): Error: foreach: key cannot be of non-integral type `string`
+fail_compilation/ice13644.d(22): Error: foreach: index cannot be of non-integral type `string`
---
*/
--- /dev/null
+/+
+TEST_OUTPUT:
+---
+fail_compilation/issue21630.d(14): Error: cannot declare `enum` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(15): Error: cannot declare `alias` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(16): Error: cannot declare `enum` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(17): Error: cannot declare `alias` loop variables for non-unrolled foreach
+---
++/
+
+void main()
+{
+ enum a = [1, 2, 3];
+ foreach(enum i; a) { } // error
+ foreach(alias i; a) { } // error
+ foreach(enum i; 0 .. 3) { } // error
+ foreach(alias i; 0 .. 3) { } // error
+}
--- /dev/null
+/*
+REQUIRED_ARGS: -preview=in -preview=dip1000
+TEST_OUTPUT:
+----
+fail_compilation/previewin3.d(2): Error: function `foo` is not callable using argument types `(int)`
+fail_compilation/previewin3.d(2): cannot pass argument `42` of type `int` to parameter `in WithDtor`
+fail_compilation/previewin3.d(8): `previewin3.foo(in WithDtor)` declared here
+----
+ */
+
+#line 1
+void rvalueErrorMsg () {
+ foo(42);
+}
+
+// Add a dtor to ensure things are passed by ref
+struct WithDtor { ~this() @safe pure nothrow @nogc {} }
+
+void foo(in WithDtor) {}
#line 100
-int* foo(ref scope return int* p);
-int* foo(out scope return int* p);
+int* foo(return ref scope int* p);
+int* foo(return out scope int* p);
-int* bar(scope return int* p);
-int* abc(ref return int* p);
+int* bar(return scope int* p);
+int* abc(return ref int* p);
void test()
{
struct S700
{
- @safe S700* get1() scope return
+ @safe S700* get1() return ref scope
{
return &this;
}
}
// First level DotVarExp dereferencing
-ref shared(int) test_dotvarexp_4(return shared ref C2 c)
+ref shared(int) test_dotvarexp_4(return ref shared C2 c)
{
return c.c1.value;
}
// Second level DotVarExp dereferencing
-ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+ref shared(int) test_dotvarexp_5(return ref shared C3 c)
{
return c.c1.c1.value;
}
// Inference tests
// Fails because no `ref`
-auto test_inference_1(return shared ref C3 c)
+auto test_inference_1(return ref shared C3 c)
{
return c;
}
}
// Fails because `const` conversion
-auto ref Object test_inference_4(const return shared ref Object a)
+auto ref Object test_inference_4(const return ref shared Object a)
{
return a;
}
return &foo(s);
}
-ref int identity(ref return int x) {return x;}
-ref int* identityPtr(ref return int* x) {return x;}
+ref int identity(return ref int x) {return x;}
+ref int* identityPtr(return ref int* x) {return x;}
int* addrOfRefEscape()
{
/*
* TEST_OUTPUT:
---
-fail_compilation/test15704.d(17): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function
-fail_compilation/test15704.d(18): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function
-fail_compilation/test15704.d(19): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release
+fail_compilation/test15704.d(19): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function
+fail_compilation/test15704.d(20): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function
+fail_compilation/test15704.d(21): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release
+fail_compilation/test15704.d(22): Error: cannot implicitly convert expression `cast(byte)0` of type `byte` to `void[]`
+fail_compilation/test15704.d(22): Error: cannot copy `byte` to `void[]`
---
*/
arr1[] = arr2[]; // overwrites pointers with arbitrary ints
arr1[] = new const(void)[3];
arr1[] = [5];
+ arr1[] = byte(); // filling not allowed
}
string* f() @safe
{
- scope string*[] ls;
+ scope string*[] ls = [null];
return ls[0];
}
auto borrowA() return /*scope inferred*/ { return ptr; }
int* borrowB() return { return ptr; }
- int* borrowC() scope return { return ptr; }
+ int* borrowC() scope return ref { return ptr; }
}
void main()
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21408.d(12): Error: can't infer return type in function `opEquals`
+fail_compilation/test21408.d(19): Error: can't infer return type in function `opEquals`
+---
+*/
+
+struct A {
+ A[] as;
+ auto opEquals(A x) {
+ return as == x.as;
+ }
+}
+
+struct B {
+ B[] as;
+ auto opEquals(B x) {
+ return x == this;
+ }
+}
+
+void main() {}
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21429.d(20): Error: no property `z` for `s` of type `test21429.S`
+fail_compilation/test21429.d(13): struct `S` defined here
+---
+*/
+//make sure this fails properly after fixing bug 21429.d
+template mixinOpDispatch(string id){
+ string opDispatch(string s)() if(s == id){ return id; }
+}
+
+struct S {
+ mixin mixinOpDispatch!"x";
+ mixin mixinOpDispatch!"y";
+}
+
+void main(){
+ S s;
+ auto fail = s.z;
+}
/* REQUIRED_ARGS: -betterC
TEST_OUTPUT:
---
-fail_compilation/test21477.d(103): Error: expression `[1]` uses the GC and cannot be used with switch `-betterC`
+fail_compilation/test21477.d(103): Error: this array literal requires the GC and cannot be used with `-betterC`
---
*/
fail_compilation/test21546.d(113): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `int* delegate() return`
fail_compilation/test21546.d(114): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `immutable(int)* delegate() return`
fail_compilation/test21546.d(115): Error: cannot implicitly convert expression `pi` of type `immutable(int)* delegate() return` to `int* delegate() return`
-fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `int delegate() ref return`
-fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `immutable(int) delegate() ref return`
-fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() ref return` to `int delegate() ref return`
-fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() ref return[]` to `int delegate() ref return[]`
+fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `int delegate() return ref`
+fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `immutable(int) delegate() return ref`
+fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() return ref` to `int delegate() return ref`
+fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() return ref[]` to `int delegate() return ref[]`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=21546
/* REQUIRED_ARGS: -betterC
TEST_OUTPUT:
---
-fail_compilation/test23710.d(111): Error: array concatenation of expression `foo ~ [1, 2, 3]` requires the GC which is not available with -betterC
+fail_compilation/test23710.d(112): Error: array concatenation of expression `foo ~ cast(int[])a` requires the GC which is not available with -betterC
---
*/
// https://issues.dlang.org/show_bug.cgi?id=23710
{
int j;
int[] foo;
+ int[3] a = [1,2,3];
if (0)
{
for (;;)
{
import core.stdc.stdio;
printf("start body\n");
- foo = foo ~ [1,2,3];
+ foo = foo ~ a;
L1:
printf("foo.length = %zu\n", foo.length);
j += foo.length;
#line 100
-int printf(const(char)*, const scope shared return ...);
-int printf(const(char)*, ref out scope immutable shared return ...);
-int printf(const(char)*, ref scope immutable shared return ...);
+int printf(const(char)*, const return scope shared ...);
+int printf(const(char)*, return ref out scope immutable shared ...);
+int printf(const(char)*, return ref scope immutable shared ...);
/*
TEST_OUTPUT:
---
-fail_compilation/verifyhookexist.d(22): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(28): Error: `object.__equals` not found. The current runtime does not support equal checks on arrays, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(29): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(33): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(36): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(41): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(21): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(28): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(32): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(35): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(40): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
---
*/
assert(__traits(getMember, u, "b") == 2);
}
+/********************************************/
+// https://github.com/dlang/dmd/issues/18247
+
+struct S4
+{
+ ulong a:64;
+}
+
+void test4()
+{
+ S4 s;
+ s.a = 1;
+ assert(s.a == 1);
+}
+
/********************************************/
int main()
test1();
test2();
test3();
+ test4();
return 0;
}
auto otherTest(inout ref A block) @nogc {}
void main() {
- B* thingie;
+ B* thingie = new B;
otherTest(*thingie);
}
string[2] str;
refValue(str[n1]);
- int[] da;
+ int[] da = [1];
refValue(da[n2]);
int n; int* p = &n;
/*
EXTRA_FILES: imports/test15777a.d imports/test15777b.d
+REQUIRED_ARGS: -verrors=simple
TEST_OUTPUT:
---
int
foobar7406(T)
int
test7406()
+runnable/foreach5.d(1200): Deprecation: foreach: loop index implicitly converted from `size_t` to `short`
---
*/
{
static assert(is(typeof(k) == const int));
}
+
+ // https://github.com/dlang/dmd/issues/21456
+ foreach (long k, long v; aa)
+ {
+ assert(k == 1 && v == 20);
+ }
+ static struct S
+ {
+ int x, y, z;
+ this(int a) { z = a; }
+ }
+ foreach (long k, S s; aa)
+ {
+ assert(k == 1 && s.z == 20);
+ }
}
/***************************************/
assert(foo17041(cast(int[2]) [10, 20]) == [31, 41]); // test rvalue
}
+/***************************************/
+// https://github.com/ldc-developers/ldc/issues/326
+
+void testLDC326()
+{
+ foreach (short i, dchar c; "ab")
+ {
+ if (c == 'a')
+ assert(i == 0);
+ else if (c == 'b')
+ assert(i == 1);
+ else
+ assert(0);
+ }
+}
+
/***************************************/
int main()
test13756();
test14653();
test17041();
+ testLDC326();
printf("Success\n");
return 0;
////////////////////////////////////////////////////////////////////////
+void test10()
+{
+ double a,b,c;
+ double* pa,pb;
+
+ a = 5;
+ b = a++;
+ assert(a == 6);
+ pa = &a;
+ pb = &b;
+ *pb = (*pa)++;
+}
+
+////////////////////////////////////////////////////////////////////////
+
int main()
{
// All the various integer divide tests
test20574();
test8();
test9();
+ test10();
printf("Success\n");
return 0;
assert(s.m.a == 3);
}
+/*************************************************/
+// https://github.com/dlang/dmd/issues/21549
+
+struct Test9
+{
+ size_t a, b = 3;
+ this(int x)
+ {
+ a = x;
+ }
+}
+
+void new9(ref Test9 p)
+{
+ new(p) Test9(1);
+}
+
+void test9()
+{
+ Test9 t9 = void;
+ new9(t9);
+ assert(t9.a == 1);
+ // assert(t9.b == 3); // Test9.init not copied yet
+}
+
/*************************************************/
int main()
test6();
test7();
test8();
+ test9();
return 0;
}
assert(x24010 == 71);
}
+/**********************************/
+extern (C) struct S21303
+{
+ static bool dtorCalled = false;
+
+ ~this() { dtorCalled = true; }
+}
+
+void test21303()
+{
+ {
+ S21303 s;
+ }
+ assert(S21303.dtorCalled);
+}
+
/**********************************/
int main()
test68();
testPR12012();
test24010();
+ test21303();
printf("Success\n");
return 0;
/////////////////////////////////////////////
+// https://github.com/dlang/dmd/issues/21690
+void testMultiDim()
+{
+ // int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
+ int[int][int] aa2 = ([ 1: [2: 3] ]); // workaround
+ auto aa3 = [ 1: [2: 3] ]; // works, too
+ static auto aa4 = [ 1: [2: 3] ]; // Error: internal compiler error: failed to detect static initialization of associative array
+ assert(aa2 == aa3);
+ assert(aa3 == aa4);
+}
+
+/////////////////////////////////////////////
void main()
{
testEnumInit();
testStaticArray();
testClassLiteral();
+ testMultiDim();
}
--- /dev/null
+// https://github.com/dlang/dmd/issues/17487
+import core.stdc.stdio;
+
+// version = PRINT;
+
+void testKey()
+{
+ version(PRINT) printf("--- testKey\n");
+ auto count = new Count;
+
+ string[S] aa;
+ aa[S(count, 42)] = "foo";
+
+ version(PRINT) printf("--- after init\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 1);
+
+ version(PRINT) printf("---\n");
+ assert(S(count, 42) in aa);
+
+ version(PRINT) printf("--- after in\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 1);
+
+ version(PRINT) printf("---\n");
+ assert(S(new Count, 22) !in aa);
+ version(PRINT) printf("--- after !in\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 1);
+
+ version(PRINT) printf("---\n");
+ aa[S(count, 42)] = "foo2";
+
+ version(PRINT) printf("--- after assign value\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 1);
+ assert(count.copyConstructed == 1); // copy ctor used when adding key, not during lookup
+}
+
+void testValue()
+{
+ version(PRINT) printf("--- testValue\n");
+ auto count = new Count;
+
+ S[int] aa;
+ aa[42] = S(count, 42);
+
+ version(PRINT) printf("--- after init\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 1);
+ assert(count.constructed == 1);
+
+ version(PRINT) printf("---\n");
+ aa[22] = S(count, 22);
+
+ version(PRINT) printf("--- after create by value\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 2);
+ assert(count.constructed == 2);
+
+ version(PRINT) printf("---\n");
+ aa[2] = aa[22];
+
+ version(PRINT) printf("--- after create by copy ctor\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 3);
+ assert(count.copyConstructed == 1);
+
+ version(PRINT) printf("---\n");
+ aa[22] = aa[42];
+
+ version(PRINT) printf("--- after assign by copy\n");
+ version(PRINT) count.print();
+ assert(count.refCount == 3);
+ assert(count.assignedFrom == 1);
+ assert(count.assignedTo == 1);
+
+ version(PRINT) printf("---\n");
+}
+
+void main()
+{
+ testKey();
+ testValue();
+ version(PRINT) printf("--- cleanup\n");
+}
+
+struct Count
+{
+ int constructed;
+ int copyConstructed;
+ int assignedFrom;
+ int assignedTo;
+ int destroyed;
+ int refCount;
+
+ void print()
+ {
+ printf("Constructed: %d times\n", constructed);
+ printf("Copy Constructed: %d times\n", copyConstructed);
+ printf("Assigned from: %d times\n", assignedFrom);
+ printf("Assigned To: %d times\n", assignedTo);
+ printf("Destroyed: %d times\n", destroyed);
+ printf("refCount: %d\n", refCount);
+ }
+}
+
+struct S
+{
+ Count* count;
+ int i;
+ bool destroyed;
+
+ this(Count* count, int i)
+ {
+ this.count = count;
+ this.i = i;
+ ++count.constructed;
+ ++count.refCount;
+ version(PRINT) printf("Constructing: %d, count(%p)\n", i, count);
+ }
+
+ this(ref S rhs)
+ {
+ this.count = rhs.count;
+ this.i = rhs.i;
+
+ ++count.copyConstructed;
+ ++count.refCount;
+ version(PRINT) printf("Copy Constructing: %d, count(%p)\n", i, count);
+ }
+
+ ~this()
+ {
+ version(PRINT) printf("Destroying: %d, count(%p)\n", i, count);
+
+ if(count)
+ {
+ ++count.destroyed;
+ --count.refCount;
+ }
+
+ destroyed = true;
+ }
+
+ void opAssign()(auto ref S rhs)
+ {
+ version(PRINT) printf("Assigning %d, count(%p) to %d, count(%p)\n",
+ rhs.i, rhs.count, this.i, this.count);
+
+ if(this.count)
+ {
+ ++this.count.assignedTo;
+ --this.count.refCount;
+ }
+
+ if(rhs.count)
+ {
+ ++rhs.count.assignedFrom;
+ ++rhs.count.refCount;
+ }
+
+ this.count = rhs.count;
+ this.i = rhs.i;
+ }
+
+ invariant
+ {
+ assert(!destroyed);
+ }
+
+ bool opEquals()(const auto ref S rhs) const {
+ return this.i == rhs.i;
+ }
+
+ size_t toHash() const {
+ return 0;
+ }
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/20473
+// REQUIRED_ARGS: -preview=bitfields
+
+struct S1
+{
+ char[] slice;
+ bool flag:1;
+}
+
+void test1()
+{
+ auto a = S1(['1','2'], false);
+ auto b = S1(['1','2'], true);
+ auto c = S1(['1','2'], true);
+ assert(a != b);
+ assert(b == c);
+ assert(typeid(S1).getHash(&a) != typeid(S1).getHash(&b));
+ assert(typeid(S1).getHash(&b) == typeid(S1).getHash(&c));
+}
+
+struct S2
+{
+ bool flag:1;
+ Object o;
+}
+
+void test2()
+{
+ auto o = new Object;
+ auto a = S2(false, o);
+ auto b = S2(true, o);
+ auto c = S2(true, o);
+ assert(a != b);
+ assert(b == c);
+ assert(typeid(S2).getHash(&a) != typeid(S2).getHash(&b));
+ assert(typeid(S2).getHash(&b) == typeid(S2).getHash(&c));
+}
+
+struct Wrapper3
+{
+ void[] wrapped;
+}
+struct S3
+{
+ bool flag:1;
+ Wrapper3 wrapper;
+}
+
+void test3()
+{
+ auto a = S3(false, Wrapper3([1,2,3]));
+ auto b = S3(true, Wrapper3([1,2,3]));
+ auto c = S3(true, Wrapper3([1,2,3]));
+ assert(a != b);
+ assert(b == c);
+ assert(typeid(S3).getHash(&a) != typeid(S3).getHash(&b));
+ assert(typeid(S3).getHash(&b) == typeid(S3).getHash(&c));
+}
+
+struct Wrapper4
+{
+ Object wrapped;
+}
+struct S4
+{
+ bool flag:1;
+ Wrapper4 wrapper;
+}
+
+void test4()
+{
+ auto o = new Object;
+ auto a = S4(false, Wrapper4(o));
+ auto b = S4(true, Wrapper4(o));
+ auto c = S4(true, Wrapper4(o));
+ assert(a != b);
+ assert(b == c);
+ assert(typeid(S4).getHash(&a) != typeid(S4).getHash(&b));
+ assert(typeid(S4).getHash(&b) == typeid(S4).getHash(&c));
+}
+
+enum Wrapper5 : string
+{
+ empty = ""
+}
+struct S5
+{
+ bool flag:1;
+ Wrapper5 wrapper;
+}
+
+void test5()
+{
+ auto a = S5(false, Wrapper5.empty);
+ auto b = S5(true, Wrapper5.empty);
+ auto c = S5(true, Wrapper5.empty);
+ assert(a != b);
+ assert(b == c);
+ assert(typeid(S5).getHash(&a) != typeid(S5).getHash(&b));
+ assert(typeid(S5).getHash(&b) == typeid(S5).getHash(&c));
+}
+
+void main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+}
--- /dev/null
+mixin template OD(string s)
+{
+
+ string opDispatch(string name)() if (name == s)
+ {
+ return name;
+ }
+}
+
+mixin template ODA(string s)
+{
+ void opDispatch(string name)(int x) if (name == s)
+ {
+ this.val = x;
+ }
+}
+
+struct T
+{
+ mixin OD!"x";
+ mixin OD!"y";
+}
+
+struct TAssign
+{
+ int val;
+ mixin ODA!"x";
+ mixin ODA!"y";
+}
+
+struct U
+{
+ mixin OD!"z";
+}
+
+
+template adder()
+{
+ int opBinary(string s : "+")(int x) { return x; }
+}
+
+template subtracter()
+{
+ int opBinary(string s : "-")(int x) { return -x; }
+}
+
+
+struct Arithmetic
+{
+ mixin adder;
+ mixin subtracter;
+
+}
+
+template adderRight()
+{
+ int opBinaryRight(string s : "+")(int x){ return x; }
+}
+
+
+template subtracterRight()
+{
+ int opBinaryRight(string s: "-")(int x){ return -x; }
+}
+
+struct ArithmeticRight
+{
+ mixin adderRight;
+ mixin subtracterRight;
+}
+
+template mixinOpAssign(string op)
+{
+ void opOpAssign(string s)(int x) if(s == op)
+ {
+ val = x;
+ lastOp = s;
+ }
+}
+
+struct AssignOverloads
+{
+ int val;
+ string lastOp;
+ mixin mixinOpAssign!"+";
+ mixin mixinOpAssign!"-";
+}
+
+
+void main()
+{
+
+ T t;
+ string s = t.x();
+ assert(s == "x");
+ assert(t.y == "y");
+
+ //explicit call should work
+ assert(t.opDispatch!"x" == "x");
+
+
+ //TODO: fix these
+ Arithmetic a;
+ assert((a + 5) == 5);
+ assert((a - 7) == -7);
+
+
+ ArithmeticRight ar;
+ assert((5 + ar) == 5);
+ assert((7 - ar) == -7);
+
+
+ U u;
+ //should work for a single mixin
+ assert(u.z == "z");
+
+ TAssign ta;
+ ta.x = 5;
+ assert(ta.val == 5);
+ ta.y = 10;
+ assert(ta.val == 10);
+
+ AssignOverloads oa;
+ oa += 5;
+ assert(oa.val == 5);
+ assert(oa.lastOp == "+");
+ oa -= 10;
+ assert(oa.val == 10);
+ assert(oa.lastOp == "-");
+
+}
--- /dev/null
+// https://github.com/dlang/dmd/issues/21435
+
+void check_align()
+{
+ pragma(inline, false);
+ int x;
+ (cast(size_t)&x & 3) == 0 || assert(false);
+}
+
+char* getenv_setargv(char* env)
+{
+ auto p = env;
+ auto q = env;
+ auto r = env;
+ auto s = env;
+ auto t = env;
+ auto u = env;
+ auto ptr = env;
+
+ while (1)
+ {
+ auto c = *ptr++;
+ switch (c)
+ {
+ case 0:
+ check_align();
+ return env;
+
+ case 'a':
+ *p++ = 'A';
+ continue;
+
+ case 'b':
+ *q++ = 'B';
+ continue;
+
+ case 'c':
+ *r++ = 'C';
+ continue;
+
+ case 'd':
+ *s++ = 'D';
+ continue;
+
+ case 'e':
+ *t++ = 'E';
+ continue;
+
+ case 'f':
+ *u++ = 'F';
+ continue;
+
+ case 'g':
+ *p++ = 'G';
+ continue;
+
+ case 'h':
+ *p++ = 'H';
+ continue;
+
+ default:
+ *p++ = c;
+ continue;
+ }
+ }
+}
+
+void main()
+{
+ char[10] m = "abcdefghi";
+ auto str = getenv_setargv(m.ptr);
+ str[0] || assert(false);
+}
--- /dev/null
+// REQUIRED_ARGS: -preview=bitfields
+struct S
+{
+ int a : 3;
+ int b : 3;
+}
+
+struct T
+{
+ uint a : 1;
+ union {
+ uint b : 2;
+ struct {
+ uint : 2;
+ uint c : 3;
+ }
+ }
+}
+
+void main()
+{
+ S s = S(1, 2);
+ assert(s.a == 1 && s.b == 2);
+ S t = S(b:1, a:2);
+ assert(t.a == 2 && t.b == 1);
+
+ T u = T(1, 2, 3);
+ assert(u.a == 1 && u.b == 2 && u.c == 3);
+ T v = T(a:1, b:2, c:3);
+ assert(v.a == 1 && v.b == 2 && v.c == 3);
+}
void test5504()
{
- immutable Bug5504 c;
+ immutable Bug5504 c = new immutable Bug5504;
c.foo(10);
c.xx!(int).hoo(10);
- shared Bug5504b d;
+ shared Bug5504b d = new shared Bug5504b;
d.foo(10);
d.xx!(int).hoo(10);
}
// https://github.com/dlang/dmd/issues/21258
void test21258()
{
- alias AliasSeq(TList...) = TList;
+ alias AliasSeq(TList...) = TList;
- struct S { int x; } // use a local type to not generate required TypeInfo elsewhere
+ struct S { int x; } // use a local type to not generate required TypeInfo elsewhere
foreach (T; AliasSeq!(S[int]))
enum E { a = T.init, } // bug report uses bad syntax here, but this crashed, too
}
/************************************************/
+void testEvaluationOrder()
+{
+ static int last;
+ int[int] aa;
+ int[4] arr;
+
+ int seqi(int n, int i, int ln = __LINE__)
+ {
+ n += ln * 100;
+ assert(n > last);
+ last = n;
+ return i;
+ }
+ ref int[int] seqaa(int n, int ln = __LINE__)
+ {
+ n += ln * 100;
+ assert(n > last);
+ last = n;
+ return aa;
+ }
+ seqaa(1)[seqi(2, 0)] = seqi(3, 1); // aa[0] = 1
+ int x = seqaa(1)[seqi(2, 0)]; // x = aa[0]
+ seqaa(1)[seqi(2, 1)] = seqaa(3)[seqi(4, 0)]; // aa[1] = aa[0]
+ assert(seqi(1, 0) in seqaa(2)); // 0 in aa
+
+ // only executed once?
+ auto naa = seqaa(1).dup;
+ auto len = seqaa(1).length;
+ auto keys = seqaa(1).keys;
+ auto values = seqaa(1).values;
+ auto hash = hashOf(seqaa(1));
+ seqaa(1).rehash;
+ seqaa(1).clear;
+
+ version (none)
+ seqaa(1) = [seqi(2, 1) : seqi(3, 1), seqi(4, 2) : seqi(5, 4)]; // aa = [1:1, 2:4]
+ else
+ aa = [1:1, 2:4];
+
+ assert(seqaa(1).remove(seqi(2, 1))); // aa.remove(1)
+
+ assert(seqaa(1) == seqaa(2)); // aa == aa
+ assert(!(seqaa(1) != seqaa(2))); // aa != aa
+}
+
+/************************************************/
+
int main()
{
testaa();
testinout();
testinenum();
test21258();
+ testEvaluationOrder();
printf("Success\n");
return 0;
void aafunc(int[int] aa) {}
+void testTypeinfo()
+{
+ int[int] a = [ 1:1, 2:4, 3:9, 4:16 ];
+ int[int] b = [ 1:1, 2:4, 3:9, 4:16 ];
+ assert(a == b);
+
+ hash_t ahash1 = hashOf(a);
+ hash_t ahash2 = typeid(a).getHash(&a);
+ assert(ahash1 == ahash2);
+ hash_t bhash1 = hashOf(a);
+ hash_t bhash2 = typeid(a).getHash(&a);
+ assert(bhash1 == bhash2);
+ assert(ahash1 == bhash1);
+ assert(typeid(a).equals(&a, &b));
+
+ string[string] c = [ "1":"one", "2":"two", "3":"three" ];
+ hash_t chash1 = hashOf(c);
+ hash_t chash2 = typeid(c).getHash(&c);
+ assert(chash1 == chash2);
+ assert(chash1 != ahash2);
+ assert(typeid(c).equals(&c, &c));
+}
+
/***************************************************/
// https://issues.dlang.org/show_bug.cgi?id=12214
assert(testRet());
static assert(testRet());
+ testTypeinfo();
test12220();
test12403();
}
/*
TEST_OUTPUT:
---
-foo1 ulong function(return ref int* delegate() return p) ref return
+foo1 ulong function(return ref int* delegate() return p) return ref
foo2 int function(return ref int delegate() p) ref
foo3 int function(ref inout(int*) p) ref
foo4 int function(return ref inout(int*) p) ref
ref ulong foo1(return ref int* delegate() return p) return;
ref int foo2(return ref int delegate() p);
ref int foo3(inout ref int* p);
- ref int foo4(return inout ref int* p);
+ ref int foo4(return ref inout int* p);
}
pragma(msg, "foo1 ", typeof(&SS.foo1));
-1017635a9636ef6a7a4bda35b1e091875d829871
+e1f6680f50d147846316c2fa3363461a2aa7ac1d
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
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/blkcache.d core/internal/gc/blockmeta.d \
- core/internal/gc/impl/conservative/gc.d \
+ core/internal/attributes.d core/internal/cast_.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/blkcache.d \
+ core/internal/gc/blockmeta.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 \
gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \
gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
- rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
- rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
- rt/invariant_.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
- rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
- rt/util/utility.d
+ rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.d rt/critical_.d \
+ rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant_.d rt/lifetime.d \
+ rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \
+ rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
core/sys/posix/arpa/inet.d core/sys/posix/config.d \
- core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \
- core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \
- core/sys/posix/libgen.d core/sys/posix/locale.d \
- core/sys/posix/mqueue.d core/sys/posix/net/if_.d \
- core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \
- core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \
- core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \
- core/sys/posix/semaphore.d core/sys/posix/setjmp.d \
- core/sys/posix/signal.d core/sys/posix/spawn.d \
+ core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \
+ core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \
+ core/sys/posix/inttypes.d core/sys/posix/libgen.d \
+ core/sys/posix/locale.d core/sys/posix/mqueue.d \
+ core/sys/posix/net/if_.d core/sys/posix/netdb.d \
+ core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \
+ core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \
+ core/sys/posix/sched.d core/sys/posix/semaphore.d \
+ core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \
core/sys/posix/stdc/time.d core/sys/posix/stdio.d \
core/sys/posix/stdlib.d core/sys/posix/string.d \
core/sys/posix/strings.d core/sys/posix/sys/filio.d \
core/internal/array/equality.lo \
core/internal/array/operations.lo core/internal/array/utils.lo \
core/internal/atomic.lo core/internal/attributes.lo \
- core/internal/container/array.lo \
+ core/internal/cast_.lo core/internal/container/array.lo \
core/internal/container/common.lo \
core/internal/container/hashtab.lo \
core/internal/container/treap.lo core/internal/convert.lo \
gcc/sections/pecoff.lo gcc/simd.lo gcc/unwind/arm.lo \
gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
- object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
- rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \
- rt/deh.lo rt/dmain2.lo rt/ehalloc.lo rt/invariant_.lo \
- rt/lifetime.lo rt/memory.lo rt/minfo.lo rt/monitor_.lo \
- rt/profilegc.lo rt/sections.lo rt/tlsgc.lo rt/util/typeinfo.lo \
- rt/util/utility.lo
+ object.lo rt/aApply.lo rt/aApplyR.lo rt/arraycat.lo \
+ rt/config.lo rt/critical_.lo rt/deh.lo rt/dmain2.lo \
+ rt/ehalloc.lo rt/invariant_.lo rt/lifetime.lo rt/memory.lo \
+ rt/minfo.lo rt/monitor_.lo rt/profilegc.lo rt/sections.lo \
+ rt/tlsgc.lo rt/util/typeinfo.lo rt/util/utility.lo
am__objects_2 = core/stdc/libgdruntime_la-errno_.lo \
etc/valgrind/libgdruntime_la-valgrind_.lo
am__objects_3 = core/sys/elf/package.lo
core/stdcpp/vector.lo core/stdcpp/xutility.lo
am__objects_5 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \
core/sys/posix/config.lo core/sys/posix/dirent.lo \
- core/sys/posix/dlfcn.lo core/sys/posix/fcntl.lo \
- core/sys/posix/grp.lo core/sys/posix/iconv.lo \
- core/sys/posix/inttypes.lo core/sys/posix/libgen.lo \
- core/sys/posix/locale.lo core/sys/posix/mqueue.lo \
- core/sys/posix/net/if_.lo core/sys/posix/netdb.lo \
- core/sys/posix/netinet/in_.lo core/sys/posix/netinet/tcp.lo \
- core/sys/posix/poll.lo core/sys/posix/pthread.lo \
- core/sys/posix/pwd.lo core/sys/posix/sched.lo \
- core/sys/posix/semaphore.lo core/sys/posix/setjmp.lo \
- core/sys/posix/signal.lo core/sys/posix/spawn.lo \
- core/sys/posix/stdc/time.lo core/sys/posix/stdio.lo \
- core/sys/posix/stdlib.lo core/sys/posix/string.lo \
- core/sys/posix/strings.lo core/sys/posix/sys/filio.lo \
- core/sys/posix/sys/ioccom.lo core/sys/posix/sys/ioctl.lo \
- core/sys/posix/sys/ipc.lo core/sys/posix/sys/mman.lo \
- core/sys/posix/sys/msg.lo core/sys/posix/sys/resource.lo \
- core/sys/posix/sys/select.lo core/sys/posix/sys/shm.lo \
- core/sys/posix/sys/socket.lo core/sys/posix/sys/stat.lo \
- core/sys/posix/sys/statvfs.lo core/sys/posix/sys/time.lo \
- core/sys/posix/sys/ttycom.lo core/sys/posix/sys/types.lo \
- core/sys/posix/sys/uio.lo core/sys/posix/sys/un.lo \
- core/sys/posix/sys/utsname.lo core/sys/posix/sys/wait.lo \
- core/sys/posix/syslog.lo core/sys/posix/termios.lo \
- core/sys/posix/time.lo core/sys/posix/ucontext.lo \
- core/sys/posix/unistd.lo core/sys/posix/utime.lo
+ core/sys/posix/dlfcn.lo core/sys/posix/endian.lo \
+ core/sys/posix/fcntl.lo core/sys/posix/grp.lo \
+ core/sys/posix/iconv.lo core/sys/posix/inttypes.lo \
+ core/sys/posix/libgen.lo core/sys/posix/locale.lo \
+ core/sys/posix/mqueue.lo core/sys/posix/net/if_.lo \
+ core/sys/posix/netdb.lo core/sys/posix/netinet/in_.lo \
+ core/sys/posix/netinet/tcp.lo core/sys/posix/poll.lo \
+ core/sys/posix/pthread.lo core/sys/posix/pwd.lo \
+ core/sys/posix/sched.lo core/sys/posix/semaphore.lo \
+ core/sys/posix/setjmp.lo core/sys/posix/signal.lo \
+ core/sys/posix/spawn.lo core/sys/posix/stdc/time.lo \
+ core/sys/posix/stdio.lo core/sys/posix/stdlib.lo \
+ core/sys/posix/string.lo core/sys/posix/strings.lo \
+ core/sys/posix/sys/filio.lo core/sys/posix/sys/ioccom.lo \
+ core/sys/posix/sys/ioctl.lo core/sys/posix/sys/ipc.lo \
+ core/sys/posix/sys/mman.lo core/sys/posix/sys/msg.lo \
+ core/sys/posix/sys/resource.lo core/sys/posix/sys/select.lo \
+ core/sys/posix/sys/shm.lo core/sys/posix/sys/socket.lo \
+ core/sys/posix/sys/stat.lo core/sys/posix/sys/statvfs.lo \
+ core/sys/posix/sys/time.lo core/sys/posix/sys/ttycom.lo \
+ core/sys/posix/sys/types.lo core/sys/posix/sys/uio.lo \
+ core/sys/posix/sys/un.lo core/sys/posix/sys/utsname.lo \
+ core/sys/posix/sys/wait.lo core/sys/posix/syslog.lo \
+ core/sys/posix/termios.lo core/sys/posix/time.lo \
+ core/sys/posix/ucontext.lo core/sys/posix/unistd.lo \
+ core/sys/posix/utime.lo
@DRUNTIME_OS_POSIX_TRUE@am__objects_6 = $(am__objects_5)
am__objects_7 = core/sys/darwin/config.lo \
core/sys/darwin/crt_externs.lo core/sys/darwin/dlfcn.lo \
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/blkcache.d core/internal/gc/blockmeta.d \
- core/internal/gc/impl/conservative/gc.d \
+ core/internal/attributes.d core/internal/cast_.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/blkcache.d \
+ core/internal/gc/blockmeta.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 \
gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \
gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
- rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
- rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
- rt/invariant_.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
- rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
- rt/util/utility.d
+ rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.d rt/critical_.d \
+ rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant_.d rt/lifetime.d \
+ rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \
+ rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d
DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
core/sys/posix/arpa/inet.d core/sys/posix/config.d \
- core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \
- core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \
- core/sys/posix/libgen.d core/sys/posix/locale.d \
- core/sys/posix/mqueue.d core/sys/posix/net/if_.d \
- core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \
- core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \
- core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \
- core/sys/posix/semaphore.d core/sys/posix/setjmp.d \
- core/sys/posix/signal.d core/sys/posix/spawn.d \
+ core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \
+ core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \
+ core/sys/posix/inttypes.d core/sys/posix/libgen.d \
+ core/sys/posix/locale.d core/sys/posix/mqueue.d \
+ core/sys/posix/net/if_.d core/sys/posix/netdb.d \
+ core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \
+ core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \
+ core/sys/posix/sched.d core/sys/posix/semaphore.d \
+ core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \
core/sys/posix/stdc/time.d core/sys/posix/stdio.d \
core/sys/posix/stdlib.d core/sys/posix/string.d \
core/sys/posix/strings.d core/sys/posix/sys/filio.d \
core/internal/array/utils.lo: core/internal/array/$(am__dirstamp)
core/internal/atomic.lo: core/internal/$(am__dirstamp)
core/internal/attributes.lo: core/internal/$(am__dirstamp)
+core/internal/cast_.lo: core/internal/$(am__dirstamp)
core/internal/container/$(am__dirstamp):
@$(MKDIR_P) core/internal/container
@: > core/internal/container/$(am__dirstamp)
@: > rt/$(am__dirstamp)
rt/aApply.lo: rt/$(am__dirstamp)
rt/aApplyR.lo: rt/$(am__dirstamp)
-rt/aaA.lo: rt/$(am__dirstamp)
-rt/adi.lo: rt/$(am__dirstamp)
rt/arraycat.lo: rt/$(am__dirstamp)
-rt/cast_.lo: rt/$(am__dirstamp)
rt/config.lo: rt/$(am__dirstamp)
rt/critical_.lo: rt/$(am__dirstamp)
rt/deh.lo: rt/$(am__dirstamp)
core/sys/posix/config.lo: core/sys/posix/$(am__dirstamp)
core/sys/posix/dirent.lo: core/sys/posix/$(am__dirstamp)
core/sys/posix/dlfcn.lo: core/sys/posix/$(am__dirstamp)
+core/sys/posix/endian.lo: core/sys/posix/$(am__dirstamp)
core/sys/posix/fcntl.lo: core/sys/posix/$(am__dirstamp)
core/sys/posix/grp.lo: core/sys/posix/$(am__dirstamp)
core/sys/posix/iconv.lo: core/sys/posix/$(am__dirstamp)
{
static assert (is (V : T), "Can't assign `newval` of type `shared " ~ V.stringof ~ "` to `shared " ~ T.stringof ~ "`.");
- core.internal.atomic.atomicStore!ms(cast(T*)&val, cast(V)newval);
+ core.internal.atomic.atomicStore!ms(cast(T*)&val, *cast(V*)&newval);
}
/**
* Returns:
* the product
*/
-pragma(inline, true)
+// pragma(inline, true)
uint mulu()(uint x, uint y, ref bool overflow)
{
+ version (D_InlineAsm_X86) enum useAsm = true;
+ else version (D_InlineAsm_X86_64) enum useAsm = true;
+ else enum useAsm = false;
+
+ static if (useAsm)
+ {
+ if (!__ctfe)
+ {
+ uint r;
+ bool o;
+ asm pure nothrow @nogc @trusted
+ {
+ mov EAX, x;
+ mul y; // EDX:EAX = EAX * y
+ mov r, EAX;
+ setc o;
+ }
+ overflow |= o;
+ return r;
+ }
+ }
+
immutable ulong r = ulong(x) * ulong(y);
if (r >> 32)
overflow = true;
pragma(inline, true)
ulong mulu()(ulong x, uint y, ref bool overflow)
{
+ version (D_InlineAsm_X86_64)
+ {
+ if (!__ctfe)
+ return mulu(x, ulong(y), overflow);
+ }
+
ulong r = x * y;
if (x >> 32 &&
r / x != y)
}
/// ditto
-pragma(inline, true)
+// pragma(inline, true)
ulong mulu()(ulong x, ulong y, ref bool overflow)
{
+ version (D_InlineAsm_X86_64)
+ {
+ if (!__ctfe)
+ {
+ ulong r;
+ bool o;
+ asm pure nothrow @nogc @trusted
+ {
+ mov RAX, x;
+ mul y; // RDX:RAX = RAX * y
+ mov r, RAX;
+ setc o;
+ }
+ overflow |= o;
+ return r;
+ }
+ }
+
immutable ulong r = x * y;
if ((x | y) >> 32 &&
x &&
__gshared Config config;
+private __gshared bool _initialized;
+
struct Config
{
bool disable; // start disabled
bool initialize()
{
- return initConfigOptions(this, "gcopt");
+ if (!_initialized)
+ _initialized = initConfigOptions(this, "gcopt");
+ return _initialized;
}
void help() @nogc nothrow
*/
module core.gc.gcinterface;
+import core.thread.threadbase : ThreadBase;
+
static import core.memory;
alias BlkAttr = core.memory.GC.BlkAttr;
alias BlkInfo = core.memory.GC.BlkInfo;
* Returns: true if successful.
*/
bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow;
+
+ /**
+ * Prepare a thread for use with the GC after the GC is initialized. Note
+ * that you can register an initThread function to call before the GC is
+ * initialized, see core.gc.registry.
+ *
+ * This function is always called *from* the thread as it is being started,
+ * before any static initializers. The thread object parameter may not yet
+ * be registered as `ThreadBase.getThis()`, as this is called before that
+ * happens.
+ */
+ void initThread(ThreadBase thread) nothrow @nogc;
+
+ /**
+ * Clean up any GC related data from the thread before it exits. There is
+ * no equivalent of this function as a hook before the GC is initialized.
+ * That is, the thread init function called before the GC is initialized
+ * (from the registry hook) should assume this function may not be called
+ * on termination if the GC is never initialized.
+ *
+ * Most times, this is called from the thread that is the given thread
+ * reference, but it's possible the `thread` parameter is not the same as
+ * the current thread.
+ *
+ * There is no guarantee the thread is still registered as
+ * `ThreadBase.getThis()`.
+ */
+ void cleanupThread(ThreadBase thread) nothrow @nogc;
}
module core.gc.registry;
import core.gc.gcinterface : GC;
+import core.thread.threadbase : ThreadBase;
/*@nogc nothrow:*/
*/
alias GCFactory = GC function();
+/**
+ * A function that will initialize a thread before the GC has been initialized.
+ * Once the GC is initialized, the interface method GC.initThread for each new
+ * thread.
+ */
+alias GCThreadInitFunction = void function(ThreadBase base) nothrow @nogc;
+
/**
* Register a GC factory under the given `name`. This function must be called
* from a C constructor before druntime is initialized.
* To use the registered GC, it's name must be specified gcopt runtime option,
* e.g. by passing $(TT, --DRT-gcopt=gc:my_gc_name) as application argument.
*
+ * The thread init function will be called *only before* the GC has been
+ * initialized to the registered GC. It is called as the first step in starting
+ * the new thread before the thread is registered with the runtime as a running
+ * thread. This allows any specific thread data that is needed for running the
+ * GC to be registered with the thread object.
+ *
+ * After the GC is initialized, the GC interface function `initThread` is
+ * called instead. This function should expect the possibility that the
+ * reciprocal `cleanupThread` method may not be called if the GC is never
+ * initialized.
+ *
* Params:
* name = name of the GC implementation; should be unique
* factory = function to instantiate the implementation
+ * threadInit = function to call from a new thread *before* registration in
+ * the list of running threads, to set up any GC-specific data.
* Note: The registry does not perform synchronization, as registration is
* assumed to be executed serially, as is the case for C constructors.
* See_Also:
* $(LINK2 https://dlang.org/spec/garbage.html#gc_config, Configuring the Garbage Collector)
*/
-void registerGCFactory(string name, GCFactory factory) nothrow @nogc
+void registerGCFactory(string name, GCFactory factory,
+ GCThreadInitFunction threadInit = null) nothrow @nogc
{
import core.stdc.stdlib : realloc;
auto ptr = cast(Entry*)realloc(entries.ptr, (entries.length + 1) * Entry.sizeof);
entries = ptr[0 .. entries.length + 1];
- entries[$ - 1] = Entry(name, factory);
+ entries[$ - 1] = Entry(name, factory, threadInit);
}
/**
return null;
}
+/**
+ * Get the thread init function used for the selected GC.
+ *
+ * Note, this must be called before `createGCInstance`. It is typically called by
+ * `rt_init`.
+ */
+GCThreadInitFunction threadInit(string name) nothrow @nogc
+{
+ foreach (entry; entries)
+ {
+ if (entry.name == name)
+ return entry.threadInit;
+ }
+ return null;
+}
+
// list of all registerd GCs
const(Entry[]) registeredGCFactories(scope int dummy=0) nothrow @nogc
{
{
string name;
GCFactory factory;
+ GCThreadInitFunction threadInit;
}
__gshared Entry[] entries;
*/
module core.internal.array.appending;
-/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
-private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow;
+private extern (C)
+{
+ bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) pure nothrow;
+ bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) pure nothrow;
+}
private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });
* Bugs:
* This function template was ported from a much older runtime hook that bypassed safety,
* purity, and throwabilty checks. To prevent breaking existing code, this function template
- * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
*/
ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @trusted
{
- // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718
+ import core.internal.traits: Unqual;
+
+ alias Unqual_T = Unqual!T;
+ alias Unqual_Tarr = Unqual_T[];
+ enum isshared = is(T == shared);
+ auto unqual_px = cast(Unqual_Tarr) px;
+
+ // Ignoring additional attributes allows reusing the same generated code
+ px = cast(Tarr)_d_arrayappendcTX_(unqual_px, n, isshared);
+ return px;
+}
+
+private ref Tarr _d_arrayappendcTX_(Tarr : T[], T)(return ref scope Tarr px, size_t n, bool isshared) @trusted
+{
version (DigitalMars) pragma(inline, false);
version (D_TypeInfo)
{
- auto ti = typeid(Tarr);
+ // Short circuit if no data is being appended.
+ if (n == 0)
+ return px;
+
+ import core.stdc.string : memcpy, memset;
+ import core.internal.lifetime : __doPostblit;
+ import core.internal.array.utils: __arrayAlloc, newCapacity, __typeAttrs;
+ import core.internal.gc.blockmeta : PAGESIZE;
+ import core.exception: onOutOfMemoryError;
+ import core.memory: GC;
+
+ alias BlkAttr = GC.BlkAttr;
+
+ enum sizeelem = T.sizeof;
+ auto length = px.length;
+ auto newlength = length + n;
+ auto newsize = newlength * sizeelem;
+ auto size = length * sizeelem;
+
+ if (!gc_expandArrayUsed(px, newsize, isshared))
+ {
+ // could not set the size, we must reallocate.
+ auto newcap = newCapacity(newlength, sizeelem);
+ auto attrs = __typeAttrs!T(cast(void*)px.ptr) | BlkAttr.APPENDABLE;
+
+ T* ptr = cast(T*)GC.malloc(newcap, attrs, typeid(T));
+ if (ptr is null)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
+
+ if (newsize != newcap)
+ {
+ // For small blocks that are always fully scanned, if we allocated more
+ // capacity than was requested, we are responsible for zeroing that
+ // memory.
+ // TODO: should let the GC figure this out, as this property may
+ // not always hold.
+ if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE)
+ memset(ptr + newlength, 0, newcap - newsize);
+
+ gc_shrinkArrayUsed(ptr[0 .. newlength], newcap, isshared);
+ }
+
+ memcpy(ptr, px.ptr, size);
- // _d_arrayappendcTX takes the `px` as a ref byte[], but its length
- // should still be the original length
- auto pxx = (cast(byte*)px.ptr)[0 .. px.length];
- ._d_arrayappendcTX(ti, pxx, n);
- px = (cast(T*)pxx.ptr)[0 .. pxx.length];
+ // do potsblit processing.
+ __doPostblit!T(ptr[0 .. length]);
+ px = ptr[0 .. newlength];
+ return px;
+ }
+
+ // we were able to expand in place, just update the length
+ px = px.ptr[0 .. newlength];
return px;
}
else
version (D_ProfileGC)
{
/**
- * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending).
+ * TraceGC wrapper around _d_arrayappendcTX.
*/
- ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
+ ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n,
+ string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
{
version (D_TypeInfo)
{
version (DigitalMars) pragma(inline, false);
import core.stdc.string : memcpy;
- import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+ import core.internal.traits : Unqual;
- enum hasPostblit = __traits(hasPostblit, T);
auto length = x.length;
_d_arrayappendcTX(x, y.length);
// Only call `copyEmplace` if `T` has a copy ctor and no postblit.
- static if (hasElaborateCopyConstructor!T && !hasPostblit)
+ static if (__traits(hasCopyConstructor, T))
{
import core.lifetime : copyEmplace;
- foreach (i, ref elem; y)
- copyEmplace(elem, x[length + i]);
+ size_t i;
+ try
+ {
+ for (i = 0; i < y.length; ++i)
+ copyEmplace(y[i], x[length + i]);
+ }
+ catch (Exception o)
+ {
+ /* Destroy, in reverse order, what we've constructed so far
+ */
+ while (i--)
+ {
+ auto elem = cast(Unqual!T*) &x[length + i];
+ destroy(*elem);
+ }
+
+ throw o;
+ }
}
else
{
if (y.length)
{
// blit all elements at once
- auto xptr = cast(Unqual!T *)&x[length];
- immutable size = T.sizeof;
-
- memcpy(xptr, cast(Unqual!T *)&y[0], y.length * size);
+ memcpy(cast(void*)&x[length], cast(void*)&y[0], y.length * T.sizeof);
// call postblits if they exist
- static if (hasPostblit)
+ static if (__traits(hasPostblit, T))
{
- auto eptr = xptr + y.length;
- for (auto ptr = xptr; ptr < eptr; ptr++)
- ptr.__xpostblit();
- }
- }
+ import core.internal.lifetime : __doPostblit;
+ size_t i = 0;
+ try __doPostblit(x[length .. $], i);
+ catch (Exception o)
+ {
+ // Destroy, in reverse order, what we've constructed so far
+ while (i--)
+ {
+ auto elem = cast(Unqual!T*) &x[length + i];
+ destroy(*elem);
+ }
+
+ throw o;
+ }
+ }}
}
return x;
_d_arrayappendT(str, "c");
assert(str == "abc");
}
+
+@safe nothrow unittest
+{
+ static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
+ static size_t inner_postblit_cnt = 0;
+ static size_t inner_dtor_cnt = 0;
+ static size_t outer_postblit_cnt = 0;
+ static size_t outer_dtor_cnt = 0;
+ static struct Inner
+ {
+ char id;
+
+ @safe:
+ this(this)
+ {
+ ++inner_postblit_cnt;
+ if (id == '2')
+ throw new FailedPostblitException();
+ }
+
+ ~this() nothrow
+ {
+ ++inner_dtor_cnt;
+ }
+ }
+
+ static struct Outer
+ {
+ Inner inner1, inner2, inner3;
+
+ nothrow @safe:
+ this(char first, char second, char third)
+ {
+ inner1 = Inner(first);
+ inner2 = Inner(second);
+ inner3 = Inner(third);
+ }
+
+ this(this)
+ {
+ ++outer_postblit_cnt;
+ }
+
+ ~this()
+ {
+ ++outer_dtor_cnt;
+ }
+ }
+
+ Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')];
+
+ try {
+ Outer[] arrApp;
+ arrApp ~= arr;
+ }
+ catch (FailedPostblitException) {}
+ catch (Exception) assert(false);
+
+ assert(inner_postblit_cnt == 5);
+ assert(inner_dtor_cnt == 4);
+ assert(outer_postblit_cnt == 1);
+ assert(outer_dtor_cnt == 1);
+}
bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow pure;
size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow pure;
bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow pure;
+ void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow pure;
}
+
+/**
+Shrink the "allocated" length of an array to be the exact size of the array.
+
+It doesn't matter what the current allocated length of the array is, the
+user is telling the runtime that he knows what he is doing.
+
+Params:
+ T = the type of the elements in the array (this should be unqualified)
+ arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
+ isshared = true if the underlying data is shared
+*/
+void _d_arrayshrinkfit(Tarr: T[], T)(Tarr arr, bool isshared) @trusted
+{
+ import core.exception : onFinalizeError;
+ import core.internal.traits: hasElaborateDestructor;
+
+ debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", T.sizeof, arr.ptr, arr.length);
+ auto reqlen = arr.length;
+
+ auto curArr = cast(Tarr)gc_getArrayUsed(arr.ptr, isshared);
+ if (curArr.ptr is null)
+ // not a valid GC pointer
+ return;
+
+ // align the array.
+ auto offset = arr.ptr - curArr.ptr;
+ auto curlen = curArr.length - offset;
+ if (curlen <= reqlen)
+ // invalid situation, or no change.
+ return;
+
+ // if the type has a destructor, destroy elements we are about to remove.
+ static if(is(T == struct) && hasElaborateDestructor!T)
+ {
+ try
+ {
+ // Finalize the elements that are being removed
+
+ // Due to the fact that the delete operator calls destructors
+ // for arrays from the last element to the first, we maintain
+ // compatibility here by doing the same.
+ for (auto curP = arr.ptr + curlen - 1; curP >= arr.ptr + reqlen; curP--)
+ {
+ // call destructor
+ curP.__xdtor();
+ }
+ }
+ catch (Exception e)
+ {
+ onFinalizeError(typeid(T), e);
+ }
+ }
+
+ gc_shrinkArrayUsed(arr[0 .. reqlen], curlen * T.sizeof, isshared);
+}
+
/**
Set the array capacity.
alias BlkAttr = GC.BlkAttr;
auto size = T.sizeof;
+
bool overflow = false;
const reqsize = mulu(size, newcapacity, overflow);
if (overflow)
enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo);
- static if (hasElaborateCopyConstructor!T)
+ static if (__traits(hasCopyConstructor, T))
{
size_t i;
try
}
else
{
- // blit all elements at once
- memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
+ if (to.length)
+ {
+ // blit all elements at once
+ memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
+
+ // call postblits if they exist
+ static if (__traits(hasPostblit, T))
+ {
+ import core.internal.lifetime : __doPostblit;
+ size_t i = 0;
+ try __doPostblit(to, i);
+ catch (Exception o)
+ {
+ // Destroy, in reverse order, what we've constructed so far
+ while (i--)
+ {
+ auto elem = cast(Unqual!T*) &to[i];
+ destroy(*elem);
+ }
+
+ throw o;
+ }
+ }
+ }
}
return to;
T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted
{
+ import core.checkedint : mulu;
import core.exception : onOutOfMemoryError;
import core.internal.traits : Unqual;
import core.internal.array.utils : __arrayAlloc;
alias UnqT = Unqual!T;
size_t elemSize = T.sizeof;
- size_t arraySize;
debug(PRINTF) printf("_d_newarrayU(length = x%zu, size = %zu)\n", length, elemSize);
if (length == 0 || elemSize == 0)
return null;
- version (D_InlineAsm_X86)
+ bool overflow = false;
+ const arraySize = mulu(elemSize, length, overflow);
+ if (!overflow)
{
- asm pure nothrow @nogc
+ if (auto arr = __arrayAlloc!UnqT(arraySize))
{
- mov EAX, elemSize ;
- mul EAX, length ;
- mov arraySize, EAX ;
- jnc Lcontinue ;
+ debug(PRINTF) printf("p = %p\n", arr.ptr);
+ return (cast(T*) arr.ptr)[0 .. length];
}
}
- else version (D_InlineAsm_X86_64)
- {
- asm pure nothrow @nogc
- {
- mov RAX, elemSize ;
- mul RAX, length ;
- mov arraySize, RAX ;
- jnc Lcontinue ;
- }
- }
- else
- {
- import core.checkedint : mulu;
- bool overflow = false;
- arraySize = mulu(elemSize, length, overflow);
- if (!overflow)
- goto Lcontinue;
- }
-
-Loverflow:
onOutOfMemoryError();
assert(0);
-
-Lcontinue:
- auto arr = __arrayAlloc!UnqT(arraySize);
- if (!arr.ptr)
- goto Loverflow;
- debug(PRINTF) printf("p = %p\n", arr.ptr);
- return (cast(T*) arr.ptr)[0 .. length];
}
/// ditto
assert(0, "Cannot create new multi-dimensional array if compiling without support for runtime type information!");
}
}
+
+
+/**
+Allocate an array literal
+
+Rely on the caller to do the initialization of the array.
+
+---
+int[] getArr()
+{
+ return [10, 20];
+ // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
+ // res[0] = 10;
+ // res[1] = 20;
+ // return res[0..2];
+}
+---
+
+Params:
+ T = unqualified type of array elements
+ length = `.length` of array literal
+
+Returns: pointer to allocated array
+*/
+void* _d_arrayliteralTX(T)(size_t length) @trusted pure nothrow
+{
+ const allocsize = length * T.sizeof;
+
+ if (allocsize == 0)
+ return null;
+ else
+ {
+ import core.memory : GC;
+ import core.internal.traits : hasIndirections, hasElaborateDestructor;
+ alias BlkAttr = GC.BlkAttr;
+
+ /* Same as in core.internal.array.utils.__typeAttrs!T,
+ * but don't use a nested template function call here to avoid
+ * possible linking errors.
+ */
+ uint attrs = BlkAttr.APPENDABLE;
+ static if (!hasIndirections!T)
+ attrs |= BlkAttr.NO_SCAN;
+ static if (is(T == struct) && hasElaborateDestructor!T)
+ attrs |= BlkAttr.FINALIZE;
+
+ return GC.malloc(allocsize, attrs, typeid(T));
+ }
+}
+
+version (D_ProfileGC)
+void* _d_arrayliteralTXTrace(T)(size_t length, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted pure nothrow
+{
+ version (D_TypeInfo)
+ {
+ import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
+ mixin(TraceHook!((T[]).stringof, "_d_arrayliteralTX"));
+
+ return _d_arrayliteralTX!T(length);
+ }
+ else
+ assert(0);
+}
// https://issues.dlang.org/show_bug.cgi?id=24453
@safe unittest
{
- static inout(char)[] foo(ref scope return inout(char)[] s)
+ static inout(char)[] foo(return ref scope inout(char)[] s)
{
auto bla = s.idup;
return s;
// * dynamic arrays,
// * (most) arrays of different (unqualified) element types, and
// * arrays of structs with custom opEquals.
-
- // The scalar-only overload takes advantage of known properties of scalars to
- // reduce template instantiation. This is expected to be the most common case.
-bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
-@nogc nothrow pure @trusted
-if (__traits(isScalar, T1) && __traits(isScalar, T2))
-{
- const length = lhs.length;
-
- static if (T1.sizeof == T2.sizeof
- // Signedness needs to match for types that promote to int.
- // (Actually it would be okay to memcmp bool[] and byte[] but that is
- // probably too uncommon to be worth checking for.)
- && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2))
- && !__traits(isFloating, T1) && !__traits(isFloating, T2))
- {
- if (__ctfe)
- return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length);
- else
- {
- // This would improperly allow equality of integers and pointers
- // but the CTFE branch will stop this function from compiling then.
- import core.stdc.string : memcmp;
- return length == rhs.length &&
- (!length || 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, length * T1.sizeof));
- }
- }
- else
- {
- return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length);
- }
-}
-
-bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs)
-if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
+bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) @trusted
{
if (lhs.length != rhs.length)
return false;
if (lhs.length == 0)
return true;
- static if (useMemcmp!(T1, T2))
- {
- if (!__ctfe)
- {
- static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure
- {
- pragma(inline, true);
- import core.stdc.string : memcmp;
- return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0;
- }
- return trustedMemcmp(lhs, rhs);
- }
- else
- {
- foreach (const i; 0 .. lhs.length)
- {
- if (at(lhs, i) != at(rhs, i))
- return false;
- }
- return true;
- }
- }
- else
- {
- foreach (const i; 0 .. lhs.length)
- {
- if (at(lhs, i) != at(rhs, i))
- return false;
- }
- return true;
- }
+ alias PureType = bool function(scope T1[], scope T2[], size_t) @safe pure nothrow @nogc;
+
+ return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length);
}
/******************************
* Outlined to enable __equals() to be inlined, as dmd cannot inline loops.
*/
private
-bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length)
+bool isEqual(T1, T2)(scope T1[] lhs, scope T2[] rhs, size_t length)
{
+ // Returns a reference to an array element, eliding bounds check and
+ // casting void to ubyte.
+ pragma(inline, true)
+ static ref at(T)(scope T[] r, size_t i) @trusted
+ // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
+ if (!(is(T == struct) && !is(typeof(T.sizeof))))
+ {
+ static if (is(T == void))
+ return (cast(ubyte[]) r)[i];
+ else
+ return r[i];
+ }
+
foreach (const i; 0 .. length)
- if (t1[i] != t2[i])
+ {
+ if (at(lhs, i) != at(rhs, i))
return false;
+ }
return true;
}
assert(a1 != a2);
}
-
-private:
-
-// - Recursively folds static array types to their element type,
-// - maps void to ubyte, and
-// - pointers to size_t.
-template BaseType(T)
-{
- static if (__traits(isStaticArray, T))
- alias BaseType = BaseType!(typeof(T.init[0]));
- else static if (is(immutable T == immutable void))
- alias BaseType = ubyte;
- else static if (is(T == E*, E))
- alias BaseType = size_t;
- else
- alias BaseType = T;
-}
-
-// Use memcmp if the element sizes match and both base element types are integral.
-// Due to int promotion, disallow small integers of diverging signed-ness though.
-template useMemcmp(T1, T2)
-{
- static if (T1.sizeof != T2.sizeof)
- enum useMemcmp = false;
- else
- {
- alias B1 = BaseType!T1;
- alias B2 = BaseType!T2;
- enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2)
- && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) );
- }
-}
-
-unittest
-{
- enum E { foo, bar }
-
- static assert(useMemcmp!(byte, byte));
- static assert(useMemcmp!(ubyte, ubyte));
- static assert(useMemcmp!(void, const void));
- static assert(useMemcmp!(void, immutable bool));
- static assert(useMemcmp!(void, inout char));
- static assert(useMemcmp!(void, shared ubyte));
- static assert(!useMemcmp!(void, byte)); // differing signed-ness
- static assert(!useMemcmp!(char[8], byte[8])); // ditto
-
- static assert(useMemcmp!(short, short));
- static assert(useMemcmp!(wchar, ushort));
- static assert(!useMemcmp!(wchar, short)); // differing signed-ness
-
- static assert(useMemcmp!(int, uint)); // no promotion, ignoring signed-ness
- static assert(useMemcmp!(dchar, E));
-
- static assert(useMemcmp!(immutable void*, size_t));
- static assert(useMemcmp!(double*, ptrdiff_t));
- static assert(useMemcmp!(long[2][3], const(ulong)[2][3]));
-
- static assert(!useMemcmp!(float, float));
- static assert(!useMemcmp!(double[2], double[2]));
- static assert(!useMemcmp!(Object, Object));
- static assert(!useMemcmp!(int[], int[]));
-}
-
// https://issues.dlang.org/show_bug.cgi?id=21094
unittest
{
return lhs == rhs;
}
}
-
-// Returns a reference to an array element, eliding bounds check and
-// casting void to ubyte.
-pragma(inline, true)
-ref at(T)(T[] r, size_t i) @trusted
- // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
- if (!(is(T == struct) && !is(typeof(T.sizeof))))
-{
- static if (is(immutable T == immutable void))
- return (cast(ubyte*) r.ptr)[i];
- else
- return r.ptr[i];
-}
return null;
}
+/**
+Given an array of length `size` that needs to be expanded to `newlength`,
+compute a new capacity.
+
+Better version by Dave Fladebo, enhanced by Steven Schveighoffer:
+This uses an inverse logorithmic algorithm to pre-allocate a bit more
+space for larger arrays.
+- The maximum "extra" space is about 80% of the requested space. This is for
+PAGE size and smaller.
+- As the arrays grow, the relative pre-allocated space shrinks.
+- Perhaps most importantly, overall memory usage and stress on the GC
+is decreased significantly for demanding environments.
+- The algorithm is tuned to avoid any division at runtime.
+
+Params:
+ newlength = new `.length`
+ elemsize = size of the element in the new array
+Returns: new capacity for array
+*/
+size_t newCapacity(size_t newlength, size_t elemsize) pure nothrow
+{
+ size_t newcap = newlength * elemsize;
+
+ /*
+ * Max growth factor numerator is 234, so allow for multiplying by 256.
+ * But also, the resulting size cannot be more than 2x, so prevent
+ * growing if 2x would fill up the address space (for 32-bit)
+ */
+ enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1);
+ if (!newcap || (newcap & ~largestAllowed))
+ return newcap;
+
+ /*
+ * The calculation for "extra" space depends on the requested capacity.
+ * We use an inverse logarithm of the new capacity to add an extra 15%
+ * to 83% capacity. Note that normally we humans think in terms of
+ * percent, but using 128 instead of 100 for the denominator means we
+ * can avoid all division by simply bit-shifthing. Since there are only
+ * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using
+ * a lookup table allows us to precalculate the multiplier based on the
+ * inverse logarithm. The formula rougly is:
+ *
+ * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1)))
+ */
+ import core.bitop;
+ static immutable multTable = (){
+ assert(__ctfe);
+ ulong[size_t.sizeof * 8] result;
+ foreach (i; 0 .. result.length)
+ {
+ auto factor = 128 + 1280 / (i + 1);
+ result[i] = factor > 234 ? 234 : factor;
+ }
+ return result;
+ }();
+
+ auto mult = multTable[bsr(newcap)];
+
+ // if this were per cent, then the code would look like:
+ // ((newlength * mult + 99) / 100) * elemsize
+ newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize;
+ debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize);
+ debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize);
+ return newcap;
+}
+
uint __typeAttrs(T)(void *copyAttrsFrom = null)
{
import core.internal.traits : hasIndirections, hasElaborateDestructor;
--- /dev/null
+module core.internal.cast_;
+
+// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
+// but we are trying to implement dynamic cast.
+bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) pure nothrow @safe @nogc
+{
+ // same class if signatures match, works with potential duplicates across binaries
+ if (a is b)
+ return true;
+
+ // new fast way
+ if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig)
+ return a.nameSig[0] == b.nameSig[0]
+ && a.nameSig[1] == b.nameSig[1]
+ && a.nameSig[2] == b.nameSig[2]
+ && a.nameSig[3] == b.nameSig[3];
+
+ // old slow way for temporary binary compatibility
+ return a.name == b.name;
+}
+
+
+/*****
+ * Dynamic cast from a class object `o` to class or interface `To`, where `To` is a subtype of `o`.
+ * Params:
+ * o = instance of class
+ * To = class or interface that is a subtype of `o`
+ * Returns:
+ * null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
+ */
+private void* _d_dynamic_cast(To)(const return scope Object o) @trusted
+{
+ void* res = null;
+ size_t offset = 0;
+
+ if (o && _d_isbaseof2!To(typeid(o), offset))
+ {
+ res = cast(void*) o + offset;
+ }
+ return res;
+}
+
+/**
+ * Dynamic cast `o` to final class `To` only one level down
+ * Params:
+ * o = object that is instance of a class
+ * To = final class that is a subclass type of `o`
+ * Returns:
+ * o if it succeeds, null if it fails
+ */
+private void* _d_paint_cast(To)(const return scope Object o)
+{
+ /* If o is really an instance of c, just do a paint
+ */
+ auto p = o && cast(void*)(areClassInfosEqual(typeid(o), typeid(To).info)) ? o : null;
+ debug assert(cast(void*)p is cast(void*)_d_dynamic_cast!To(o));
+ return cast(void*)p;
+}
+
+private void* _d_class_cast_impl(const return scope Object o, const ClassInfo c) pure nothrow @safe @nogc
+{
+ if (!o)
+ return null;
+
+ ClassInfo oc = typeid(o);
+ int delta = oc.depth;
+
+ if (delta && c.depth)
+ {
+ delta -= c.depth;
+ if (delta < 0)
+ return null;
+
+ while (delta--)
+ oc = oc.base;
+ if (areClassInfosEqual(oc, c))
+ return cast(void*)o;
+ return null;
+ }
+
+ // no depth data - support the old way
+ do
+ {
+ if (areClassInfosEqual(oc, c))
+ return cast(void*)o;
+ oc = oc.base;
+ } while (oc);
+ return null;
+}
+
+/*****
+ * Dynamic cast from a class object o to class type `To`, where `To` is a subclass type of `o`.
+ * Params:
+ * o = instance of class
+ * To = a subclass type of o
+ * Returns:
+ * null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
+ */
+private void* _d_class_cast(To)(const return scope Object o)
+{
+ return _d_class_cast_impl(o, typeid(To));
+}
+
+/*************************************
+ * Attempts to cast interface Object o to class type `To`.
+ * Returns o if successful, null if not.
+ */
+private void* _d_interface_cast(To)(void* p) @trusted
+{
+ if (!p)
+ return null;
+
+ Interface* pi = **cast(Interface***) p;
+
+ Object o2 = cast(Object)(p - pi.offset);
+ void* res = null;
+ size_t offset = 0;
+ if (o2 && _d_isbaseof2!To(typeid(o2), offset))
+ {
+ res = cast(void*) o2 + offset;
+ }
+ return res;
+}
+
+/**
+* Hook that detects the type of cast performed and calls the appropriate function.
+* Params:
+* o = object that is being casted
+* To = type to which the object is being casted
+* Returns:
+* null if the cast fails, otherwise returns the object casted to the type `To`.
+*/
+void* _d_cast(To, From)(From o) @trusted
+{
+ static if (is(From == class) && is(To == interface))
+ {
+ return _d_dynamic_cast!To(o);
+ }
+
+ static if (is(From == class) && is(To == class))
+ {
+ static if (is(From FromSupers == super) && is(To ToSupers == super))
+ {
+ /* Check for:
+ * class A { }
+ * final class B : A { }
+ * ... cast(B) A ...
+ */
+ // Multiple inheritance is not allowed, so we can safely assume
+ // that the second super can only be an interface.
+ static if (__traits(isFinalClass, To) && is(ToSupers[0] == From) &&
+ ToSupers.length == 1 && FromSupers.length <= 1)
+ {
+ return _d_paint_cast!To(o);
+ }
+ }
+
+ static if (is (To : From))
+ {
+ static if (is (To == From))
+ {
+ return cast(void*)o;
+ }
+ else
+ {
+ return _d_class_cast!To(o);
+ }
+ }
+
+ return null;
+ }
+
+ static if (is(From == interface))
+ {
+ static if (is(From == To))
+ {
+ return cast(void*)o;
+ }
+ else
+ {
+ return _d_interface_cast!To(cast(void*)o);
+ }
+ }
+ else
+ {
+ return null;
+ }
+}
+
+private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset)
+{
+ auto c = typeid(To).info;
+
+ if (areClassInfosEqual(oc, c))
+ return true;
+
+ do
+ {
+ if (oc.base && areClassInfosEqual(oc.base, c))
+ return true;
+
+ // Bugzilla 2013: Use depth-first search to calculate offset
+ // from the derived (oc) to the base (c).
+ foreach (iface; oc.interfaces)
+ {
+ if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2!To(iface.classinfo, offset))
+ {
+ offset += iface.offset;
+ return true;
+ }
+ }
+
+ oc = oc.base;
+ } while (oc);
+
+ return false;
+}
+
+@safe pure unittest
+{
+ interface I {}
+
+ class A {}
+ class B : A {}
+ class C : B, I{}
+
+ A ac = new C();
+ assert(_d_cast!I(ac) !is null); // A(c) to I
+ assert(_d_dynamic_cast!I(ac) !is null);
+
+ A ab = new B();
+ assert(_d_cast!I(ab) is null); // A(b) to I
+ assert(_d_dynamic_cast!I(ab) is null);
+}
+
+@safe pure unittest
+{
+ class A {}
+ class B : A {}
+ class C : B {}
+ final class D : C {}
+
+ C cd = new D();
+ assert(_d_cast!D(cd) !is null); // C(d) to D
+ assert(_d_paint_cast!D(cd) !is null);
+
+ class G {}
+ final class J {}
+ A a = new A();
+ assert(_d_cast!G(a) is null); // A(a) to G
+ assert(_d_paint_cast!G(a) is null);
+
+ assert(_d_cast!J(a) is null); // A(a) to J
+ assert(_d_paint_cast!J(a) is null);
+}
+
+@safe pure unittest
+{
+ class A {}
+ class B : A {}
+ class C : B {}
+ class D {}
+
+ A ac = new C();
+ assert(_d_cast!C(ac) !is null); // A(c) to C
+ assert(_d_class_cast!C(ac) !is null);
+
+ assert(_d_cast!B(ac) !is null); // A(c) to B
+ assert(_d_class_cast!B(ac) !is null);
+
+ A ab = new B();
+ assert(_d_cast!C(ab) is null); // A(b) to C
+ assert(_d_class_cast!C(ab) is null);
+
+ A a = new A();
+ assert(_d_cast!D(a) is null); // A(a) to D
+ assert(_d_class_cast!D(a) is null);
+}
+
+@safe pure unittest
+{
+ interface I1 {}
+ interface I2 {}
+ interface I3 {}
+ class A {}
+ class B : A, I1, I2 {}
+ class C : B, I3 {}
+
+ I1 bi = new B();
+ assert(_d_cast!I2(bi) !is null); // I1(b) to I2
+ assert(_d_interface_cast!I2(cast(void*)bi) !is null);
+
+ assert(_d_cast!A(bi) !is null); // I1(b) to A
+ assert(_d_interface_cast!A(cast(void*)bi) !is null);
+
+ assert(_d_cast!B(bi) !is null); // I1(b) to B
+ assert(_d_interface_cast!B(cast(void*)bi) !is null);
+
+ assert(_d_cast!I3(bi) is null); // I1(b) to I3
+ assert(_d_interface_cast!I3(cast(void*)bi) is null);
+
+ assert(_d_cast!C(bi) is null); // I1(b) to C
+ assert(_d_interface_cast!C(cast(void*)bi) is null);
+
+ assert(_d_cast!I1(bi) !is null); // I1(b) to I1
+ assert(_d_interface_cast!I1(cast(void*)bi) !is null);
+
+ I3 ci = new C();
+ assert(_d_cast!I1(ci) !is null); // I3(c) to I1
+ assert(_d_interface_cast!I1(cast(void*)ci) !is null);
+}
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1)
{
+ pragma(inline, true);
return cast(const(ubyte)[])arr;
}
{
static if (T.sizeof == 1)
{
+ pragma(inline, true);
if (__ctfe)
{
ubyte[] result = ctfe_alloc(1);
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
{
+ pragma(inline, true);
if (!__ctfe)
return (cast(const ubyte*) &val)[0 .. T.sizeof];
else static if (is(typeof(val[0]) : void))
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
{
+ pragma(inline, true);
if (__ctfe)
{
static if (is(T V == enum)){}
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
{
+ pragma(inline, true);
if (__ctfe)
{
if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
@trusted pure nothrow @nogc
const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union))
{
+ pragma(inline, true);
if (__ctfe)
{
ubyte[] bytes = ctfe_alloc(T.sizeof);
}
// free the allocation on thread exit.
-@standalone static ~this()
+void cleanupBlkCache(void* storage) nothrow @nogc
{
- if (__blkcache_storage)
+ if (storage)
{
+ // check if this is the same thread as the current running thread, and
+ // if so, make sure we don't leave a dangling pointer.
+ if (__blkcache_storage is storage)
+ __blkcache_storage = null;
import core.stdc.stdlib : free;
- import core.thread.threadbase;
- auto tBase = ThreadBase.getThis();
- if (tBase !is null)
- tBase.tlsGCData = null;
- free(__blkcache_storage);
- __blkcache_storage = null;
+ free(storage);
}
}
return false;
// try extending the block into subsequent pages.
- immutable requiredExtension = newUsed - info.size - LARGEPAD;
+ immutable requiredExtension = newUsed - (info.size - LARGEPAD);
auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null);
if (extendedSize == 0)
// could not extend, can't satisfy the request
return existingCapacity - offset;
}
+
+ void initThread(ThreadBase t) nothrow @nogc { }
+
+ void cleanupThread(ThreadBase t) nothrow @nogc
+ {
+ cleanupBlkCache(t.tlsGCData);
+ t.tlsGCData = null;
+ }
}
roots.removeAll();
ranges.removeAll();
- toscanConservative.reset();
- toscanPrecise.reset();
+ scanStackConservative.reset(); // scanStackPrecise overlaps with scanStackConservative
}
{
nothrow:
@disable this(this);
- auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
void reset()
{
_length = 0;
if (_p)
{
- os_mem_unmap(_p, _cap * RANGE.sizeof);
+ os_mem_unmap(_p, _cap);
_p = null;
}
_cap = 0;
void push(RANGE rng)
{
- if (_length == _cap) grow();
- _p[_length++] = rng;
+ if ((_length + 1) * RANGE.sizeof > _cap) grow();
+ _p[_length] = rng;
+ _length++;
}
- RANGE pop()
- in { assert(!empty); }
- do
+ void pushReverse(RANGE[] ranges)
{
- return _p[--_length];
- }
-
- bool popLocked(ref RANGE rng)
- {
- if (_length == 0)
- return false;
+ while ((_length + ranges.length) * RANGE.sizeof > _cap)
+ grow();
- stackLock.lock();
- scope(exit) stackLock.unlock();
- if (_length == 0)
- return false;
- rng = _p[--_length];
- return true;
+ // reverse order for depth-first-order traversal
+ foreach_reverse (ref range; ranges)
+ _p[_length++] = range;
}
- ref inout(RANGE) opIndex(size_t idx) inout
- in { assert(idx < _length); }
+ void pop(ref RANGE rng)
+ in { assert(_length > 0); }
do
{
- return _p[idx];
+ rng = _p[--_length];
}
- @property size_t length() const { return _length; }
- @property bool empty() const { return !length; }
+ @property bool empty() const { return !_length; }
+ size_t length() const { return _length; }
+ RANGE* ptr() { return _p; }
private:
void grow()
pragma(inline, false);
enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
- immutable ncap = _cap ? 2 * _cap : initSize / RANGE.sizeof;
- auto p = cast(RANGE*)os_mem_map(ncap * RANGE.sizeof);
+ immutable ncap = _cap ? 2 * _cap : initSize;
+ auto p = cast(RANGE*)os_mem_map(ncap);
if (p is null) onOutOfMemoryError();
debug (VALGRIND) makeMemUndefined(p[0..ncap]);
if (_p !is null)
{
p[0 .. _length] = _p[0 .. _length];
- os_mem_unmap(_p, _cap * RANGE.sizeof);
+ os_mem_unmap(_p, _cap);
}
_p = p;
_cap = ncap;
size_t _length;
RANGE* _p;
- size_t _cap;
+ size_t _cap; // in bytes
}
- ToScanStack!(ScanRange!false) toscanConservative;
- ToScanStack!(ScanRange!true) toscanPrecise;
-
+ union
+ {
+ ToScanStack!(ScanRange!false) scanStackConservative;
+ ToScanStack!(ScanRange!true) scanStackPrecise;
+ }
template scanStack(bool precise)
{
static if (precise)
- alias scanStack = toscanPrecise;
+ alias scanStack = scanStackPrecise;
else
- alias scanStack = toscanConservative;
+ alias scanStack = scanStackConservative;
}
/**
*/
private void mark(bool precise, bool parallel, bool shared_mem)(ScanRange!precise rng) scope nothrow
{
- alias toscan = scanStack!precise;
-
debug(MARK_PRINTF)
printf("marking range: [%p..%p] (%#llx)\n", rng.pbot, rng.ptop, cast(long)(rng.ptop - rng.pbot));
- // limit the amount of ranges added to the toscan stack
+ // limit the amount of ranges added to the scan stack
enum FANOUT_LIMIT = 32;
size_t stackPos;
ScanRange!precise[FANOUT_LIMIT] stack = void;
{
static if (parallel)
{
- if (!toscan.popLocked(rng))
+ if (!scanStackPopLocked(rng))
break; // nothing more to do
}
else
{
- if (toscan.empty)
+ if (scanStack!precise.empty)
break; // nothing more to do
// pop range from global stack and recurse
- rng = toscan.pop();
+ scanStack!precise.pop(rng);
}
}
// printf(" pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
rng.pbot += (void*).sizeof;
if (rng.pbot < rng.ptop)
{
- if (stackPos < stack.length)
- {
- stack[stackPos] = tgt;
- stackPos++;
- continue;
- }
- static if (parallel)
+ if (stackPos >= stack.length)
{
- toscan.stackLock.lock();
- scope(exit) toscan.stackLock.unlock();
+ static if (parallel)
+ {
+ scanStackPushLocked!precise(stack);
+ }
+ else
+ {
+ scanStack!precise.pushReverse(stack);
+ }
+ stackPos = 0;
}
- toscan.push(rng);
- // reverse order for depth-first-order traversal
- foreach_reverse (ref range; stack)
- toscan.push(range);
- stackPos = 0;
+ stack[stackPos] = tgt;
+ stackPos++;
+ continue;
}
LendOfRange:
// continue with last found range
Gcx.instance.numScanThreads = 0;
Gcx.instance.scanThreadData = null;
Gcx.instance.busyThreads = 0;
+ Gcx.instance.stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
- memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof);
+ memset(&Gcx.instance.evStackFilled, 0, Gcx.instance.evStackFilled.sizeof);
memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof);
}
}
uint numScanThreads;
ScanThreadData* scanThreadData;
- Event evStart;
+ Event evStackFilled;
Event evDone;
shared uint busyThreads;
shared uint stoppedThreads;
- bool stopGC;
+ shared bool stopGC;
void markParallel() nothrow
{
if (toscanRoots.empty)
return;
- void** pbot = toscanRoots._p;
- void** ptop = toscanRoots._p + toscanRoots._length;
+ auto pbot = toscanRoots.ptr;
+ auto ptop = pbot + toscanRoots.length;
debug(PARALLEL_PRINTF) printf("markParallel\n");
- size_t pointersPerThread = toscanRoots._length / (numScanThreads + 1);
+ size_t pointersPerThread = toscanRoots.length / (numScanThreads + 1);
if (pointersPerThread > 0)
{
void pushRanges(bool precise)()
{
- alias toscan = scanStack!precise;
- toscan.stackLock.lock();
+ stackLock.lock();
for (int idx = 0; idx < numScanThreads; idx++)
{
- toscan.push(ScanRange!precise(pbot, pbot + pointersPerThread));
+ scanStack!precise.push(ScanRange!precise(pbot, pbot + pointersPerThread));
pbot += pointersPerThread;
}
- toscan.stackLock.unlock();
+ stackLock.unlock();
}
if (ConservativeGC.isPrecise)
pushRanges!true();
}
assert(pbot < ptop);
- busyThreads.atomicOp!"+="(1); // main thread is busy
-
- evStart.setIfInitialized();
+ evStackFilled.setIfInitialized(); // background threads start now
debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot));
+ void pullLoop(bool precise)()
+ {
+ mark!(precise, true, true)(ScanRange!precise(pbot, ptop));
+
+ while (pullFromScanStackImpl!precise())
+ evDone.wait(1.msecs);
+ }
if (ConservativeGC.isPrecise)
- mark!(true, true, true)(ScanRange!true(pbot, ptop, null));
+ pullLoop!(true)();
else
- mark!(false, true, true)(ScanRange!false(pbot, ptop));
-
- busyThreads.atomicOp!"-="(1);
+ pullLoop!(false)();
- debug(PARALLEL_PRINTF) printf("waitForScanDone\n");
- pullFromScanStack();
debug(PARALLEL_PRINTF) printf("waitForScanDone done\n");
}
if (!scanThreadData)
onOutOfMemoryError();
- evStart.initialize(false, false);
+ evStackFilled.initialize(true, false);
evDone.initialize(false, false);
version (Posix)
stopGC = true;
while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead)
{
- evStart.setIfInitialized();
- evDone.wait(dur!"msecs"(1));
+ evStackFilled.setIfInitialized();
+ evDone.wait(1.msecs);
}
for (int idx = 0; idx < numScanThreads; idx++)
}
}
+ evStackFilled.terminate();
evDone.terminate();
- evStart.terminate();
cstdlib.free(scanThreadData);
// scanThreadData = null; // keep non-null to not start again after shutdown
{
while (!stopGC)
{
- evStart.wait();
+ evStackFilled.wait();
pullFromScanStack();
- evDone.setIfInitialized();
+ evDone.setIfInitialized(); // tell main loop we are done
}
stoppedThreads.atomicOp!"+="(1);
+ evDone.setIfInitialized(); // wake up main
}
- void pullFromScanStack() nothrow
+ bool pullFromScanStack() nothrow
{
if (ConservativeGC.isPrecise)
- pullFromScanStackImpl!true();
+ return pullFromScanStackImpl!true();
else
- pullFromScanStackImpl!false();
+ return pullFromScanStackImpl!false();
}
- void pullFromScanStackImpl(bool precise)() nothrow
+ bool pullFromScanStackImpl(bool precise)() nothrow
{
- if (atomicLoad(busyThreads) == 0)
- return;
-
version (Posix) debug (PARALLEL_PRINTF)
{
import core.sys.posix.pthread : pthread_self, pthread_t;
}
ScanRange!precise rng;
- alias toscan = scanStack!precise;
- while (atomicLoad(busyThreads) > 0)
+ stackLock.lock();
+ while (!scanStack!precise.empty())
{
- if (toscan.empty)
- {
- evDone.wait(dur!"msecs"(1));
- continue;
- }
-
busyThreads.atomicOp!"+="(1);
- if (toscan.popLocked(rng))
+ scanStack!precise.pop(rng);
+ if (scanStack!precise.empty)
+ evStackFilled.reset();
+ stackLock.unlock();
+
+ version (Posix) debug (PARALLEL_PRINTF)
{
- version (Posix) debug (PARALLEL_PRINTF)
- {
- printf("scanBackground thread %d scanning range [%p,%lld] from stack\n",
- threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot));
- }
- mark!(precise, true, true)(rng);
+ printf("scanBackground thread %d scanning range [%p,%lld] from stack\n",
+ threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot));
}
+ mark!(precise, true, true)(rng);
+ // returns here only if an empty scan stack has been seen
+
+ stackLock.lock();
busyThreads.atomicOp!"-="(1);
}
+ bool cont = busyThreads > 0;
+ stackLock.unlock();
+
version (Posix) debug (PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId);
+ return cont;
+ }
+
+ auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
+
+ void scanStackPushLocked(bool precise)(ScanRange!precise[] ranges)
+ {
+ stackLock.lock();
+ scope(exit) stackLock.unlock();
+ bool wasEmpty = scanStack!precise.empty;
+
+ scanStack!precise.pushReverse(ranges);
+
+ if (wasEmpty)
+ evStackFilled.setIfInitialized();
+ }
+
+ bool scanStackPopLocked(bool precise)(ref ScanRange!precise rng)
+ {
+ stackLock.lock();
+ scope(exit) stackLock.unlock();
+ if (scanStack!precise.empty())
+ return false;
+
+ scanStack!precise.pop(rng);
+
+ if (scanStack!precise.empty())
+ evStackFilled.reset();
+ return true;
}
}
}
}
+// https://github.com/dlang/dmd/issues/21615
+debug(SENTINEL) {} else // no additional capacity with SENTINEL
+@safe unittest
+{
+ size_t numReallocations = 0;
+ ubyte[] buffer = new ubyte[4096];
+ auto p = &buffer[0];
+ foreach (i; 0 .. 1000) {
+ buffer.length += 4096;
+ if (p !is &buffer[0])
+ {
+ ++numReallocations;
+ p = &buffer[0];
+ }
+ }
+
+ // pick a decently small number, it's unclear where this memory will start out.
+ assert(numReallocations <= 5);
+}
+
/* ============================ MEMSTOMP =============================== */
/// Mark the specified memory region as uninitialized -
import core.internal.container.array;
+import core.thread.threadbase : ThreadBase;
+
import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
static import core.memory;
{
return false;
}
+
+ void initThread(ThreadBase t) nothrow @nogc
+ {
+ }
+
+ void cleanupThread(ThreadBase t) nothrow @nogc
+ {
+ }
}
import core.gc.gcinterface;
+import core.gc.registry : GCThreadInitFunction, threadInit;
+
+import core.thread.threadbase : ThreadBase;
+
import core.internal.container.array;
import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
extern (C) void gc_addRoot(const void* p ) nothrow @nogc;
}
+
class ProtoGC : GC
{
Array!Root roots;
Array!Range ranges;
+ // stored on first use, which should be called whenever rt_init is called.
+ private GCThreadInitFunction _initThreadFn;
+
// Call this function when initializing the real GC
// upon ProtoGC term. This function should be called
// after the real GC is in place.
{
return false;
}
+
+ void initThread(ThreadBase thread) nothrow
+ {
+ if (_initThreadFn is null)
+ {
+ static void defaultThreadInit(ThreadBase base) nothrow @nogc { }
+ import core.gc.config;
+ config.initialize();
+ _initThreadFn = .threadInit(config.gc);
+ if (_initThreadFn is null)
+ _initThreadFn = &defaultThreadInit;
+ }
+
+ _initThreadFn(thread);
+ }
+
+ void cleanupThread(ThreadBase thread)
+ {
+ }
}
return instance.shrinkArrayUsed( slice, existingUsed, atomic );
}
- GC gc_getProxy() nothrow
+ GC gc_getProxy() nothrow @nogc
{
return instance;
}
size_t hashOf(T)(auto ref T val, size_t seed = 0)
if (is(T == enum) && !__traits(isScalar, T))
{
+ pragma(inline, true);
static if (is(T EType == enum)) {} //for EType
return hashOf(cast(EType) val, seed);
}
size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
{
+ pragma(inline, true);
import core.internal.convert : toUbyte;
// FIXME:
// We would like to to do this:
size_t hashOf(T)(auto ref T val, size_t seed = 0)
if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
{
+ pragma(inline, true);
// FIXME:
// We would like to to do this:
//
else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))
//ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields
{
+ pragma(inline, true);
return bytesHashAlignedBy!ElementType(toUbyte(val), seed);
}
else //Other types. CTFE unsupported
{
+ pragma(inline, true);
assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
return bytesHashAlignedBy!ElementType((cast(const(ubyte)*) val.ptr)[0 .. ElementType.sizeof*val.length], seed);
}
else static if (is(T EType == enum) && is(typeof(val[0])))
{
// Enum type whose base type is vector.
+ pragma(inline, true);
return hashOf(cast(EType) val);
}
else static if (__traits(isIntegral, T))
{
static if (is(T V : V*))
{
+ pragma(inline, true);
if (__ctfe)
{
if (val is null) return hashOf(size_t(0), seed);
else static if (is(T EType == enum) && is(typeof(val[0])))
{
// Enum type whose base type is vector.
+ pragma(inline, true);
return hashOf(cast(EType) val, seed);
}
else static if (__traits(isIntegral, val) && T.sizeof <= size_t.sizeof)
}
else
{
+ pragma(inline, true);
import core.internal.convert : toUbyte;
return bytesHashAlignedBy!T(toUbyte(val), seed);
}
@trusted @nogc nothrow pure
size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null)))
{
+ pragma(inline, true);
return 0;
}
@trusted @nogc nothrow pure
size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : typeof(null)))
{
+ pragma(inline, true);
return hashOf(size_t(0), seed);
}
@trusted @nogc nothrow pure
size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
{
+ pragma(inline, true);
if (__ctfe)
{
if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed));
if (!is(T == enum) && (is(T == interface) || is(T == class))
&& canBitwiseHash!T)
{
+ pragma(inline, true);
if (__ctfe) if (val is null) return 0;
return hashOf(cast(const void*) val);
}
if (!is(T == enum) && (is(T == interface) || is(T == class))
&& canBitwiseHash!T)
{
+ pragma(inline, true);
if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
return hashOf(cast(const void*) val, seed);
}
@system pure nothrow @nogc
size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
{
+ pragma(inline, true);
return bytesHashAlignedBy!ubyte((cast(const(ubyte)*) buf)[0 .. len], seed);
}
// handle aligned reads, do the conversion here
private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
{
+ pragma(inline, true);
version (BigEndian)
{
return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]);
dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
+/
@nogc nothrow pure @trusted
-private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
+private size_t _bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
{
auto len = bytes.length;
auto data = bytes.ptr;
return h1;
}
+// precompile bytesHash into the runtime to also get optimized versions in debug builds
+@nogc nothrow pure @trusted
+private size_t _bytesHashAligned(scope const(ubyte)[] bytes, size_t seed)
+{
+ pragma(inline, true);
+ return _bytesHash!true(bytes, seed);
+}
+@nogc nothrow pure @trusted
+private size_t _bytesHashUnaligned(scope const(ubyte)[] bytes, size_t seed)
+{
+ pragma(inline, true);
+ return _bytesHash!false(bytes, seed);
+}
+
+/+
+Params:
+dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
++/
+@nogc nothrow pure @trusted
+private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
+{
+ pragma(inline, true);
+ static if (dataKnownToBeAligned)
+ return _bytesHashAligned(bytes, seed);
+ else
+ return _bytesHashUnaligned(bytes, seed);
+}
+
// Check that bytesHash works with CTFE
pure nothrow @system @nogc unittest
{
{
static if (__traits(isStaticArray, T) && is(T : E[], E))
__doPostblit(cast(E[]) arr);
- else static if (!__traits(compiles, arr[0].__xpostblit))
+ else
{
- alias Unqual_T = Unqual!T;
- foreach (ref elem; (() @trusted => cast(Unqual_T[]) arr)())
+ import core.internal.traits : Unqual;
+ foreach (ref elem; (() @trusted => cast(Unqual!T[]) arr)())
elem.__xpostblit();
}
+ }
+}
+
+// ditto, but with an index to keep track of how many elements have been postblitted
+void __doPostblit(T)(T[] arr, ref size_t i)
+{
+ // infer static postblit type, run postblit if any
+ static if (__traits(hasPostblit, T))
+ {
+ static if (__traits(isStaticArray, T) && is(T : E[], E))
+ __doPostblit(cast(E[]) arr, i);
else
- foreach (ref elem; arr)
- elem.__xpostblit();
+ {
+ i = 0;
+ import core.internal.traits : Unqual;
+ for(auto eptr = cast(Unqual!T*)&arr[0]; i < arr.length; ++i, ++eptr)
+ eptr.__xpostblit();
+ }
}
}
/**
- Turn an Associative Array into a binary compatible struct for static initialization.
-
- This does not implement all the pieces of
- the associative array type in druntime, just enough to create an AA from an
- existing range of key/value pairs.
-
- Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022.
- License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Martin Nowak, Steven Schveighoffer
-*/
+ * template implementation of associative arrays.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak, Steven Schveighoffer, Rainer Schuetze
+ *
+ * Source: $(DRUNTIMESRC core/internal/_newaa.d)
+ *
+ * derived from rt/aaA.d
+ */
module core.internal.newaa;
-import core.memory;
+/// AA version for debuggers, bump whenever changing the layout
+immutable int _aaVersion = 1;
+
+import core.internal.util.math : min, max;
+import core.internal.traits : substInout;
// grow threshold
private enum GROW_NUM = 4;
private enum INIT_NUM_BUCKETS = 8;
// magic hash constants to distinguish empty, deleted, and filled buckets
private enum HASH_EMPTY = 0;
+private enum HASH_DELETED = 0x1;
private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
-private struct Bucket
+/// AA wrapper
+struct AA(K, V)
{
- size_t hash;
- void *entry;
+ Impl!(K,V)* impl;
+ alias impl this;
+
+ @property bool empty() const pure nothrow @nogc @safe
+ {
+ pragma(inline, true);
+ return impl is null || !impl.length;
+ }
+ @property size_t length() const pure nothrow @nogc @safe
+ {
+ pragma(inline, true);
+ return impl is null ? 0 : impl.length;
+ }
+}
+
+/// like core.internal.traits.Unconst, but stripping inout, too
+private template Unconstify(T : const U, U)
+{
+ static if (is(U == inout V, V))
+ alias Unconstify = V;
+ else
+ alias Unconstify = U;
+}
+
+ref _refAA(K, V)(ref V[K] aa) @trusted
+{
+ pragma(inline, true);
+ return *(cast(AA!(substInout!K, substInout!V)*)&aa);
+}
+
+auto _toAA(K, V)(const V[K] aa) @trusted
+{
+ pragma(inline, true);
+ return *(cast(const(AA!(K, V))*)&aa);
+}
+
+auto _toAA(K, V)(inout V[K] aa) @trusted
+{
+ pragma(inline, true);
+ return *(cast(inout(AA!(K, V))*)&aa);
}
-struct Impl
+// for backward compatibility, but should be deprecated
+auto _toAA(K, V)(shared const V[K] aa) @trusted
{
+ pragma(inline, true);
+ return *(cast(AA!(K, V)*)&aa);
+}
+
+// for backward compatibility, but should be deprecated
+auto _toAA(K, V)(shared V[K] aa) @trusted
+{
+ pragma(inline, true);
+ return *(cast(AA!(K, V)*)&aa);
+}
+
+// resolve ambiguity for immutable converting to const and shared const
+auto _toAA(K, V)(immutable V[K] aa) @trusted
+{
+ pragma(inline, true);
+ return *(cast(AA!(K, V)*)&aa);
+}
+
+static struct Entry(K, V)
+{
+ K key;
+ V value;
+}
+
+// backward compatibility conversions
+private ref compat_key(K, K2)(ref K2 key)
+{
+ pragma(inline, true);
+ static if (is(K2 == const(char)[]) && is(K == string))
+ return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key);
+ else
+ return key;
+}
+
+private void _aaMove(V)(ref V src, ref V dst) @trusted
+{
+ import core.stdc.string : memcpy, memset;
+ // move without postblit!?
+ memcpy(&dst, &src, V.sizeof);
+ static if (__traits(isZeroInit, V))
+ memset(&src, 0, V.sizeof);
+ else
+ memcpy(&src, &V.init, V.sizeof);
+}
+
+// mimick behaviour of rt.aaA for initialization
+Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value)
+{
+ static if (__traits(compiles, new Entry!(K, V)(key, value)))
+ {
+ auto entry = new Entry!(K, V)(key, value);
+ }
+ else static if (__traits(compiles, { K k; new Entry!(K, V)(k); }))
+ {
+ auto entry = new Entry!(K, V)(key);
+ _aaMove(value, entry.value);
+ }
+ else
+ {
+ auto entry = new Entry!(K, V);
+ _aaMove(key, entry.key);
+ _aaMove(value, entry.value);
+ }
+ return entry;
+}
+
+// mimick behaviour of rt.aaA for initialization
+Entry!(K, V)* _newEntry(K, V, K2)(ref K2 key)
+{
+ static if (__traits(compiles, new Entry!(K, V)(key)) &&
+ !(is(V == struct) && __traits(isNested, V))) // not detected by "compiles"
+ {
+ auto entry = new Entry!(K, V)(key);
+ }
+ else static if (__traits(compiles, { K2 k; new Entry!(K, V)(k, V.init); }))
+ {
+ // with disabled ctor for V
+ auto entry = new Entry!(K, V)(key, V.init);
+ }
+ else
+ {
+ // with disabled ctor for K and V
+ auto entry = new Entry!(K, V);
+ entry.key = key;
+ }
+ static if (!__traits(isZeroInit, V))
+ {
+ () @trusted { (cast(ubyte*)&entry.value)[0..V.sizeof] = 0; }();
+ }
+ return entry;
+}
+
+template pure_hashOf(K)
+{
+ static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
+ {
+ // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
+ pragma(inline, true)
+ hash_t pure_hashOf(scope const ref K key) @trusted { return hashOf(cast()key); }
+ }
+ else
+ {
+ // for backward compatibility, do not require const in hashOf()
+ hash_t wrap_hashOf(K)(scope const ref K key) @trusted { return hashOf(cast()key); }
+ enum pure_hashOf = cast(hash_t function(scope ref const K key) pure nothrow @nogc @safe) &wrap_hashOf!K;
+ }
+}
+
+// for backward compatibilty pretend the comparison is @safe, pure, etc
+// this also breaks cyclic inference on recursive data types
+template pure_keyEqual(K1, K2 = K1)
+{
+ static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
+ {
+ // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
+ pragma(inline, true)
+ bool pure_keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; }
+ }
+ else
+ {
+ bool keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; }
+ enum pure_keyEqual = cast(bool function(ref const K1, ref const K2) pure nothrow @nogc @safe) &keyEqual;
+ }
+}
+
+private struct Impl(K, V)
+{
+private:
+ alias Bucket = .Bucket!(K, V);
+
+ this(size_t sz /* = INIT_NUM_BUCKETS */) nothrow
+ {
+ buckets = allocBuckets(sz);
+ firstUsed = cast(uint) buckets.length;
+
+ // only for binary compatibility
+ version(D_TypeInfo)
+ entryTI = typeid(Entry!(K, V));
+ hashFn = delegate size_t (scope ref const K key) nothrow pure @nogc @safe {
+ return pure_hashOf!K(key);
+ };
+
+ keysz = cast(uint) K.sizeof;
+ valsz = cast(uint) V.sizeof;
+ valoff = cast(uint) talign(keysz, V.alignof);
+
+ enum ctflags = () {
+ import core.internal.traits;
+ Impl.Flags flags;
+ static if (__traits(hasPostblit, K))
+ flags |= flags.keyHasPostblit;
+ static if (hasIndirections!K || hasIndirections!V)
+ flags |= flags.hasPointers;
+ return flags;
+ } ();
+ flags = ctflags;
+ }
+
Bucket[] buckets;
uint used;
uint deleted;
- const(TypeInfo) entryTI;
+ const(TypeInfo) entryTI; // only for binary compatibility
uint firstUsed;
- immutable uint keysz;
- immutable uint valsz;
- immutable uint valoff;
- Flags flags;
- size_t delegate(scope const void*) nothrow hashFn;
+ immutable uint keysz; // only for binary compatibility
+ immutable uint valsz; // only for binary compatibility
+ immutable uint valoff; // only for binary compatibility
+ Flags flags; // only for binary compatibility
+ size_t delegate(scope ref const K) nothrow pure @nogc @safe hashFn;
enum Flags : ubyte
{
keyHasPostblit = 0x1,
hasPointers = 0x2,
}
+
+ @property size_t length() const pure nothrow @nogc @safe
+ {
+ pragma(inline, true);
+ assert(used >= deleted);
+ return used - deleted;
+ }
+
+ @property size_t dim() const pure nothrow @nogc @safe
+ {
+ pragma(inline, true);
+ return buckets.length;
+ }
+
+ @property size_t mask() const pure nothrow @nogc @safe
+ {
+ pragma(inline, true);
+ return dim - 1;
+ }
+
+ // find the first slot to insert a value with hash
+ size_t findSlotInsert(size_t hash) const pure nothrow @nogc @safe
+ {
+ for (size_t i = hash & mask, j = 1;; ++j)
+ {
+ if (!buckets[i].filled)
+ return i;
+ i = (i + j) & mask;
+ }
+ }
+
+ // lookup a key
+ inout(Bucket)* findSlotLookup(K2)(size_t hash, scope ref const K2 key) inout pure @safe nothrow
+ {
+ for (size_t i = hash & mask, j = 1;; ++j)
+ {
+ auto b = &buckets[i]; // avoid multiple bounds checks
+ if (b.hash == hash && b.entry)
+ if (pure_keyEqual!(K2, K)(key, b.entry.key))
+ return b;
+ if (b.empty)
+ return null;
+ i = (i + j) & mask;
+ }
+ }
+
+ void grow() pure nothrow @safe
+ {
+ // If there are so many deleted entries, that growing would push us
+ // below the shrink threshold, we just purge deleted entries instead.
+ if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
+ resize(dim);
+ else
+ resize(GROW_FAC * dim);
+ }
+
+ void shrink() pure nothrow @safe
+ {
+ if (dim > INIT_NUM_BUCKETS)
+ resize(dim / GROW_FAC);
+ }
+
+ void resize(size_t ndim) pure nothrow @safe
+ {
+ auto obuckets = buckets;
+ buckets = allocBuckets(ndim);
+
+ foreach (ref b; obuckets[firstUsed .. $])
+ if (b.filled)
+ buckets[findSlotInsert(b.hash)] = b;
+
+ firstUsed = 0;
+ used -= deleted;
+ deleted = 0;
+ obuckets.length = 0; // safe to free b/c impossible to reference, but doesn't really free
+ }
+
+ void clear() pure nothrow
+ {
+ // clear all data, but don't change bucket array length
+ buckets[firstUsed .. $] = Bucket.init;
+ deleted = used = 0;
+ firstUsed = cast(uint) dim;
+ }
+
+ size_t calcHash(K2)(ref K2 key) const nothrow pure @nogc @safe
+ {
+ static if(is(K2* : K*)) // ref compatible?
+ hash_t hash = pure_hashOf!K(key);
+ else
+ hash_t hash = pure_hashOf!K2(key);
+ // highest bit is set to distinguish empty/deleted from filled buckets
+ return mix(hash) | HASH_FILLED_MARK;
+ }
+
+ static Bucket[] allocBuckets(size_t dim) pure nothrow @safe
+ {
+ // could allocate with BlkAttr.NO_INTERIOR, but that does not combine
+ // well with arrays and type info for precise scanning
+ return new Bucket[dim];
+ }
}
-private struct AAShell
+//==============================================================================
+// Bucket
+//------------------------------------------------------------------------------
+
+private struct Bucket(K, V)
+{
+private pure nothrow @nogc:
+ size_t hash;
+ Entry!(K, V)* entry;
+
+ @property bool empty() const
+ {
+ pragma(inline, true);
+ return hash == HASH_EMPTY;
+ }
+
+ @property bool deleted() const
+ {
+ pragma(inline, true);
+ return hash == HASH_DELETED;
+ }
+
+ @property bool filled() const @safe
+ {
+ pragma(inline, true);
+ return cast(ptrdiff_t) hash < 0;
+ }
+}
+
+//==============================================================================
+// Helper functions
+//------------------------------------------------------------------------------
+
+private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
{
- Impl *impl;
+ immutable mask = algn - 1;
+ assert(!(mask & algn));
+ return (tsize + mask) & ~mask;
}
+// mix hash to "fix" bad hash functions
private size_t mix(size_t h) @safe pure nothrow @nogc
{
// final mix function of MurmurHash2
return h;
}
-// create a binary-compatible AA structure that can be used directly as an
-// associative array.
-// NOTE: this must only be called during CTFE
-AAShell makeAA(K, V)(V[K] src) @trusted
+private size_t nextpow2(const size_t n) pure nothrow @nogc @safe
{
- assert(__ctfe, "makeAA Must only be called at compile time");
- immutable srclen = src.length;
- assert(srclen <= uint.max);
- alias E = TypeInfo_AssociativeArray.Entry!(K, V);
- if (srclen == 0)
- return AAShell.init;
- // first, determine the size that would be used if we grew the bucket list
- // one element at a time using the standard AA algorithm.
- size_t dim = INIT_NUM_BUCKETS;
- while (srclen * GROW_DEN > dim * GROW_NUM)
- dim = dim * GROW_FAC;
-
- // used during runtime.
- typeof(Impl.hashFn) hashFn = (scope const void* val) {
- auto x = cast(K*)val;
- return hashOf(*x);
- };
+ import core.bitop : bsr;
- Bucket[] buckets;
- // Allocate and fill the buckets
- if (__ctfe)
- buckets = new Bucket[dim];
+ if (!n)
+ return 1;
+
+ const isPowerOf2 = !((n - 1) & n);
+ return 1 << (bsr(n) + !isPowerOf2);
+}
+
+pure nothrow @nogc unittest
+{
+ // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
+ foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16])
+ assert(nextpow2(n) == pow2);
+}
+
+//==============================================================================
+// API Implementation
+//------------------------------------------------------------------------------
+
+/** Allocate associative array data.
+ * Called for `new SomeAA` expression.
+ * Returns:
+ * A new associative array.
+ * Note:
+ * not supported in CTFE
+ */
+V[K] _d_aaNew(K, V)()
+{
+ AA!(K, V) aa;
+ aa.impl = new Impl!(K,V)(INIT_NUM_BUCKETS);
+ return *cast(V[K]*)&aa;
+}
+
+/// Determine number of entries in associative array.
+/// Note:
+/// emulated by the compiler during CTFE
+size_t _d_aaLen(K, V)(inout V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ return aa ? aa.length : 0;
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (aa[key]) expressions when value is mutable.
+ * Params:
+ * aa = associative array
+ * key = reference to the key value
+ * found = returns whether the key was found or a new entry was added
+ * Returns:
+ * if key was in the aa, a mutable pointer to the existing value.
+ * If key was not in the aa, a mutable pointer to newly inserted value which
+ * is set to zero
+ */
+V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found)
+{
+ ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T
+ return _aaGetX!(K, V)(aax, key, found);
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of require, update and _d_aaGetY
+ * Params:
+ * a = associative array
+ * key = reference to the key value
+ * found = true if the value was found
+ * Returns:
+ * if key was in the aa, a mutable pointer to the existing value.
+ * If key was not in the aa, a mutable pointer to newly inserted value which
+ * is set to V.init
+ */
+V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found)
+{
+ ref aa = _refAA!(K, V)(a);
+
+ // lazily alloc implementation
+ if (aa is null)
+ {
+ aa.impl = new Impl!(K, V)(INIT_NUM_BUCKETS);
+ }
+
+ ref key2 = compat_key!(K)(key);
+
+ // get hash and bucket for key
+ immutable hash = aa.calcHash(key2);
+
+ // found a value => return it
+ if (auto p = aa.findSlotLookup(hash, key2))
+ {
+ found = true;
+ return &p.entry.value;
+ }
+
+ auto pi = aa.findSlotInsert(hash);
+ if (aa.buckets[pi].deleted)
+ --aa.deleted;
+ // check load factor and possibly grow
+ else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
+ {
+ aa.grow();
+ pi = aa.findSlotInsert(hash);
+ assert(aa.buckets[pi].empty);
+ }
+
+ // update search cache and allocate entry
+ aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+ ref p = aa.buckets[pi];
+ p.hash = hash;
+ p.entry = _newEntry!(K, V)(key2);
+ return &p.entry.value;
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (aa[key]) expressions when value is not mutable.
+ * Params:
+ * aa = associative array
+ * key = key value
+ * Returns:
+ * pointer to value if present, null otherwise
+ */
+auto _d_aaGetRvalueX(K, V, K2)(inout V[K] aa, auto ref scope K2 key)
+{
+ return _d_aaIn(aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(shared(V[K]) aa, auto ref scope K2 key)
+{
+ // accept shared for backward compatibility, should be deprecated
+ return cast(shared(V)*)_d_aaIn(cast(V[K]) aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(shared const(V[K]) aa, auto ref scope K2 key)
+{
+ // accept shared for backward compatibility, should be deprecated
+ return cast(const shared(V)*)_d_aaIn(cast(V[K]) aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(immutable(V[K]) aa, auto ref scope K2 key)
+{
+ // resolve ambiguity for immutable converting to const and shared const
+ return _d_aaIn((() @trusted => cast(V[K]) aa) (), key);
+}
+
+/***********************************
+ * Creates a new associative array of the same size and copies the contents of
+ * the associative array into it.
+ * Params:
+ * a = The associative array.
+ */
+auto _aaDup(T : V[K], K, V)(T a)
+{
+ auto aa = _toAA!(K, V)(a);
+ immutable len = aa.length;
+ if (len == 0)
+ return null;
+
+ auto impl = new Impl!(K, V)(aa.dim);
+ // copy the entries
+ bool sameHash = aa.hashFn == impl.hashFn; // can be different if coming from template/rt
+ foreach (b; aa.buckets[aa.firstUsed .. $])
+ {
+ if (!b.filled)
+ continue;
+ hash_t hash = sameHash ? b.hash : impl.calcHash(b.entry.key);
+ auto pi = impl.findSlotInsert(hash);
+ auto p = &impl.buckets[pi];
+ p.hash = hash;
+ p.entry = new Entry!(K, V)(b.entry.key, b.entry.value);
+ impl.firstUsed = min(impl.firstUsed, cast(uint)pi);
+ }
+ impl.used = cast(uint) len;
+ return () @trusted { return *cast(Unconstify!V[K]*)&impl; }();
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (key in aa) expressions.
+ * Params:
+ * a = associative array opaque pointer
+ * key = reference to the key value
+ * Returns:
+ * pointer to value if present, null otherwise
+ */
+auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (aa.empty)
+ return null;
+
+ ref key2 = compat_key!(K)(key);
+
+ immutable hash = aa.calcHash(key2);
+ if (auto p = aa.findSlotLookup(hash, key2))
+ return &p.entry.value;
+ return null;
+}
+
+// fake purity for backward compatibility with runtime hooks
+private extern(C) bool gc_inFinalizer() pure nothrow @safe;
+
+/// Delete entry scope const AA, return true if it was present
+auto _d_aaDel(T : V[K], K, V, K2)(T a, auto ref K2 key)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (aa.empty)
+ return false;
+
+ ref key2 = compat_key!(K)(key);
+
+ immutable hash = aa.calcHash(key2);
+ if (auto p = aa.findSlotLookup(hash, key2))
+ {
+ // clear entry
+ p.hash = HASH_DELETED;
+ p.entry = null;
+
+ ++aa.deleted;
+ // `shrink` reallocates, and allocating from a finalizer leads to
+ // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
+ if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !__ctfe && !gc_inFinalizer())
+ aa.shrink();
+
+ return true;
+ }
+ return false;
+}
+
+/// Remove all elements from AA.
+void _aaClear(K, V)(V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (!aa.empty)
+ {
+ aa.clear();
+ }
+}
+
+/// Rehash AA
+V[K] _aaRehash(K, V)(V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (!aa.empty)
+ aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
+ return a;
+}
+
+/// Return a GC allocated array of all values
+auto _aaValues(K, V)(inout V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (aa.empty)
+ return null;
+
+ static if (__traits(compiles, { V val = aa.buckets[0].entry.value; } ))
+ V[] res; // if value has no const indirections
+ else
+ typeof([aa.buckets[0].entry.value]) res; // as mutable as it can get
+ res = new typeof(res[0])[aa.length];
+
+ if (false) // never execute, but infer function attributes from this operation
+ res ~= aa.buckets[0].entry.value;
+
+ size_t i = 0;
+ foreach (b; aa.buckets[aa.firstUsed .. $])
+ {
+ if (!b.filled)
+ continue;
+ import core.lifetime;
+ () @trusted { copyEmplace(b.entry.value, res[i++]); }();
+ }
+ return res;
+}
+
+/// Return a GC allocated array of all keys
+auto _aaKeys(K, V)(inout V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (aa.empty)
+ return null;
+
+ static if (__traits(compiles, { K key = aa.buckets[0].entry.key; } ))
+ K[] res; // if key has no const indirections
else
- assert(0);
+ typeof([aa.buckets[0].entry.key]) res; // as mutable as it can get
+ res = new typeof(res[0])[aa.length];
- assert(buckets.length >= dim);
+ if (false) // never execute, but infer function attributes from this operation
+ res ~= aa.buckets[0].entry.key;
- immutable mask = dim - 1;
- assert((dim & mask) == 0); // must be a power of 2
+ size_t i = 0;
+ foreach (b; aa.buckets[aa.firstUsed .. $])
+ {
+ if (!b.filled)
+ continue;
+ // res ~= b.entry.key;
+ import core.lifetime;
+ () @trusted { copyEmplace(b.entry.key, res[i++]); }();
+ }
+ return res;
+}
- Bucket* findSlotInsert(immutable size_t hash)
+/// foreach opApply over all values
+/// Note:
+/// emulated by the compiler during CTFE
+int _d_aaApply(K, V, DG)(inout V[K] a, DG dg)
+{
+ auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }();
+ if (aa.empty)
+ return 0;
+
+ foreach (b; aa.buckets)
{
- for (size_t i = hash & mask, j = 1;; ++j)
+ if (!b.filled)
+ continue;
+ if (auto res = dg(b.entry.value))
+ return res;
+ }
+ return 0;
+}
+
+int _d_aaApply(K, V, DG)(shared V[K] a, DG dg)
+{
+ return _d_aaApply!(K, V, DG)(cast(V[K]) a, dg);
+}
+
+int _d_aaApply(K, V, DG)(shared const V[K] a, DG dg)
+{
+ return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+int _d_aaApply(K, V, DG)(immutable V[K] a, DG dg)
+{
+ return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+/// foreach opApply over all key/value pairs
+/// Note:
+/// emulated by the compiler during CTFE
+int _d_aaApply2(K, V, DG)(inout V[K] a, DG dg)
+{
+ auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }();
+ if (aa.empty)
+ return 0;
+
+ foreach (b; aa.buckets)
+ {
+ if (!b.filled)
+ continue;
+ if (auto res = dg(b.entry.key, b.entry.value))
+ return res;
+ }
+ return 0;
+}
+
+int _d_aaApply2(K, V, DG)(shared V[K] a, DG dg)
+{
+ return _d_aaApply2!(K, V, DG)(cast(V[K]) a, dg);
+}
+
+int _d_aaApply2(K, V, DG)(shared const V[K] a, DG dg)
+{
+ return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+int _d_aaApply2(K, V, DG)(immutable V[K] a, DG dg)
+{
+ return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+/** Construct an associative array of type ti from corresponding keys and values.
+ * Called for an AA literal `[k1:v1, k2:v2]`.
+ * Params:
+ * keys = array of keys
+ * vals = array of values
+ * Returns:
+ * A new associative array opaque pointer, or null if `keys` is empty.
+ */
+Impl!(K, V)* _d_assocarrayliteralTX(K, V)(K[] keys, V[] vals)
+{
+ assert(keys.length == vals.length);
+
+ immutable length = keys.length;
+
+ if (!length)
+ return null;
+
+ auto aa = new Impl!(K, V)(nextpow2(INIT_DEN * length / INIT_NUM));
+ size_t duplicates = 0;
+ foreach (i; 0 .. length)
+ {
+ immutable hash = aa.calcHash(keys[i]);
+
+ auto p = aa.findSlotLookup!K(hash, keys[i]);
+ if (p)
{
- if (buckets[i].hash == HASH_EMPTY)
- return &buckets[i];
- i = (i + j) & mask;
+ static if (__traits(compiles, p.entry.value = vals[i])) // immutable?
+ p.entry.value = vals[i];
+ else
+ p.entry = _newEntry!(K, V)(keys[i], vals[i]);
+ duplicates++;
+ continue;
+ }
+ auto pi = aa.findSlotInsert(hash);
+ p = &aa.buckets[pi];
+ p.hash = hash;
+ p.entry = _newEntry!(K, V)(keys[i], vals[i]); // todo: move key and value?
+ aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+ }
+ aa.used = cast(uint) (length - duplicates);
+ return aa;
+}
+
+/// compares 2 AAs for equality
+bool _aaEqual(T : AA!(K, V), K, V)(scope T aa1, scope T aa2)
+{
+ if (aa1 is aa2)
+ return true;
+
+ immutable len = aa1.length;
+ if (len != aa2.length)
+ return false;
+
+ if (!len) // both empty
+ return true;
+
+ bool sameHash = aa1.hashFn == aa2.hashFn; // can be different if coming from template/rt
+ // compare the entries
+ foreach (b1; aa1.buckets[aa1.firstUsed .. $])
+ {
+ if (!b1.filled)
+ continue;
+ hash_t hash = sameHash ? b1.hash : aa2.calcHash(b1.entry.key);
+ auto pb2 = aa2.findSlotLookup!K(hash, b1.entry.key);
+ if (pb2 is null || !pure_keyEqual!(V, V)(b1.entry.value, pb2.entry.value)) // rarely, inference on opEqual breaks builds here
+ return false;
+ }
+ return true;
+}
+
+/// compares 2 AAs for equality (compiler hook)
+bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2)
+{
+ scope aa1 = _toAA!(K, V)(a1);
+ scope aa2 = _toAA!(K, V)(a2);
+ return _aaEqual(aa1, aa2);
+}
+
+/// callback from TypeInfo_AssociativeArray.equals (ignore const for now)
+bool _aaOpEqual(K, V)(scope /* const */ AA!(K, V)* aa1, scope /* const */ AA!(K, V)* aa2)
+{
+ return _aaEqual(*aa1, *aa2);
+}
+
+/// compute a hash callback from TypeInfo_AssociativeArray.xtoHash (ignore scope const for now)
+hash_t _aaGetHash(K, V)(/* scope const */ AA!(K, V)* paa)
+{
+ const aa = *paa;
+
+ if (aa.empty)
+ return 0;
+
+ size_t h;
+ foreach (b; aa.buckets)
+ {
+ // use addition here, so that hash is independent of element order
+ if (b.filled)
+ h += hashOf(pure_hashOf!V(b.entry.value), pure_hashOf!K(b.entry.key));
+ }
+
+ return h;
+}
+
+/**
+ * _aaRange implements a ForwardRange
+ */
+struct AARange(K, V)
+{
+ alias Key = substInout!K;
+ alias Value = substInout!V;
+
+ Impl!(Key, Value)* impl;
+ size_t idx;
+ alias impl this;
+}
+
+AARange!(K, V) _aaRange(K, V)(V[K] a)
+{
+ auto aa = _toAA!(K, V)(a);
+ if (!aa)
+ return AARange!(K, V)();
+
+ foreach (i; aa.firstUsed .. aa.dim)
+ {
+ if (aa.buckets[i].filled)
+ return AARange!(K, V)(aa, i);
+ }
+ return AARange!(K, V)(aa, aa.dim);
+}
+
+bool _aaRangeEmpty(K, V)(AARange!(K, V) r)
+{
+ return r.impl is null || r.idx >= r.dim;
+}
+
+K* _aaRangeFrontKey(K, V)(AARange!(K, V) r)
+{
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= r.dim)
+ return null;
+ auto entry = r.buckets[r.idx].entry;
+ return entry is null ? null : &r.buckets[r.idx].entry.key;
+}
+
+V* _aaRangeFrontValue(K, V)(AARange!(K, V) r)
+{
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= r.dim)
+ return null;
+
+ auto entry = r.buckets[r.idx].entry;
+ return entry is null ? null : &r.buckets[r.idx].entry.value;
+}
+
+void _aaRangePopFront(K, V)(ref AARange!(K, V) r)
+{
+ if (r.idx >= r.dim) return;
+ for (++r.idx; r.idx < r.dim; ++r.idx)
+ {
+ if (r.buckets[r.idx].filled)
+ break;
+ }
+}
+
+// test postblit for AA literals
+unittest
+{
+ import core.memory;
+
+ static struct T
+ {
+ ubyte field;
+ static size_t postblit, dtor;
+ this(this)
+ {
+ ++postblit;
+ }
+
+ ~this()
+ {
+ ++dtor;
}
}
- uint firstUsed = cast(uint) buckets.length;
- foreach (k, v; src)
- {
- immutable h = hashOf(k).mix | HASH_FILLED_MARK;
- auto location = findSlotInsert(h);
- immutable nfu = cast(uint) (location - &buckets[0]);
- if (nfu < firstUsed)
- firstUsed = nfu;
- *location = Bucket(h, new E(k, v));
- }
-
- enum flags = () {
- import core.internal.traits;
- Impl.Flags flags;
- static if (__traits(hasPostblit, K))
- flags |= flags.keyHasPostblit;
- static if (hasIndirections!E)
- flags |= flags.hasPointers;
- return flags;
- } ();
- // return the new implementation
- return AAShell(new Impl(buckets, cast(uint)srclen, 0, typeid(E), firstUsed,
- K.sizeof, V.sizeof, E.value.offsetof, flags, hashFn));
+ T t;
+ auto aa1 = [0 : t, 1 : t];
+ assert(T.dtor == 2 && T.postblit == 4);
+ aa1[0] = t;
+ assert(T.dtor == 3 && T.postblit == 5);
+
+ T.dtor = 0;
+ T.postblit = 0;
+
+ auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten
+ assert(T.dtor == 4 && T.postblit == 6);
+
+ T.dtor = 0;
+ T.postblit = 0;
+
+ auto aa3 = [t : 0];
+ assert(T.dtor == 1 && T.postblit == 2);
+ aa3[t] = 1;
+ assert(T.dtor == 1 && T.postblit == 2);
+ aa3.remove(t);
+ assert(T.dtor == 1 && T.postblit == 2);
+ aa3[t] = 2;
+ assert(T.dtor == 1 && T.postblit == 3);
+
+ // dtor will be called by GC finalizers
+ aa1 = null;
+ aa2 = null;
+ aa3 = null;
+ auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor;
+ GC.runFinalizers((cast(char*)dtor1)[0 .. 1]);
+ auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor;
+ GC.runFinalizers((cast(char*)dtor2)[0 .. 1]);
+ assert(T.dtor == 7 && T.postblit == 3);
+}
+
+// create a binary-compatible AA structure that can be used directly as an
+// associative array.
+// NOTE: this must only be called during CTFE
+AA!(K, V) makeAA(K, V)(V[K] src) @trusted
+{
+ assert(__ctfe, "makeAA Must only be called at compile time");
+ // keys and values are cheap operations in CTFE, so just reuse _d_assocarrayliteralTX
+ auto impl = _d_assocarrayliteralTX!(K, V)(cast(K[])src.keys, cast(V[])src.values);
+ auto aa = AA!(K, V)(impl);
+ return aa;
}
unittest
{
alias substInoutForm = T; // prevent matching to the form of alias-this-ed type
}
- else static if (is(T : V[K], K, V)) alias substInoutForm = substInout!V[substInout!K];
- else static if (is(T : U[n], U, size_t n)) alias substInoutForm = substInout!U[n];
- else static if (is(T : U[], U)) alias substInoutForm = substInout!U[];
- else static if (is(T : U*, U)) alias substInoutForm = substInout!U*;
- else alias substInoutForm = T;
+ else static if (is(T == V[K], K, V)) alias substInoutForm = substInout!V[substInout!K];
+ else static if (is(T == U[n], U, size_t n)) alias substInoutForm = substInout!U[n];
+ else static if (is(T == U[], U)) alias substInoutForm = substInout!U[];
+ else static if (is(T == U*, U)) alias substInoutForm = substInout!U*;
+ else alias substInoutForm = T;
+}
+
+unittest
+{
+ // https://github.com/dlang/dmd/issues/21452
+ struct S { int x; }
+ struct T { int x; alias x this; }
+
+ enum EnumInt { a = 123 }
+ enum EnumUInt : uint { a = 123 }
+ enum EnumFloat : float { a = 123 }
+ enum EnumString : string { a = "123" }
+ enum EnumStringW : wstring { a = "123" }
+ enum EnumStruct : S { a = S(7) }
+ enum EnumAliasThis : T { a = T(7) }
+ enum EnumDArray : int[] { a = [1] }
+ enum EnumAArray : int[int] { a = [0 : 1] }
+
+ static assert(substInout!(EnumInt).stringof == "EnumInt");
+ static assert(substInout!(inout(EnumUInt)).stringof == "const(EnumUInt)");
+ static assert(substInout!(EnumFloat).stringof == "EnumFloat");
+ static assert(substInout!(EnumString).stringof == "EnumString");
+ static assert(substInout!(inout(EnumStringW)).stringof == "const(EnumStringW)");
+ static assert(substInout!(EnumStruct).stringof == "EnumStruct");
+ static assert(substInout!(EnumAliasThis).stringof == "EnumAliasThis");
+ static assert(substInout!(EnumDArray).stringof == "EnumDArray");
+ static assert(substInout!(inout(EnumAArray)[int]).stringof == "const(EnumAArray)[int]");
}
/// used to declare an extern(D) function that is defined in a different module
else version (Darwin)
alias real c_long_double;
}
+ else version (AArch64)
+ {
+ version (linux)
+ alias real c_long_double;
+ else version (FreeBSD)
+ alias real c_long_double;
+ else version (OpenBSD)
+ alias real c_long_double;
+ else version (NetBSD)
+ alias real c_long_double;
+ else version (DragonFlyBSD)
+ alias real c_long_double;
+ else version (Solaris)
+ alias real c_long_double;
+ else version (Darwin)
+ alias real c_long_double;
+ }
}
static assert(is(c_long_double), "c_long_double needs to be declared for this platform/architecture.");
else version (AAPCS64)
{
alias va_list = core.internal.vararg.aarch64.va_list;
+ version (DigitalMars)
+ public import core.internal.vararg.aarch64 : __va_list, __va_list_tag;
}
else version (RISCV_Any)
{
///
inout(T)* data() inout @safe { return _Get_data()._Myptr; }
///
- inout(T)[] as_array() scope return inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
+ inout(T)[] as_array() return ref scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
///
ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
///
inout(T)* data() inout @trusted { return __get_pointer(); }
///
- inout(T)[] as_array() scope return inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
+ inout(T)[] as_array() return ref scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
///
ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
/* Determine CPU on which the calling thread is running */
int sched_getcpu();
}
+else version (CRuntime_Musl)
+{
+ int sched_getcpu();
+}
/* Reassociate the calling thread with namespace referred to by fd */
int setns(int fd, int nstype);
struct DIR
{
+ // Managed by OS
}
- static if ( __USE_FILE_OFFSET64 )
- {
- dirent* readdir64(DIR*);
- alias readdir64 readdir;
- }
- else
+ dirent* readdir(DIR*);
+
+ static if (__USE_LARGEFILE64)
{
- dirent* readdir(DIR*);
+ alias readdir64 = readdir;
}
}
else version (CRuntime_UClibc)
void* dli_saddr;
}
}
-else
-version (CRuntime_Musl)
+else version (CRuntime_Musl)
{
enum RTLD_LAZY = 1;
enum RTLD_NOW = 2;
void* dli_saddr;
}
}
-else version (CRuntime_Musl)
-{
- enum {
- RTLD_LAZY = 1,
- RTLD_NOW = 2,
- RTLD_NOLOAD = 4,
- RTLD_NODELETE = 4096,
- RTLD_GLOBAL = 256,
- RTLD_LOCAL = 0,
- }
- int dlclose(void*);
- const(char)* dlerror();
- void* dlopen(const scope char*, int);
- void* dlsym(void*, const scope char*);
-
- int dladdr(scope const void *addr, Dl_info *info);
- struct Dl_info
- {
- const(char)* dli_fname;
- void* dli_fbase;
- const(char)* dli_sname;
- void* dli_saddr;
- }
-}
else version (CRuntime_UClibc)
{
version (X86_Any)
--- /dev/null
+/**
+ * D header file for POSIX.
+ *
+ * $(LINK2 https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html posix endian.h)
+ *
+ * Standards: The Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
+ */
+module core.sys.posix.endian;
+
+version (Posix):
+nothrow:
+@safe:
+@nogc:
+
+import core.bitop;
+
+enum LITTLE_ENDIAN = 1234;
+enum BIG_ENDIAN = 4321;
+enum PDP_ENDIAN = 3412;
+
+version (LittleEndian)
+{
+ enum BYTE_ORDER = LITTLE_ENDIAN;
+
+ pragma(inline, true):
+ ushort htobe16(ushort x) => core.bitop.byteswap(x);
+ ushort htole16(ushort x) => x;
+ ushort be16toh(ushort x) => core.bitop.byteswap(x);
+ ushort le16toh(ushort x) => x;
+
+ uint htobe32(uint x) => core.bitop.bswap(x);
+ uint htole32(uint x) => x;
+ uint be32toh(uint x) => core.bitop.bswap(x);
+ uint le32toh(uint x) => x;
+
+ ulong htobe64(ulong x) => core.bitop.bswap(x);
+ ulong htole64(ulong x) => x;
+ ulong be64toh(ulong x) => core.bitop.bswap(x);
+ ulong le64toh(ulong x) => x;
+}
+else
+{
+ enum BYTE_ORDER = BIG_ENDIAN;
+
+ pragma(inline, true):
+ ushort htobe16(ushort x) => x;
+ ushort htole16(ushort x) => core.bitop.byteswap(x);
+ ushort be16toh(ushort x) => x;
+ ushort le16toh(ushort x) => core.bitop.byteswap(x);
+
+ uint htobe32(uint x) => x;
+ uint htole32(uint x) => core.bitop.bswap(x);
+ uint be32toh(uint x) => x;
+ uint le32toh(uint x) => core.bitop.bswap(x);
+
+ ulong htobe64(ulong x) => x;
+ ulong htole64(ulong x) => core.bitop.bswap(x);
+ ulong be64toh(ulong x) => x;
+ ulong le64toh(ulong x) => core.bitop.bswap(x);
+}
int sched_priority;
int __reserved1;
static if (muslRedirTime64)
- c_long[2] __reserved2;
+ c_long[4] __reserved2;
else
- timespec[2] __reserved2;
+ {
+ struct __timespec32
+ {
+ time_t __reserved_1;
+ c_long __reserved_2;
+ }
+ __timespec32[2] __reserved2;
+ }
int __reserved3;
}
}
}
else version (CRuntime_Musl)
{
- static if ( __USE_FILE_OFFSET64 )
- {
- int fgetpos64(FILE*, fpos_t *);
- alias fgetpos64 fgetpos;
-
- FILE* fopen64(const scope char*, const scope char*);
- alias fopen64 fopen;
-
- FILE* freopen64(const scope char*, const scope char*, FILE*);
- alias freopen64 freopen;
-
- int fseek(FILE*, c_long, int);
-
- int fsetpos64(FILE*, const scope fpos_t*);
- alias fsetpos64 fsetpos;
+ int fgetpos(FILE*, fpos_t *);
+ FILE* fopen(const scope char*, const scope char*);
+ FILE* freopen(const scope char*, const scope char*, FILE*);
+ int fseek(FILE*, c_long, int);
+ int fsetpos(FILE*, const scope fpos_t*);
+ FILE* tmpfile();
- FILE* tmpfile64();
- alias tmpfile64 tmpfile;
- }
- else
+ static if (__USE_LARGEFILE64)
{
- int fgetpos(FILE*, fpos_t *);
- FILE* fopen(const scope char*, const scope char*);
- FILE* freopen(const scope char*, const scope char*, FILE*);
- int fseek(FILE*, c_long, int);
- int fsetpos(FILE*, const scope fpos_t*);
- FILE* tmpfile();
+ alias fgetpos64 = fgetpos;
+ alias fopen64 = fopen;
+ alias freopen64 = freopen;
+ alias fsetpos64 = fsetpos;
+ alias tmpfile64 = tmpfile;
}
}
else version (Solaris)
{
enum L_ctermid = 20;
- static if ( __USE_FILE_OFFSET64 )
- {
- int fseeko64(FILE*, off_t, int);
- alias fseeko64 fseeko;
- }
- else
- {
- int fseeko(FILE*, off_t, int);
- }
+ int fseeko(FILE*, off_t, int);
+ off_t ftello(FILE*);
- static if ( __USE_FILE_OFFSET64 )
+ static if (__USE_LARGEFILE64)
{
- off_t ftello64(FILE*);
- alias ftello64 ftello;
- }
- else
- {
- off_t ftello(FILE*);
+ alias fseeko64 = fseeko;
+ alias ftello64 = ftello;
}
ssize_t getdelim(char**, size_t*, int, FILE*);
void srand48(c_long);
void srandom(uint);
int unlockpt(int);
-
- static if ( __USE_LARGEFILE64 )
- {
- int mkstemp64(char*);
- alias mkstemp64 mkstemp;
- }
- else
- {
int mkstemp(char*);
- }
+ static if (__USE_LARGEFILE64)
+ {
+ alias mkstemp64 = mkstemp;
+ }
}
else version (Solaris)
{
}
else version (CRuntime_Musl)
{
- static if (__USE_LARGEFILE64) void* mmap64(void*, size_t, int, int, int, off_t);
- static if (__USE_FILE_OFFSET64)
- alias mmap = mmap64;
- else
- void* mmap(void*, size_t, int, int, int, off_t);
+ void* mmap(void*, size_t, int, int, int, off_t);
int munmap(void*, size_t);
+
+ static if (__USE_LARGEFILE64)
+ {
+ alias mmap64 = mmap;
+ }
}
else version (CRuntime_UClibc)
{
{
struct stat_t
{
- c_ulong st_dev;
- c_long[3] st_pad1;
+ version (CRuntime_Musl)
+ {
+ dev_t st_dev;
+ c_long[2] st_pad1;
+ }
+ else
+ {
+ c_ulong st_dev;
+ c_long[3] st_pad1;
+ }
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
fsfilcnt_t f_files;
fsfilcnt_t f_ffree;
fsfilcnt_t f_favail;
- static if (true /+__BYTE_ORDER == __LITTLE_ENDIAN+/)
+ version (LittleEndian)
{
c_ulong f_fsid;
byte[2*int.sizeof-c_long.sizeof] __padding;
int statvfs (const char * file, statvfs_t* buf);
int fstatvfs (int fildes, statvfs_t *buf);
- alias statvfs statvfs64;
- alias fstatvfs fstatvfs64;
+ alias statvfs64 = statvfs;
+ alias fstatvfs64 = fstatvfs;
}
else version (NetBSD)
{
package void tlsRTdataInit() nothrow @nogc
{
m_tlsrtdata = rt_tlsgc_init();
+
+ // Let the selected GC initialize anything it needs.
+ import core.internal.gc.proxy : gc_getProxy;
+ gc_getProxy().initThread(this);
}
package void initDataStorage() nothrow
package void destroyDataStorage() nothrow @nogc
{
+ // allow the GC to clean up any resources it allocated for this thread.
+ import core.internal.gc.proxy : gc_getProxy;
+ gc_getProxy().cleanupThread(this);
+
rt_tlsgc_destroy(m_tlsrtdata);
m_tlsrtdata = null;
}
override bool equals(in void* p1, in void* p2) @trusted const
{
- return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2);
+ return xopEquals(p1, p2);
}
override hash_t getHash(scope const void* p) nothrow @trusted const
{
- return _aaGetHash(cast(AA*)p, this);
+ return xtoHash(p);
}
// BUG: need to add the rest of the functions
override @property uint flags() nothrow pure const { return 1; }
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
- static struct Entry(K, V)
- {
- K key;
- V value;
- }
+ private static import core.internal.newaa;
+ alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
TypeInfo value;
TypeInfo key;
TypeInfo entry;
+ bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
+ hash_t function(scope const void*) nothrow @safe xtoHash;
+
+ alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
+ alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
+
override @property size_t talign() nothrow pure const
{
return (char[int]).alignof;
}
private extern (C) Object _d_newclass(const TypeInfo_Class ci);
-private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
- scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
+
+extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe
+{
+ import core.internal.cast_ : areClassInfosEqual;
+
+ if (areClassInfosEqual(oc, c))
+ return true;
+
+ do
+ {
+ if (oc.base && areClassInfosEqual(oc.base, c))
+ return true;
+
+ // Bugzilla 2013: Use depth-first search to calculate offset
+ // from the derived (oc) to the base (c).
+ foreach (iface; oc.interfaces)
+ {
+ if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
+ return true;
+ }
+
+ oc = oc.base;
+ } while (oc);
+
+ return false;
+}
+
+/******************************************
+ * Given a pointer:
+ * If it is an Object, return that Object.
+ * If it is an interface, return the Object implementing the interface.
+ * If it is null, return null.
+ * Else, undefined crash
+ */
+extern(C) Object _d_toObject(return scope void* p) @nogc nothrow pure @trusted
+{
+ if (!p)
+ return null;
+
+ Object o = cast(Object) p;
+ Interface* pi = **cast(Interface***) p;
+
+ /* Interface.offset lines up with ClassInfo.name.ptr,
+ * so we rely on pointers never being less than 64K,
+ * and Objects never being greater.
+ */
+ if (pi.offset < 0x10000)
+ return cast(Object)(p - pi.offset);
+
+ return o;
+}
/**
* Runtime type information about a class.
* $(D Throwable) is thrown from inside a $(D catch) block. The originally
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
+ *
+ * If the 0 bit is set in this, it means the next exception is refcounted,
+ * meaning this object owns that object and should destroy it on
+ * destruction.
+ *
+ * WARNING: This means we are storing an interior pointer to the next in
+ * chain, to signify that the next in the chain is actually reference
+ * counted. We rely on the fact that a pointer to a Throwable is not 1-byte
+ * aligned. It is important to use a local field to indicate reference
+ * counting since we are not allowed to look at GC-allocated references
+ * inside the destructor. It is very important to store this as a void*,
+ * since optimizers can consider an unaligned pointer to not exist.
*/
- private Throwable nextInChain;
+ private void* _nextInChainPtr;
+
+ private @property bool _nextIsRefcounted() @trusted scope pure nothrow @nogc const
+ {
+ if (__ctfe)
+ return false;
+ return (cast(size_t)_nextInChainPtr) & 1;
+ }
private uint _refcount; // 0 : allocated by GC
// 1 : allocated by _d_newThrowable()
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
*/
- @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
+ @property inout(Throwable) next() @trusted inout return scope pure nothrow @nogc
+ {
+ if (__ctfe)
+ return cast(inout(Throwable)) _nextInChainPtr;
+
+ return cast(inout(Throwable)) (_nextInChainPtr - _nextIsRefcounted);
+ }
+
/**
* Replace next in chain with `tail`.
* Use `chainTogether` instead if at all possible.
*/
- @property void next(Throwable tail) @safe scope pure nothrow @nogc
+ @property void next(Throwable tail) @trusted scope pure nothrow @nogc
{
+ void* newTail = cast(void*)tail;
if (tail && tail._refcount)
+ {
++tail._refcount; // increment the replacement *first*
+ ++newTail; // indicate ref counting is used
+ }
- auto n = nextInChain;
- nextInChain = null; // sever the tail before deleting it
+ auto n = next;
+ auto nrc = _nextIsRefcounted;
+ _nextInChainPtr = null; // sever the tail before deleting it
- if (n && n._refcount)
+ if (nrc)
_d_delThrowable(n); // now delete the old tail
- nextInChain = tail; // and set the new tail
+ _nextInChainPtr = newTail; // and set the new tail
}
/**
int opApply(scope int delegate(Throwable) dg)
{
int result = 0;
- for (Throwable t = this; t; t = t.nextInChain)
+ for (Throwable t = this; t; t = t.next)
{
result = dg(t);
if (result)
return e2;
if (!e2)
return e1;
- if (e2.refcount())
- ++e2.refcount();
- for (auto e = e1; 1; e = e.nextInChain)
+ for (auto e = e1; 1; e = e.next)
{
- if (!e.nextInChain)
+ if (!e.next)
{
- e.nextInChain = e2;
+ e.next = e2;
break;
}
}
@nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
this.msg = msg;
- this.nextInChain = nextInChain;
- if (nextInChain && nextInChain._refcount)
- ++nextInChain._refcount;
+ this.next = nextInChain;
//this.info = _d_traceContext();
}
@trusted nothrow ~this()
{
- if (nextInChain && nextInChain._refcount)
- _d_delThrowable(nextInChain);
+ if (_nextIsRefcounted)
+ _d_delThrowable(next);
+
// handle owned traceinfo
if (infoDeallocator !is null)
{
auto e = new Exception("msg");
assert(e.file == __FILE__);
assert(e.line == __LINE__ - 2);
- assert(e.nextInChain is null);
+ assert(e._nextInChainPtr is null);
assert(e.msg == "msg");
}
auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42);
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.nextInChain !is null);
+ assert(e._nextInChainPtr !is null);
assert(e.msg == "msg");
}
auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.nextInChain !is null);
+ assert(e._nextInChainPtr !is null);
assert(e.msg == "msg");
}
auto e = new Error("msg");
assert(e.file is null);
assert(e.line == 0);
- assert(e.nextInChain is null);
+ assert(e._nextInChainPtr is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
auto e = new Error("msg", new Exception("It's an Exception!"));
assert(e.file is null);
assert(e.line == 0);
- assert(e.nextInChain !is null);
+ assert(e._nextInChainPtr !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.nextInChain !is null);
+ assert(e._nextInChainPtr !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
}
-extern (C)
-{
- // from druntime/src/rt/aaA.d
-
- private struct AA { void* impl; }
- // size_t _aaLen(in AA aa) pure nothrow @nogc;
- private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
- private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow;
- // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
- inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow;
- inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
- void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow;
- void _aaClear(AA aa) pure nothrow;
-
- // alias _dg_t = extern(D) int delegate(void*);
- // int _aaApply(AA aa, size_t keysize, _dg_t dg);
+public import core.internal.newaa : _d_aaIn, _d_aaDel, _d_aaNew, _d_aaEqual, _d_assocarrayliteralTX;
+public import core.internal.newaa : _d_aaLen, _d_aaGetY, _d_aaGetRvalueX, _d_aaApply, _d_aaApply2;
+// public import core.exception : onRangeError; // causes extra messages with -transition=fields
+extern (C) noreturn _d_arraybounds(string file, uint line) @trusted pure nothrow @nogc;
- // alias _dg2_t = extern(D) int delegate(void*, void*);
- // int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
+private import core.internal.newaa;
- private struct AARange { AA impl; size_t idx; }
- AARange _aaRange(AA aa) pure nothrow @nogc @safe;
- bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe;
- void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe;
- void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
- void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
-
- int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
- hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
-
- /*
- _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
- This is a typesystem hole, however this is existing hole.
- Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus
- copiler allowed to create AA literal with keys, which have impure unsafe toHash methods.
- */
- void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure;
-}
-
-void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
+void* aaLiteral(Key, Value)(Key[] keys, Value[] values)
{
- return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
+ return _d_assocarrayliteralTX(keys, values);
}
// Lower an Associative Array to a newaa struct for static initialization.
auto _aaAsStruct(K, V)(V[K] aa) @safe
{
- import core.internal.newaa : makeAA;
assert(__ctfe);
return makeAA!(K, V)(aa);
}
*/
void clear(Value, Key)(Value[Key] aa) @trusted
{
- _aaClear(*cast(AA *) &aa);
+ _aaClear(aa);
}
/** ditto */
void clear(Value, Key)(Value[Key]* aa) @trusted
{
- _aaClear(*cast(AA *) aa);
+ (*aa).clear();
}
///
* aa = The associative array.
* Returns:
* The rehashed associative array.
+ * Note:
+ * emulated by the compiler during CTFE
*/
-T rehash(T : Value[Key], Value, Key)(T aa)
+Value[Key] rehash(Value, Key)(Value[Key] aa)
{
- _aaRehash(cast(AA*)&aa, typeid(Value[Key]));
- return aa;
+ return _aaRehash(aa);
}
/** ditto */
-T rehash(T : Value[Key], Value, Key)(T* aa)
+Value[Key] rehash(T : Value[Key], Value, Key)(T* aa)
{
- _aaRehash(cast(AA*)aa, typeid(Value[Key]));
- return *aa;
+ return (*aa).rehash(); // CTFE only intercepts the non-pointer overload
}
/** ditto */
-T rehash(T : shared Value[Key], Value, Key)(T aa)
+Value[Key] rehash(T : shared Value[Key], Value, Key)(auto ref T aa)
{
- _aaRehash(cast(AA*)&aa, typeid(Value[Key]));
- return aa;
+ return (cast(Value[Key])aa).rehash(); // CTFE only intercepts the V[K] overload
}
/** ditto */
-T rehash(T : shared Value[Key], Value, Key)(T* aa)
+Value[Key] rehash(T : shared Value[Key], Value, Key)(T* aa)
{
- _aaRehash(cast(AA*)aa, typeid(Value[Key]));
- return *aa;
+ return (cast(Value[Key])*aa).rehash(); // CTFE only intercepts the non-pointer overload
}
/***********************************
* the associative array into it.
* Params:
* aa = The associative array.
+ * Note:
+ * emulated by the compiler during CTFE
*/
-V[K] dup(T : V[K], K, V)(T aa)
+auto dup(T : V[K], K, V)(T aa)
{
- //pragma(msg, "K = ", K, ", V = ", V);
-
// Bug10720 - check whether V is copyable
static assert(is(typeof({ V v = aa[K.init]; })),
"cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
- V[K] result;
-
- //foreach (k, ref v; aa)
- // result[k] = v; // Bug13701 - won't work if V is not mutable
-
- ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
- {
- import core.stdc.string : memcpy;
-
- void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
- memcpy(pv, &v, V.sizeof);
- return *cast(V*)pv;
- }
-
- foreach (k, ref v; aa)
- {
- static if (!__traits(hasPostblit, V))
- duplicateElem(k, v);
- else static if (__traits(isStaticArray, V))
- _doPostblit(duplicateElem(k, v)[]);
- else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
- (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
- else
- duplicateElem(k, v).__xpostblit();
- }
-
- return result;
+ return _aaDup(aa);
}
/** ditto */
-V[K] dup(T : V[K], K, V)(T* aa)
+auto dup(T : V[K], K, V)(T* aa)
{
return (*aa).dup;
}
}
// this should never be made public.
-private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
+private auto _aaToRange(K, V)(auto ref inout V[K] aa) @trusted
{
- // ensure we are dealing with a genuine AA.
- static if (is(const(V[K]) == const(T)))
- alias realAA = aa;
- else
- const(V[K]) realAA = aa;
- return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
+ import core.internal.traits : substInout;
+
+ alias K2 = substInout!K;
+ alias V2 = substInout!V;
+ auto aa2 = cast(V2[K2])aa;
+ return _aaRange(aa2);
}
/***********************************
{
import core.internal.traits : substInout;
+ const(V[K]) aa2 = aa;
+
static struct Result
{
- AARange r;
+ typeof(_aaToRange(aa2)) r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
@property ref front() @trusted
{
- return *cast(substInout!K*) _aaRangeFrontKey(r);
+ return *cast(substInout!K*)_aaRangeFrontKey(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
- return Result(_aaToRange(aa));
+ return Result(_aaToRange(aa2));
}
/** ditto */
-auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
+auto byKey(K, V)(V[K]* aa) pure nothrow @nogc
{
return (*aa).byKey();
}
{
import core.internal.traits : substInout;
+ const(V[K]) aa2 = aa;
+
static struct Result
{
- AARange r;
+ typeof(_aaToRange(aa2)) r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
@property ref front() @trusted
{
- return *cast(substInout!V*) _aaRangeFrontValue(r);
+ return *cast(substInout!V*)_aaRangeFrontValue(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
- return Result(_aaToRange(aa));
+ return Result(_aaToRange(aa2));
}
/** ditto */
-auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
+auto byValue(K, V)(V[K]* aa) pure nothrow @nogc
{
return (*aa).byValue();
}
{
import core.internal.traits : substInout;
+ const(V[K]) aa2 = aa;
+
static struct Result
{
- AARange r;
+ typeof(_aaToRange(aa2)) r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
{
// We save the pointers here so that the Pair we return
// won't mutate when Result.popFront is called afterwards.
- private void* keyp;
- private void* valp;
+ private const(substInout!K)* keyp;
+ private const(substInout!V)* valp;
@property ref key() inout @trusted
{
- return *cast(substInout!K*) keyp;
+ return *cast(K*)keyp;
}
@property ref value() inout @trusted
{
- return *cast(substInout!V*) valp;
+ return *cast(V*)valp;
}
}
return Pair(_aaRangeFrontKey(r),
@property Result save() { return this; }
}
- return Result(_aaToRange(aa));
+ return Result(_aaToRange(aa2));
}
/** ditto */
* aa = The associative array.
* Returns:
* A dynamic array containing a copy of the keys.
+ * Note:
+ * emulated by the compiler during CTFE
*/
-Key[] keys(T : Value[Key], Value, Key)(T aa) @property
+auto keys(Value, Key)(inout Value[Key] aa) @property
{
- // ensure we are dealing with a genuine AA.
- static if (is(const(Value[Key]) == const(T)))
- alias realAA = aa;
- else
- const(Value[Key]) realAA = aa;
- auto res = () @trusted {
- auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
- return *cast(Key[]*)&a;
- }();
- static if (__traits(hasPostblit, Key))
- _doPostblit(res);
- return res;
+ return _aaKeys!(Key, Value)(aa);
}
/** ditto */
-Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
+auto keys(T : Value[Key], Value, Key)(T *aa) @property
{
- return (*aa).keys;
+ return (*aa).keys; // CTFE only intercepts the non-pointer overload
}
///
* aa = The associative array.
* Returns:
* A dynamic array containing a copy of the values.
+ * Note:
+ * emulated by the compiler during CTFE
*/
-Value[] values(T : Value[Key], Value, Key)(T aa) @property
+auto values(Value, Key)(inout Value[Key] aa) @property
{
- // ensure we are dealing with a genuine AA.
- static if (is(const(Value[Key]) == const(T)))
- alias realAA = aa;
- else
- const(Value[Key]) realAA = aa;
- auto res = () @trusted {
- auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
- return *cast(Value[]*)&a;
- }();
- static if (__traits(hasPostblit, Value))
- _doPostblit(res);
- return res;
+ return _aaValues!(Key, Value)(aa);
}
/** ditto */
-Value[] values(T : Value[Key], Value, Key)(T *aa) @property
+auto values(T : Value[Key], Value, Key)(T *aa) @property
{
- return (*aa).values;
+ return (*aa).values; // CTFE only intercepts the non-pointer overload
}
///
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
bool found;
- // if key is @safe-ly copyable, `require` can infer @safe
- static if (isSafeCopyable!K)
- {
- auto p = () @trusted
- {
- return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
- } ();
- }
- else
- {
- auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
- }
+ auto p = _aaGetX(aa, key, found);
if (found)
return *p;
- else
- {
- *p = value; // Not `return (*p = value)` since if `=` is overloaded
- return *p; // this might not return a ref to the left-hand side.
- }
+ *p = value; // Not `return (*p = value)` since if `=` is overloaded
+ return *p; // this might not return a ref to the left-hand side.
}
///
* 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
+ * `update` accepts a value parameter. If it returns a value, the value is
* set for `key`.
* Params:
* aa = The associative array.
* 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.
+ * Takes a V 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)))
{
bool found;
- // if key is @safe-ly copyable, `update` may infer @safe
- static if (isSafeCopyable!K)
- {
- auto p = () @trusted
- {
- return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
- } ();
- }
- else
- {
- auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
- }
+ auto p = _aaGetX(aa, key, found);
if (!found)
*p = create();
else
a.reserve(10);
}
-// HACK: This is a lie. `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary
-// for now to prevent breaking code.
-private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
-
/**
Assume that it is safe to append to this array. Appends made to this array
after calling this function may append in place, even if the array was a
*/
auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system
{
- _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr));
+ import core.internal.array.capacity : _d_arrayshrinkfit;
+ import core.internal.traits : Unqual;
+
+ alias Unqual_Tarr = Unqual!T[];
+ enum isshared = is(T == shared);
+ _d_arrayshrinkfit(cast(Unqual_Tarr)arr, isshared);
+
return arr;
}
public import core.internal.array.construction : _d_newarrayTTrace;
public import core.internal.array.construction : _d_newarraymTXTrace;
public import core.internal.array.capacity: _d_arraysetlengthTTrace;
+ public import core.internal.array.construction : _d_arrayliteralTXTrace;
}
public import core.internal.array.appending : _d_arrayappendcTX;
public import core.internal.array.comparison : __cmp;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.construction : _d_newarrayT;
public import core.internal.array.construction : _d_newarraymTX;
+public import core.internal.array.construction : _d_arrayliteralTX;
public import core.internal.array.arrayassign : _d_arrayassign_l;
public import core.internal.array.arrayassign : _d_arrayassign_r;
public import core.internal.array.arrayassign : _d_arraysetassign;
public import core.internal.array.capacity : _d_arraysetlengthT;
+public import core.internal.cast_: _d_cast;
public import core.internal.dassert: _d_assert_fail;
+++ /dev/null
-/**
- * Implementation of associative arrays.
- *
- * Copyright: Copyright Digital Mars 2000 - 2015.
- * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- * Source: $(DRUNTIMESRC rt/_aaA.d)
- */
-module rt.aaA;
-
-/// AA version for debuggers, bump whenever changing the layout
-extern (C) immutable int _aaVersion = 1;
-
-import core.memory : GC;
-import core.internal.util.math : min, max;
-
-// grow threshold
-private enum GROW_NUM = 4;
-private enum GROW_DEN = 5;
-// shrink threshold
-private enum SHRINK_NUM = 1;
-private enum SHRINK_DEN = 8;
-// grow factor
-private enum GROW_FAC = 4;
-// growing the AA doubles it's size, so the shrink threshold must be
-// smaller than half the grow threshold to have a hysteresis
-static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN);
-// initial load factor (for literals), mean of both thresholds
-private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2;
-private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
-
-private enum INIT_NUM_BUCKETS = 8;
-// magic hash constants to distinguish empty, deleted, and filled buckets
-private enum HASH_EMPTY = 0;
-private enum HASH_DELETED = 0x1;
-private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
-
-/// Opaque AA wrapper
-struct AA
-{
- Impl* impl;
- alias impl this;
-
- private @property bool empty() const pure nothrow @nogc @safe
- {
- return impl is null || !impl.length;
- }
-}
-
-private struct Impl
-{
-private:
- this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow
- {
- keysz = cast(uint) ti.key.tsize;
- valsz = cast(uint) ti.value.tsize;
- buckets = allocBuckets(sz);
- firstUsed = cast(uint) buckets.length;
- valoff = cast(uint) talign(keysz, ti.value.talign);
- hashFn = &ti.key.getHash;
-
- import rt.lifetime : hasPostblit, unqualify;
-
- if (hasPostblit(unqualify(ti.key)))
- flags |= Flags.keyHasPostblit;
- if ((ti.key.flags | ti.value.flags) & 1)
- flags |= Flags.hasPointers;
-
- entryTI = ti.entry;
- }
-
- Bucket[] buckets;
- uint used;
- uint deleted;
- const(TypeInfo) entryTI;
- uint firstUsed;
- immutable uint keysz;
- immutable uint valsz;
- immutable uint valoff;
- Flags flags;
-
- // function that calculates hash of a key. Set on creation
- // the parameter is a pointer to the key.
- size_t delegate(scope const void*) nothrow hashFn;
-
- enum Flags : ubyte
- {
- none = 0x0,
- keyHasPostblit = 0x1,
- hasPointers = 0x2,
- }
-
- @property size_t length() const pure nothrow @nogc @safe
- {
- assert(used >= deleted);
- return used - deleted;
- }
-
- @property size_t dim() const pure nothrow @nogc @safe
- {
- return buckets.length;
- }
-
- @property size_t mask() const pure nothrow @nogc
- {
- return dim - 1;
- }
-
- // find the first slot to insert a value with hash
- inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc
- {
- for (size_t i = hash & mask, j = 1;; ++j)
- {
- if (!buckets[i].filled)
- return &buckets[i];
- i = (i + j) & mask;
- }
- }
-
- // lookup a key
- inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
- {
- for (size_t i = hash & mask, j = 1;; ++j)
- {
- if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry))
- return &buckets[i];
- else if (buckets[i].empty)
- return null;
- i = (i + j) & mask;
- }
- }
-
- void grow(scope const TypeInfo keyti) pure nothrow
- {
- // If there are so many deleted entries, that growing would push us
- // below the shrink threshold, we just purge deleted entries instead.
- if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
- resize(dim);
- else
- resize(GROW_FAC * dim);
- }
-
- void shrink(scope const TypeInfo keyti) pure nothrow
- {
- if (dim > INIT_NUM_BUCKETS)
- resize(dim / GROW_FAC);
- }
-
- void resize(size_t ndim) pure nothrow
- {
- auto obuckets = buckets;
- buckets = allocBuckets(ndim);
-
- foreach (ref b; obuckets[firstUsed .. $])
- if (b.filled)
- *findSlotInsert(b.hash) = b;
-
- firstUsed = 0;
- used -= deleted;
- deleted = 0;
- GC.free(obuckets.ptr); // safe to free b/c impossible to reference
- }
-
- void clear() pure nothrow @trusted
- {
- import core.stdc.string : memset;
- // clear all data, but don't change bucket array length
- memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof);
- deleted = used = 0;
- firstUsed = cast(uint) dim;
- }
-}
-
-//==============================================================================
-// Bucket
-//------------------------------------------------------------------------------
-
-private struct Bucket
-{
-private pure nothrow @nogc:
- size_t hash;
- void* entry;
-
- @property bool empty() const
- {
- return hash == HASH_EMPTY;
- }
-
- @property bool deleted() const
- {
- return hash == HASH_DELETED;
- }
-
- @property bool filled() const @safe
- {
- return cast(ptrdiff_t) hash < 0;
- }
-}
-
-Bucket[] allocBuckets(size_t dim) @trusted pure nothrow
-{
- enum attr = GC.BlkAttr.NO_INTERIOR;
- immutable sz = dim * Bucket.sizeof;
- return (cast(Bucket*) GC.calloc(sz, attr))[0 .. dim];
-}
-
-//==============================================================================
-// Entry
-//------------------------------------------------------------------------------
-
-private void* allocEntry(scope const Impl* aa, scope const void* pkey)
-{
- import rt.lifetime : _d_newitemU;
- import core.stdc.string : memcpy, memset;
-
- immutable akeysz = aa.valoff;
- void* res = void;
- if (aa.entryTI)
- res = _d_newitemU(aa.entryTI);
- else
- {
- auto flags = (aa.flags & Impl.Flags.hasPointers) ? 0 : GC.BlkAttr.NO_SCAN;
- res = GC.malloc(akeysz + aa.valsz, flags);
- }
-
- memcpy(res, pkey, aa.keysz); // copy key
- memset(res + akeysz, 0, aa.valsz); // zero value
-
- return res;
-}
-
-private bool hasDtor(const TypeInfo ti) pure nothrow
-{
- import rt.lifetime : unqualify;
-
- if (typeid(ti) is typeid(TypeInfo_Struct))
- if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor)
- return true;
- if (typeid(ti) is typeid(TypeInfo_StaticArray))
- return hasDtor(unqualify(ti.next));
-
- return false;
-}
-
-private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow
-{
- // classes are references
- const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
- return isNoClass ? ti.rtInfo() : rtinfoHasPointers;
-}
-
-unittest
-{
- void test(K, V)()
- {
- static struct Entry
- {
- K key;
- V val;
- }
- auto keyti = typeid(K);
- auto valti = typeid(V);
- auto valrti = getRTInfo(valti);
- auto keyrti = getRTInfo(keyti);
-
- auto impl = new Impl(typeid(V[K]));
- if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers)
- {
- assert(!(impl.flags & Impl.Flags.hasPointers));
- }
- else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers)
- {
- assert(impl.flags & Impl.Flags.hasPointers);
- }
- else
- {
- auto rtInfo = cast(size_t*) impl.entryTI.rtInfo();
- auto refInfo = cast(size_t*) typeid(Entry).rtInfo();
- assert(rtInfo[0] == refInfo[0]); // size
- enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof;
- size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord;
- foreach (i; 0 .. words)
- assert(rtInfo[1 + i] == refInfo[i + 1]);
- }
- }
- test!(long, int)();
- test!(string, string);
- test!(ubyte[16], Object);
-
- static struct Small
- {
- ubyte[16] guid;
- string name;
- }
- test!(string, Small);
-
- static struct Large
- {
- ubyte[1024] data;
- string[412] names;
- ubyte[1024] moredata;
- }
- version (OnlyLowMemUnittests) {} else
- test!(Large, Large);
-}
-
-//==============================================================================
-// Helper functions
-//------------------------------------------------------------------------------
-
-private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
-{
- immutable mask = algn - 1;
- assert(!(mask & algn));
- return (tsize + mask) & ~mask;
-}
-
-// mix hash to "fix" bad hash functions
-private size_t mix(size_t h) @safe pure nothrow @nogc
-{
- // final mix function of MurmurHash2
- enum m = 0x5bd1e995;
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
-}
-
-private size_t calcHash(scope const void *pkey, scope const Impl* impl) nothrow
-{
- immutable hash = impl.hashFn(pkey);
- // highest bit is set to distinguish empty/deleted from filled buckets
- return mix(hash) | HASH_FILLED_MARK;
-}
-
-private size_t nextpow2(const size_t n) pure nothrow @nogc
-{
- import core.bitop : bsr;
-
- if (!n)
- return 1;
-
- const isPowerOf2 = !((n - 1) & n);
- return 1 << (bsr(n) + !isPowerOf2);
-}
-
-pure nothrow @nogc unittest
-{
- // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
- foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16])
- assert(nextpow2(n) == pow2);
-}
-
-//==============================================================================
-// API Implementation
-//------------------------------------------------------------------------------
-
-/** Allocate associative array data.
- * Called for `new SomeAA` expression.
- * Params:
- * ti = TypeInfo for the associative array
- * Returns:
- * A new associative array.
- */
-extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
-{
- return new Impl(ti);
-}
-
-/// Determine number of entries in associative array.
-extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
-{
- return aa ? aa.length : 0;
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (aa[key]) expressions when value is mutable.
- * Params:
- * paa = associative array opaque pointer
- * ti = TypeInfo for the associative array
- * valsz = ignored
- * pkey = pointer to the key value
- * Returns:
- * if key was in the aa, a mutable pointer to the existing value.
- * If key was not in the aa, a mutable pointer to newly inserted value which
- * is set to all zeros
- */
-extern (C) void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti,
- const size_t valsz, scope const void* pkey)
-{
- bool found;
- return _aaGetX(paa, ti, valsz, pkey, found);
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of require
- * Params:
- * paa = associative array opaque pointer
- * ti = TypeInfo for the associative array
- * valsz = ignored
- * pkey = pointer to the key value
- * found = true if the value was found
- * Returns:
- * if key was in the aa, a mutable pointer to the existing value.
- * If key was not in the aa, a mutable pointer to newly inserted value which
- * is set to all zeros
- */
-extern (C) void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti,
- const size_t valsz, scope const void* pkey, out bool found)
-{
- // lazily alloc implementation
- AA aa = *paa;
- if (aa is null)
- {
- aa = new Impl(ti);
- *paa = aa;
- }
-
- // get hash and bucket for key
- immutable hash = calcHash(pkey, aa);
-
- // found a value => return it
- if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
- {
- found = true;
- return p.entry + aa.valoff;
- }
-
- auto p = aa.findSlotInsert(hash);
- if (p.deleted)
- --aa.deleted;
- // check load factor and possibly grow
- else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
- {
- aa.grow(ti.key);
- p = aa.findSlotInsert(hash);
- assert(p.empty);
- }
-
- // update search cache and allocate entry
- aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
- p.hash = hash;
- p.entry = allocEntry(aa, pkey);
- // postblit for key
- if (aa.flags & Impl.Flags.keyHasPostblit)
- {
- import rt.lifetime : __doPostblit, unqualify;
-
- __doPostblit(p.entry, aa.keysz, unqualify(ti.key));
- }
- // return pointer to value
- return p.entry + aa.valoff;
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (aa[key]) expressions when value is not mutable.
- * Params:
- * aa = associative array opaque pointer
- * keyti = TypeInfo for the key
- * valsz = ignored
- * pkey = pointer to the key value
- * Returns:
- * pointer to value if present, null otherwise
- */
-extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz,
- scope const void* pkey)
-{
- return _aaInX(aa, keyti, pkey);
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (key in aa) expressions.
- * Params:
- * aa = associative array opaque pointer
- * keyti = TypeInfo for the key
- * pkey = pointer to the key value
- * Returns:
- * pointer to value if present, null otherwise
- */
-extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey)
-{
- if (aa.empty)
- return null;
-
- immutable hash = calcHash(pkey, aa);
- if (auto p = aa.findSlotLookup(hash, pkey, keyti))
- return p.entry + aa.valoff;
- return null;
-}
-
-/// Delete entry scope const AA, return true if it was present
-extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey)
-{
- if (aa.empty)
- return false;
-
- immutable hash = calcHash(pkey, aa);
- if (auto p = aa.findSlotLookup(hash, pkey, keyti))
- {
- // clear entry
- p.hash = HASH_DELETED;
- p.entry = null;
-
- ++aa.deleted;
- // `shrink` reallocates, and allocating from a finalizer leads to
- // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
- if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer())
- aa.shrink(keyti);
-
- return true;
- }
- return false;
-}
-
-/// Remove all elements from AA.
-extern (C) void _aaClear(AA aa) pure nothrow @safe
-{
- if (!aa.empty)
- {
- aa.clear();
- }
-}
-
-/// Rehash AA
-extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow
-{
- AA aa = *paa;
- if (!aa.empty)
- aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
- return aa;
-}
-
-/// Return a GC allocated array of all values
-extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz,
- const TypeInfo tiValueArray) pure nothrow
-{
- if (aa.empty)
- return null;
-
- import rt.lifetime : _d_newarrayU;
-
- auto res = _d_newarrayU(tiValueArray, aa.length).ptr;
- auto pval = res;
-
- immutable off = aa.valoff;
- foreach (b; aa.buckets[aa.firstUsed .. $])
- {
- if (!b.filled)
- continue;
- pval[0 .. valsz] = cast(void[]) b.entry[off .. valsz + off];
- pval += valsz;
- }
- // postblit is done in object.values
- return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
-}
-
-/// Return a GC allocated array of all keys
-extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow
-{
- if (aa.empty)
- return null;
-
- import rt.lifetime : _d_newarrayU;
-
- auto res = _d_newarrayU(tiKeyArray, aa.length).ptr;
- auto pkey = res;
-
- foreach (b; aa.buckets[aa.firstUsed .. $])
- {
- if (!b.filled)
- continue;
- pkey[0 .. keysz] = cast(void[]) b.entry[0 .. keysz];
- pkey += keysz;
- }
- // postblit is done in object.keys
- return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
-}
-
-// opApply callbacks are extern(D)
-extern (D) alias dg_t = int delegate(void*);
-extern (D) alias dg2_t = int delegate(void*, void*);
-
-/// foreach opApply over all values
-extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg)
-{
- if (aa.empty)
- return 0;
-
- immutable off = aa.valoff;
- foreach (b; aa.buckets)
- {
- if (!b.filled)
- continue;
- if (auto res = dg(b.entry + off))
- return res;
- }
- return 0;
-}
-
-/// foreach opApply over all key/value pairs
-extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
-{
- if (aa.empty)
- return 0;
-
- immutable off = aa.valoff;
- foreach (b; aa.buckets)
- {
- if (!b.filled)
- continue;
- if (auto res = dg(b.entry, b.entry + off))
- return res;
- }
- return 0;
-}
-
-/** Construct an associative array of type ti from corresponding keys and values.
- * Called for an AA literal `[k1:v1, k2:v2]`.
- * Params:
- * ti = TypeInfo for the associative array
- * keys = array of keys
- * vals = array of values
- * Returns:
- * A new associative array opaque pointer, or null if `keys` is empty.
- */
-extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
- void[] vals)
-{
- assert(keys.length == vals.length);
-
- immutable keysz = ti.key.tsize;
- immutable valsz = ti.value.tsize;
- immutable length = keys.length;
-
- if (!length)
- return null;
-
- auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM));
-
- void* pkey = keys.ptr;
- void* pval = vals.ptr;
- immutable off = aa.valoff;
- uint actualLength = 0;
- foreach (_; 0 .. length)
- {
- immutable hash = calcHash(pkey, aa);
-
- auto p = aa.findSlotLookup(hash, pkey, ti.key);
- if (p is null)
- {
- p = aa.findSlotInsert(hash);
- p.hash = hash;
- p.entry = allocEntry(aa, pkey); // move key, no postblit
- aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
- actualLength++;
- }
- else if (aa.entryTI && hasDtor(ti.value))
- {
- // destroy existing value before overwriting it
- ti.value.destroy(p.entry + off);
- }
- // set hash and blit value
- auto pdst = p.entry + off;
- pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit
-
- pkey += keysz;
- pval += valsz;
- }
- aa.used = actualLength;
- return aa;
-}
-
-/// compares 2 AAs for equality
-extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2)
-{
- if (aa1 is aa2)
- return true;
-
- immutable len = _aaLen(aa1);
- if (len != _aaLen(aa2))
- return false;
-
- if (!len) // both empty
- return true;
-
- import rt.lifetime : unqualify;
-
- auto uti = unqualify(tiRaw);
- auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
- // compare the entries
- immutable off = aa1.valoff;
- foreach (b1; aa1.buckets)
- {
- if (!b1.filled)
- continue;
- auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key);
- if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off))
- return false;
- }
- return true;
-}
-
-/// compute a hash
-extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow
-{
- const AA aa = *paa;
-
- if (aa.empty)
- return 0;
-
- import rt.lifetime : unqualify;
-
- auto uti = unqualify(tiRaw);
- auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
- immutable off = aa.valoff;
- auto keyHash = &ti.key.getHash;
- auto valHash = &ti.value.getHash;
-
- size_t h;
- foreach (b; aa.buckets)
- {
- // use addition here, so that hash is independent of element order
- if (b.filled)
- h += hashOf(valHash(b.entry + off), keyHash(b.entry));
- }
-
- return h;
-}
-
-/**
- * _aaRange implements a ForwardRange
- */
-struct Range
-{
- Impl* impl;
- size_t idx;
- alias impl this;
-}
-
-extern (C) pure nothrow @nogc @safe
-{
- Range _aaRange(return scope AA aa)
- {
- if (!aa)
- return Range();
-
- foreach (i; aa.firstUsed .. aa.dim)
- {
- if (aa.buckets[i].filled)
- return Range(aa, i);
- }
- return Range(aa, aa.dim);
- }
-
- bool _aaRangeEmpty(Range r)
- {
- return r.impl is null || r.idx >= r.dim;
- }
-
- void* _aaRangeFrontKey(Range r)
- {
- assert(!_aaRangeEmpty(r));
- if (r.idx >= r.dim)
- return null;
- return r.buckets[r.idx].entry;
- }
-
- void* _aaRangeFrontValue(Range r)
- {
- assert(!_aaRangeEmpty(r));
- if (r.idx >= r.dim)
- return null;
-
- auto entry = r.buckets[r.idx].entry;
- return entry is null ?
- null :
- (() @trusted { return entry + r.valoff; } ());
- }
-
- void _aaRangePopFront(ref Range r)
- {
- if (r.idx >= r.dim) return;
- for (++r.idx; r.idx < r.dim; ++r.idx)
- {
- if (r.buckets[r.idx].filled)
- break;
- }
- }
-}
-
-// Most tests are now in test_aa.d
-
-// test postblit for AA literals
-unittest
-{
- static struct T
- {
- ubyte field;
- static size_t postblit, dtor;
- this(this)
- {
- ++postblit;
- }
-
- ~this()
- {
- ++dtor;
- }
- }
-
- T t;
- auto aa1 = [0 : t, 1 : t];
- assert(T.dtor == 0 && T.postblit == 2);
- aa1[0] = t;
- assert(T.dtor == 1 && T.postblit == 3);
-
- T.dtor = 0;
- T.postblit = 0;
-
- auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten
- assert(T.dtor == 1 && T.postblit == 3);
-
- T.dtor = 0;
- T.postblit = 0;
-
- auto aa3 = [t : 0];
- assert(T.dtor == 0 && T.postblit == 1);
- aa3[t] = 1;
- assert(T.dtor == 0 && T.postblit == 1);
- aa3.remove(t);
- assert(T.dtor == 0 && T.postblit == 1);
- aa3[t] = 2;
- assert(T.dtor == 0 && T.postblit == 2);
-
- // dtor will be called by GC finalizers
- aa1 = null;
- aa2 = null;
- aa3 = null;
- auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor;
- GC.runFinalizers((cast(char*)dtor1)[0 .. 1]);
- auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor;
- GC.runFinalizers((cast(char*)dtor2)[0 .. 1]);
- assert(T.dtor == 6 && T.postblit == 2);
-}
-
-// Ensure the newaa struct layout (used for static initialization) is in sync
-unittest
-{
- import newaa = core.internal.newaa;
- static assert(newaa.Impl.sizeof == Impl.sizeof);
- // ensure compatible types and offsets
- static foreach (i; 0 .. Impl.tupleof.length)
- {
- // for bucket array and Flags, we "compatible" types, not exactly the same types.
- static if (__traits(identifier, Impl.tupleof[i]) == "buckets"
- || __traits(identifier, Impl.tupleof[i]) == "flags")
- static assert(Impl.tupleof[i].sizeof == newaa.Impl.tupleof[i].sizeof);
- else
- static assert(is(typeof(Impl.tupleof[i]) == typeof(newaa.Impl.tupleof[i])));
-
- static assert(Impl.tupleof[i].offsetof == newaa.Impl.tupleof[i].offsetof);
- }
-}
+++ /dev/null
-/**
- * Implementation of dynamic array property support routines.
- *
- * Copyright: Copyright Digital Mars 2000 - 2015.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Walter Bright
- * Source: $(DRUNTIMESRC rt/_adi.d)
- */
-
-module rt.adi;
-
-// debug = adi; // uncomment to turn on debugging printf's
-
-debug (adi) import core.stdc.stdio : printf;
-
-/***************************************
- * Support for array equality test.
- * Returns:
- * 1 equal
- * 0 not equal
- */
-
-extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
-{
- debug(adi) printf("_adEq2(a1.length = %zd, a2.length = %zd)\n", a1.length, a2.length);
- if (a1.length != a2.length)
- return 0; // not equal
- if (!ti.equals(&a1, &a2))
- return 0;
- return 1;
-}
-
-@safe unittest
-{
- debug(adi) printf("array.Eq unittest\n");
-
- struct S(T) { T val; }
- alias String = S!string;
- alias Float = S!float;
-
- String[1] a = [String("hello"c)];
-
- assert(a != [String("hel")]);
- assert(a != [String("helloo")]);
- assert(a != [String("betty")]);
- assert(a == [String("hello")]);
- assert(a != [String("hxxxx")]);
-
- Float[1] fa = [Float(float.nan)];
- assert(fa != fa);
-}
-
-unittest
-{
- debug(adi) printf("struct.Eq unittest\n");
-
- static struct TestStruct
- {
- int value;
-
- bool opEquals(const TestStruct rhs) const
- {
- return value == rhs.value;
- }
- }
-
- TestStruct[] b = [TestStruct(5)];
- TestStruct[] c = [TestStruct(6)];
- assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false);
- assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true);
-}
+++ /dev/null
-/**
- * Implementation of array assignment support routines.
- *
- * Copyright: Copyright Digital Mars 2004 - 2010.
- * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC rt/_cast_.d)
- */
-
-/* Copyright Digital Mars 2004 - 2010.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module rt.cast_;
-
-debug(cast_) import core.stdc.stdio : printf;
-
-extern (C):
-@nogc:
-nothrow:
-pure:
-
-// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
-// but we are trying to implement dynamic cast.
-extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
-{
- // same class if signatures match, works with potential duplicates across binaries
- if (a is b)
- return true;
-
- // new fast way
- if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig)
- return a.nameSig[0] == b.nameSig[0]
- && a.nameSig[1] == b.nameSig[1]
- && a.nameSig[2] == b.nameSig[2]
- && a.nameSig[3] == b.nameSig[3];
-
- // old slow way for temporary binary compatibility
- return a.name == b.name;
-}
-
-/******************************************
- * Given a pointer:
- * If it is an Object, return that Object.
- * If it is an interface, return the Object implementing the interface.
- * If it is null, return null.
- * Else, undefined crash
- */
-Object _d_toObject(return scope void* p)
-{
- if (!p)
- return null;
-
- Object o = cast(Object) p;
- ClassInfo oc = typeid(o);
- Interface* pi = **cast(Interface***) p;
-
- /* Interface.offset lines up with ClassInfo.name.ptr,
- * so we rely on pointers never being less than 64K,
- * and Objects never being greater.
- */
- if (pi.offset < 0x10000)
- {
- debug(cast_) printf("\tpi.offset = %zd\n", pi.offset);
- return cast(Object)(p - pi.offset);
- }
- return o;
-}
-
-/*************************************
- * Attempts to cast interface Object o to class c.
- * Returns o if successful, null if not.
- */
-void* _d_interface_cast(void* p, ClassInfo c)
-{
- debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, cast(int) c.name.length, c.name.ptr);
- if (!p)
- return null;
-
- Interface* pi = **cast(Interface***) p;
-
- debug(cast_) printf("\tpi.offset = %zd\n", pi.offset);
- Object o2 = cast(Object)(p - pi.offset);
- void* res = null;
- size_t offset = 0;
- if (o2 && _d_isbaseof2(typeid(o2), c, offset))
- {
- debug(cast_) printf("\toffset = %zd\n", offset);
- res = cast(void*) o2 + offset;
- }
- debug(cast_) printf("\tresult = %p\n", res);
- return res;
-}
-
-/*****
- * Dynamic cast from a class object `o` to class or interface `c`, where `c` is a subtype of `o`.
- * Params:
- * o = instance of class
- * c = a subclass of o
- * Returns:
- * null if o is null or c is not a subclass of o. Otherwise, return o.
- */
-void* _d_dynamic_cast(Object o, ClassInfo c)
-{
- debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr);
-
- void* res = null;
- size_t offset = 0;
- if (o && _d_isbaseof2(typeid(o), c, offset))
- {
- debug(cast_) printf("\toffset = %zd\n", offset);
- res = cast(void*) o + offset;
- }
- debug(cast_) printf("\tresult = %p\n", res);
- return res;
-}
-
-/*****
- * Dynamic cast from a class object o to class c, where c is a subclass of o.
- * Params:
- * o = instance of class
- * c = a subclass of o
- * Returns:
- * null if o is null or c is not a subclass of o. Otherwise, return o.
- */
-void* _d_class_cast(Object o, ClassInfo c)
-{
- debug(cast_) printf("_d_cast_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr);
-
- if (!o)
- return null;
-
- ClassInfo oc = typeid(o);
- int delta = oc.depth;
-
- if (delta && c.depth)
- {
- delta -= c.depth;
- if (delta < 0)
- return null;
-
- while (delta--)
- oc = oc.base;
- if (areClassInfosEqual(oc, c))
- return cast(void*)o;
- return null;
- }
-
- // no depth data - support the old way
- do
- {
- if (areClassInfosEqual(oc, c))
- return cast(void*)o;
- oc = oc.base;
- } while (oc);
- return null;
-}
-
-/**
- * Dynamic cast `o` to final class `c` only one level down
- * Params:
- * o = object that is instance of a class
- * c = class to cast it to
- * Returns:
- * o if it succeeds, null if it fails
- */
-void* _d_paint_cast(Object o, ClassInfo c)
-{
- /* If o is really an instance of c, just do a paint
- */
- auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null;
- debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c));
- return cast(void*)p;
-}
-
-int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
-{
- if (areClassInfosEqual(oc, c))
- return true;
-
- do
- {
- if (oc.base && areClassInfosEqual(oc.base, c))
- return true;
-
- // Bugzilla 2013: Use depth-first search to calculate offset
- // from the derived (oc) to the base (c).
- foreach (iface; oc.interfaces)
- {
- if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset))
- {
- offset += iface.offset;
- return true;
- }
- }
-
- oc = oc.base;
- } while (oc);
-
- return false;
-}
-
-int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
-{
- size_t offset = 0;
- return _d_isbaseof2(oc, c, offset);
-}
return attrs;
}
-/**
-Shrink the "allocated" length of an array to be the exact size of the array.
-
-It doesn't matter what the current allocated length of the array is, the
-user is telling the runtime that he knows what he is doing.
-
-Params:
- ti = `TypeInfo` of array type
- arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
-*/
-extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow
-{
- debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", ti.next.tsize, arr.ptr, arr.length);
- auto tinext = unqualify(ti.next);
- auto size = tinext.tsize; // array element size
- auto reqsize = arr.length * size;
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-
- auto curArr = gc_getArrayUsed(arr.ptr, isshared);
- if (curArr.ptr is null)
- // not a valid GC pointer
- return;
-
- // align the array.
- auto offset = arr.ptr - curArr.ptr;
- auto cursize = curArr.length - offset;
- if (cursize <= reqsize)
- // invalid situation, or no change.
- return;
-
- // if the type has a destructor, destroy elements we are about to remove.
- if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
- {
- auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
- if (sti.xdtor)
- {
- try
- {
- finalize_array(arr.ptr + reqsize, cursize - reqsize, sti);
- }
- catch (Exception e)
- {
- onFinalizeError(sti, e);
- }
- }
- }
-
- gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared);
-}
-
package bool hasPostblit(in TypeInfo ti) nothrow pure
{
return (&ti.postblit).funcptr !is &TypeInfo.postblit;
if (length == 0 || size == 0)
return null;
- version (D_InlineAsm_X86)
+ bool overflow = false;
+ size = mulu(size, length, overflow);
+ if (!overflow)
{
- asm pure nothrow @nogc
+ if (auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext))
{
- mov EAX,size ;
- mul EAX,length ;
- mov size,EAX ;
- jnc Lcontinue ;
+ debug(PRINTF) printf(" p = %p\n", ptr);
+ return ptr[0 .. length];
}
}
- else version (D_InlineAsm_X86_64)
- {
- asm pure nothrow @nogc
- {
- mov RAX,size ;
- mul RAX,length ;
- mov size,RAX ;
- jnc Lcontinue ;
- }
- }
- else
- {
- bool overflow = false;
- size = mulu(size, length, overflow);
- if (!overflow)
- goto Lcontinue;
- }
-Loverflow:
+
onOutOfMemoryError();
assert(0);
-Lcontinue:
-
- auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext);
- if (!ptr)
- goto Loverflow;
- debug(PRINTF) printf(" p = %p\n", ptr);
- return ptr[0 .. length];
}
/// ditto
}
-/**
-Given an array of length `size` that needs to be expanded to `newlength`,
-compute a new capacity.
-
-Better version by Dave Fladebo, enhanced by Steven Schveighoffer:
-This uses an inverse logorithmic algorithm to pre-allocate a bit more
-space for larger arrays.
-- The maximum "extra" space is about 80% of the requested space. This is for
-PAGE size and smaller.
-- As the arrays grow, the relative pre-allocated space shrinks.
-- Perhaps most importantly, overall memory usage and stress on the GC
-is decreased significantly for demanding environments.
-- The algorithm is tuned to avoid any division at runtime.
-
-Params:
- newlength = new `.length`
- elemsize = size of the element in the new array
-Returns: new capacity for array
-*/
-size_t newCapacity(size_t newlength, size_t elemsize)
-{
- size_t newcap = newlength * elemsize;
-
- /*
- * Max growth factor numerator is 234, so allow for multiplying by 256.
- * But also, the resulting size cannot be more than 2x, so prevent
- * growing if 2x would fill up the address space (for 32-bit)
- */
- enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1);
- if (!newcap || (newcap & ~largestAllowed))
- return newcap;
-
- /*
- * The calculation for "extra" space depends on the requested capacity.
- * We use an inverse logarithm of the new capacity to add an extra 15%
- * to 83% capacity. Note that normally we humans think in terms of
- * percent, but using 128 instead of 100 for the denominator means we
- * can avoid all division by simply bit-shifthing. Since there are only
- * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using
- * a lookup table allows us to precalculate the multiplier based on the
- * inverse logarithm. The formula rougly is:
- *
- * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1)))
- */
- import core.bitop;
- static immutable multTable = (){
- assert(__ctfe);
- ulong[size_t.sizeof * 8] result;
- foreach (i; 0 .. result.length)
- {
- auto factor = 128 + 1280 / (i + 1);
- result[i] = factor > 234 ? 234 : factor;
- }
- return result;
- }();
-
- auto mult = multTable[bsr(newcap)];
-
- // if this were per cent, then the code would look like:
- // ((newlength * mult + 99) / 100) * elemsize
- newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize;
- debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize);
- debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize);
- return newcap;
-}
-
-
-/**
-Extend an array by n elements.
-
-Caller must initialize those elements.
-
-Params:
- ti = type info of array type (not element type)
- px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated.
- n = number of elements to append
-Returns: `px` after being appended to
-*/
-extern (C)
-byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
-{
- // This is a cut&paste job from _d_arrayappendT(). Should be refactored.
-
- // Short circuit if no data is being appended.
- if (n == 0)
- return px;
-
-
- // only optimize array append where ti is not a shared type
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize; // array element size
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- auto length = px.length;
- auto newlength = length + n;
- auto newsize = newlength * sizeelem;
- auto size = length * sizeelem;
-
- if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared))
- {
- // could not set the size, we must reallocate.
- auto newcap = newCapacity(newlength, sizeelem);
- auto attrs = __typeAttrs(tinext, px.ptr) | BlkAttr.APPENDABLE;
- auto ptr = cast(byte*) GC.malloc(newcap, attrs, tinext);
- if (ptr is null)
- {
- onOutOfMemoryError();
- assert(0);
- }
-
- if (newsize != newcap)
- {
- // For small blocks that are always fully scanned, if we allocated more
- // capacity than was requested, we are responsible for zeroing that
- // memory.
- // TODO: should let the GC figure this out, as this property may
- // not always hold.
- if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE)
- memset(ptr + newsize, 0, newcap - newsize);
-
- gc_shrinkArrayUsed(ptr[0 .. newsize], newcap, isshared);
- }
-
- memcpy(ptr, px.ptr, size);
-
- // do potsblit processing.
- __doPostblit(ptr, size, tinext);
-
- px = ptr[0 .. newlength];
- return px;
- }
-
- // we were able to expand in place, just update the length
- px = px.ptr[0 .. newlength];
- return px;
-}
-
-
/**
Append `dchar` to `char[]`, converting UTF-32 to UTF-8
return x;
}
-/**
-Allocate an array literal
-
-Rely on the caller to do the initialization of the array.
-
----
-int[] getArr()
-{
- return [10, 20];
- // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
- // res[0] = 10;
- // res[1] = 20;
- // return res[0..2];
-}
----
-
-Params:
- ti = `TypeInfo` of resulting array type
- length = `.length` of array literal
-
-Returns: pointer to allocated array
-*/
-extern (C)
-void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
-{
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize; // array element size
- void* result;
-
- debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %zd, length = %zd)\n", sizeelem, length);
- if (length == 0 || sizeelem == 0)
- return null;
- else
- {
- auto allocsize = length * sizeelem;
- return GC.malloc(allocsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext);
- }
-}
-
unittest
{
-5a142da0af7b72bfed314278d14464d8f1147391
+f87979028bd0e0f3e67dbf5b0cd1335ca3f7ed67
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
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 \
- std/internal/test/dummyrange.d std/internal/test/range.d \
+ std/internal/entropy.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 std/internal/test/dummyrange.d \
+ std/internal/test/range.d \
std/internal/test/sumtype_example_overloads.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 \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/int128.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/cstring.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/entropy.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.lo \
@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 \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/dummyrange.d std/internal/test/range.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/entropy.d std/internal/math/biguintcore.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/gammafunction.d std/internal/memory.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/scopebuffer.d std/internal/test/dummyrange.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/range.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/sumtype_example_overloads.d std/internal/test/uda.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_comp.d std/internal/unicode_decomp.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_grapheme.d std/internal/unicode_norm.d \
@: > std/internal/$(am__dirstamp)
std/internal/attributes.lo: std/internal/$(am__dirstamp)
std/internal/cstring.lo: std/internal/$(am__dirstamp)
+std/internal/entropy.lo: std/internal/$(am__dirstamp)
std/internal/math/$(am__dirstamp):
@$(MKDIR_P) std/internal/math
@: > std/internal/math/$(am__dirstamp)
$(TDNW $(MREF std,bigint))
$(TD An arbitrary-precision integer type.)
)
+ $(TR
+ $(TDNW $(MREF std,int128))
+ $(TDNW $(MREF core,int128))
+ $(TD 128 bit-precision integer type.)
+ )
$(TR
$(TDNW $(MREF std,complex))
$(TD A complex number type.)
A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of elements of which are an $(REF indexed, std,range) view into `r`.
+Note:
+ The elements of the resulting range reuse the same internal buffer of
+ permutations, so each element is invalidated by `popFront`. If copies of
+ intermediate permutations are desired, they need to be individually copied,
+ such as using `.map!(e => e.array)` to save them in individual, independent
+ arrays.
+
See_Also:
$(REF nextPermutation, std,algorithm,sorting).
*/
static struct A
{
int x;
- this(scope ref return const A other)
+ this(scope return ref const A other)
{
import std.stdio;
x = other.x;
- `needle` is the index into `needles` which matched.
- Both are `-1` if there was no match.
+ Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode,
+ auto-decoding), the return value of this function may $(I not) correspond
+ to the array index for strings. To find the index of an element matching
+ the predicate in a string, use $(REF indexOf, std,string) instead.
+
See_Also: $(REF indexOf, std,string)
+/
auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
$(LREF startsWith)`!pred(haystack)` is `true`.
- If `startsWith!pred(haystack)` is not `true` for any element in
`haystack`, then `-1` is returned.
+
+ Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode,
+ auto-decoding), the return value of this function may $(I not) correspond
+ to the array index for strings. To find the index of an element matching
+ the predicate in a string, use $(REF indexOf, std,string) instead.
+/
ptrdiff_t countUntil(alias pred, R)(R haystack)
if (isInputRange!R &&
$(LREF isDigit)
$(LREF isGraphical)
$(LREF isHexDigit)
+ $(LREF isLower)
$(LREF isOctalDigit)
$(LREF isPrintable)
$(LREF isPunctuation)
/**
* Returns the first element in the range
*/
- @property Elem front()
+ ref @property Elem front()
{
return _begin.value;
}
/**
* Returns the last element in the range
*/
- @property Elem back()
+ ref @property Elem back()
{
return _end.prev.value;
}
* elements `a` and `b`, $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
* always equal `false`.
*
+ * Care should also be taken to not modify elements in the tree (e.g. via `front` /
+ * `back`, which return by `ref`) in a way which would affect the order defined by
+ * the `less` predicate.
+ *
* If `allowDuplicates` is set to `true`, then inserting the same element more than
* once continues to add more elements. If it is `false`, duplicate elements are
* ignored on insertion. If duplicates are allowed, then new elements are
* inserted after all existing duplicate elements.
*/
final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false)
-if (is(typeof(binaryFun!less(T.init, T.init))))
+if (is(typeof((ref const T a) => binaryFun!less(a, a))))
{
import std.meta : allSatisfy;
import std.range : Take;
*
* Complexity: $(BIGOH 1)
*/
- inout(Elem) front() inout
+ ref inout(Elem) front() inout
{
return _begin.value;
}
*
* Complexity: $(BIGOH log(n))
*/
- inout(Elem) back() inout
+ ref inout(Elem) back() inout
{
return _end.prev.value;
}
t.insert([1, 3, 5, 4, 2]);
assert(t[].equal([5, 4, 3, 2, 1]));
}
+
+// should support `less` predicate taking `ref const`
+@safe pure unittest
+{
+ struct S
+ {
+ int* value;
+ }
+
+ cast(void) new RedBlackTree!(S, (ref const S a, ref const S b) => a.value > b.value);
+}
$(BOOKTABLE,
$(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
$(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
+ $(TR $(TD Duration) $(TD +) $(TD DateTime) $(TD -->) $(TD DateTime))
)
Params:
mixin("return retval._addSeconds(" ~ op ~ "seconds);");
}
+ /// ditto
+ DateTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+ if (op == "+")
+ {
+ return this + duration;
+ }
+
+
///
@safe unittest
{
assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
DateTime(2015, 12, 31, 23, 59, 59));
+
+ assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
+ hours(1) + DateTime(2015, 12, 31, 23, 59, 59));
}
@safe unittest
import core.time : Duration;
/++
Gives the result of adding or subtracting a $(REF Duration, core,time)
- from
+ from this $(LREF Date).
The legal types of arithmetic for $(LREF Date) using this operator are
$(BOOKTABLE,
$(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
$(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
+ $(TR $(TD Duration) $(TD +) $(TD Date) $(TD -->) $(TD Date))
)
Params:
mixin("return retval._addDays(" ~ op ~ "days);");
}
+
+ /// ditto
+ Date opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+ if (op == "+")
+ {
+ return this + duration;
+ }
+
///
@safe unittest
{
assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
+
+ assert(Date(2004, 2, 26) + days(4) == days(4) + Date(2004, 2, 26));
}
@safe unittest
$(BOOKTABLE,
$(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
$(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
+ $(TR $(TD Duration) $(TD +) $(TD TimeOfDay) $(TD -->) $(TD TimeOfDay))
)
Params:
mixin("return retval._addSeconds(" ~ op ~ "seconds);");
}
+ /// ditto
+ TimeOfDay opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+ if (op == "+")
+ {
+ return this + duration;
+ }
+
///
@safe unittest
{
assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
+
+ assert(TimeOfDay(12, 12, 12) + seconds(1) == seconds(1) + TimeOfDay(12, 12, 12));
}
@safe unittest
$(BOOKTABLE,
$(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime))
$(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime))
+ $(TR $(TD Duration) $(TD +) $(TD SysTime) $(TD -->) $(TD SysTime))
)
Params:
return retval;
}
+ /// ditto
+ SysTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow
+ if (op == "+")
+ {
+ return this + duration;
+ }
+
///
@safe unittest
{
assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) ==
SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
+
+ assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) ==
+ seconds(1) + SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
}
@safe unittest
// be there, but since PosixTimeZone _does_ use leap seconds if
// the time zone file does, we'll test that functionality if the
// appropriate files exist.
- if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists)
+ if (chainPath(PosixTimeZone.getDefaultTZDatabaseDir(), "right", tzName).exists)
{
auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName);
the TZDatabaseDir version to pass an arbitrary path at compile-time,
rather than hard-coding it here. Android concatenates all time zone
data into a single file called tzdata and stores it in the directory
- below.
+ below. If the TZDIR environment variable is set, it is consulted
+ before this constant.
+/
enum defaultTZDatabaseDir = "";
}
enum defaultTZDatabaseDir = "";
}
+ private static string getDefaultTZDatabaseDir()
+ {
+ import core.stdc.stdlib : getenv;
+ import std.string : fromStringz;
+
+ auto dir = getenv("TZDIR");
+ if (dir)
+ return fromStringz(dir).idup;
+
+ return defaultTZDatabaseDir;
+ }
+
/++
Returns a $(LREF TimeZone) with the give name per the TZ Database. The
+/
// TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed
// directory.
- static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted
+ static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = getDefaultTZDatabaseDir()) @trusted
{
import std.algorithm.sorting : sort;
import std.conv : to;
Throws:
`FileException` if it fails to read from disk.
+/
- static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @safe
+ static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = getDefaultTZDatabaseDir()) @safe
{
import std.algorithm.sorting : sort;
import std.array : appender;
{}
else
{
- foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth))
+ string tzDatabaseDir = getDefaultTZDatabaseDir();
+ foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth))
{
if (de.isFile)
{
- auto tzName = de.name[defaultTZDatabaseDir.length .. $];
+ auto tzName = de.name[tzDatabaseDir.length .. $];
if (!canFind(tzNames, tzName))
assertThrown!DateTimeException(testPTZFailure(tzName));
this(this) @disable;
+ nothrow @nogc @trusted
void[] payload() inout
{
return (cast(ubyte*) &this)[0 .. size];
}
+ nothrow @nogc @trusted
bool adjacent(in Node* right) const
{
assert(right);
return p.ptr < right && right < p.ptr + p.length + Node.sizeof;
}
+ nothrow @nogc @trusted
bool coalesce(void* memoryEnd = null)
{
// Coalesce the last node before the memory end with any possible gap
return true;
}
+ nothrow @nogc @safe
Tuple!(void[], Node*) allocateHere(size_t bytes)
{
assert(bytes >= Node.sizeof);
if (leftover >= Node.sizeof)
{
// There's room for another node
- auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes);
+ auto newNode = (() @trusted => cast(Node*) ((cast(ubyte*) &this) + bytes))();
newNode.size = leftover;
newNode.next = next == &this ? newNode : next;
assert(next);
else alias parent = ParentAllocator.instance;
private void[] payload;
private Node* root;
- private bool regionMode() const { return bytesUsedRegionMode != size_t.max; }
- private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; }
+ nothrow @nogc @safe private bool regionMode() const { return bytesUsedRegionMode != size_t.max; }
+ nothrow @nogc @safe private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; }
private size_t bytesUsedRegionMode = 0;
auto byNodePtr()
}
}
+ nothrow @nogc @safe
private Node* sortFreelist(Node* root)
{
// Find a monotonic run
return merge(root, tail);
}
+ nothrow @nogc @safe
private Node* merge(Node* left, Node* right)
{
assert(left != right);
return result;
}
+ nothrow @nogc @safe
private void coalesceAndMakeCircular()
{
for (auto n = root;;)
Otherwise, sorts the free list accumulated so far and switches strategy for
future allocations to KR style.
*/
+ nothrow @nogc @safe
void switchToFreeList()
{
if (!regionMode) return;
Returns: A word-aligned buffer of `n` bytes, or `null`.
*/
+ nothrow @nogc @safe
void[] allocate(size_t n)
{
if (!n || !root) return null;
immutable balance = root.size - actualBytes;
if (balance >= Node.sizeof)
{
- auto newRoot = cast(Node*) (result + actualBytes);
+ auto newRoot = (() @trusted => cast(Node*) ((cast(ubyte*) result) + actualBytes))();
newRoot.next = root.next;
newRoot.size = balance;
root = newRoot;
root = null;
switchToFreeList;
}
- return result[0 .. n];
+ return (() @trusted => result[0 .. n])();
}
// Not enough memory, switch to freelist mode and fall through
at the front of the free list. These blocks get coalesced, whether
`allocateAll` succeeds or fails due to fragmentation.
*/
+ nothrow @nogc @safe
void[] allocateAll()
{
if (regionMode) switchToFreeList;
assert(olongshort.optLong == "--foo");
}
+private string optionValidatorErrorFormat(string msg, size_t idx)
+{
+ import std.conv : to;
+ return "getopt validator: " ~ msg ~ " (at position " ~ to!(string)(idx) ~
+ ")";
+}
+
/*
This function verifies that the variadic parameters passed in getOpt
follow this pattern:
*/
private template optionValidator(A...)
{
- import std.format : format;
-
- enum fmt = "getopt validator: %s (at position %d)";
enum isReceiver(T) = is(T == U*, U) || (is(T == function)) || (is(T == delegate));
enum isOptionStr(T) = isSomeString!T || isSomeChar!T;
{
static if (isReceiver!(A[0]))
{
- msg = format(fmt, "first argument must be a string or a config", 0);
+ msg = optionValidatorErrorFormat("first argument must be a string or a config", 0);
}
else static if (!isOptionStr!(A[0]) && !is(A[0] == config))
{
- msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0);
+ msg = optionValidatorErrorFormat("invalid argument type: " ~ A[0].stringof, 0);
}
else
{
static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) &&
!(is(A[i] == config)))
{
- msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i);
+ msg = optionValidatorErrorFormat("invalid argument type: " ~ A[i].stringof, i);
goto end;
}
else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1]))
{
- msg = format(fmt, "a receiver can not be preceeded by a receiver", i);
+ msg = optionValidatorErrorFormat("a receiver can not be preceeded by a receiver", i);
goto end;
}
else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1])
&& isSomeString!(A[i-2]))
{
- msg = format(fmt, "a string can not be preceeded by two strings", i);
+ msg = optionValidatorErrorFormat("a string can not be preceeded by two strings", i);
goto end;
}
}
}
static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
{
- msg = format(fmt, "last argument must be a receiver or a config",
+ msg = optionValidatorErrorFormat("last argument must be a receiver or a config",
A.length -1);
}
}
size_t idx, string file = __FILE__, size_t line = __LINE__)
{
import std.conv : to, ConvException;
- import std.format : format;
try
{
return to!R(value);
}
catch (ConvException e)
{
- throw new ConvException(format("Argument '%s' at position '%u' could "
- ~ "not be converted to type '%s' as required by option '%s'.",
- value, idx, R.stringof, option), e, file, line);
+ throw new ConvException("Argument '" ~ value ~ "' at position '" ~
+ to!(string)(idx) ~ "' could not be converted to type '" ~
+ R.stringof ~ "' as required by option '" ~ option ~ "'.", e, file,
+ line);
}
}
--- /dev/null
+// Written in the D programming language.
+
+/+
+ CSPRNG library prototype.
+
+ This code has not been audited.
+ Do not use for cryptographic purposes.
+
+ The terms $(I entropy) and $(I entropy sources) here do refer to
+ cryptographically-safe random numbers and higher-level generators of such
+ — typically powered by an entropy pool provided by the operating system.
+
+ An example of similar usage of said terminology would be the `getentropy()`
+ function provided by
+ $(LINK2 https://man.freebsd.org/cgi/man.cgi?query=getentropy&apropos=0&sektion=3&manpath=FreeBSD+14.2-RELEASE&arch=default&format=html,
+ FreeBSD).
+
+ This library does not interact with any actual low-level entropy sources
+ by itself. Instead it interfaces with system-provided CSPRNGs that are
+ typically seeded through aforementioned entropy sources by the operating
+ system as needed.
+
+ See_also:
+ $(LINK https://blog.cr.yp.to/20140205-entropy.html),
+ $(LINK https://cr.yp.to/talks/2014.10.18/slides-djb-20141018-a4.pdf)
+
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ Authors: Elias Batek
+ Source: $(PHOBOSSRC std/internal/entropy.d)
+ +/
+module std.internal.entropy;
+
+import std.meta;
+
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+// Self-test: Detect potentially unsuitable default entropy source.
+@safe unittest
+{
+ auto buffer = new ubyte[](32);
+ forceEntropySource(defaultEntropySource);
+ const result = getEntropy(buffer);
+
+ assert(
+ !result.isUnavailable,
+ "The default entropy source for the target platform"
+ ~ " is unavailable on this machine. Please consider"
+ ~ " patching it to accommodate to your environment."
+ );
+ assert(result.isOK);
+}
+
+// Self-test: Detect faulty implementation.
+@system unittest
+{
+ forceEntropySource(defaultEntropySource);
+
+ bool test() @system
+ {
+ static immutable pattern = 0xDEAD_BEEF_1337_0000;
+ long number = pattern;
+ const result = getEntropy(&number, number.sizeof);
+ assert(result.isOK);
+ return number != pattern;
+ }
+
+ size_t timesFailed = 0;
+ foreach (n; 0 .. 3)
+ if (!test())
+ ++timesFailed;
+
+ assert(
+ timesFailed <= 1,
+ "Suspicious random data: Potential security issue or really unlucky; please retry."
+ );
+}
+
+// Self-test: Detect faulty implementation.
+@safe unittest
+{
+ forceEntropySource(defaultEntropySource);
+
+ bool test() @safe
+ {
+ ubyte[32] data;
+ data[] = 0;
+
+ const result = getEntropy(data[]);
+ assert(result.isOK);
+
+ size_t zeros = 0;
+ foreach (b; data)
+ if (b == 0)
+ ++zeros;
+
+ enum threshold = 24;
+ return zeros < threshold;
+ }
+
+ size_t timesFailed = 0;
+ foreach (n; 0 .. 3)
+ if (!test())
+ ++timesFailed;
+
+ assert(
+ timesFailed <= 1,
+ "Suspicious random data: Potential security issue or really unlucky; please retry."
+ );
+}
+
+@nogc nothrow:
+
+// Flagship function
+/++
+ Retrieves random data from an applicable system CSPRNG.
+
+ Params:
+ buffer = An output buffer to store the retrieved entropy in.
+ The length of it will determine the amount of random data to
+ be obtained.
+
+ This function (and all overloads) always attempt to fill
+ the entire buffer. Therefore, they can block, spin or report
+ an error.
+
+ Returns:
+ An `EntropyResult` that either reports success
+ or the type of error that has occurred.
+
+ In case of an error, the data in `buffer` MUST NOT be used.
+ The recommended way to check for success is through the `isOK()`
+ helper function.
+ +/
+EntropyResult getEntropy(scope void[] buffer) @safe
+{
+ return getEntropyImpl(buffer);
+}
+
+///
+@safe unittest
+{
+ int[4] bytes;
+ if (getEntropy(cast(void[]) bytes).isOK)
+ {
+ // Success; data in `bytes` may be used.
+ }
+
+ assert((cast(void[]) bytes).length == bytes.sizeof);
+}
+
+// Convenience overload
+/// ditto
+EntropyResult getEntropy(scope ubyte[] buffer) @safe
+{
+ return getEntropy(cast(void[]) buffer);
+}
+
+///
+@safe unittest
+{
+ ubyte[16] bytes;
+ if (getEntropy(bytes).isOK)
+ {
+ // Success; data in `bytes` may be used.
+ }
+}
+
+// Convenience wrapper
+/// ditto
+/++
+ Retrieves random data from an applicable system CSPRNG.
+
+ Params:
+ buffer = An output buffer to store the retrieved entropy in.
+ length = Length of the provided `buffer`.
+ Specifying a wrong value here, will lead to memory corruption.
+
+ Returns:
+ An `EntropyResult` that either reports success
+ or the type of error that has occurred.
+
+ In case of an error, the data in `buffer` MUST NOT be used.
+ The recommended way to check for success is through the `isOK()`
+ helper function.
+ +/
+EntropyResult getEntropy(scope void* buffer, size_t length) @system
+{
+ return getEntropy(buffer[0 .. length]);
+}
+
+///
+@system unittest
+{
+ ubyte[16] bytes;
+ if (getEntropy(cast(void*) bytes.ptr, bytes.length).isOK)
+ {
+ // Success; data in `bytes` may be used.
+ }
+}
+
+///
+@system unittest
+{
+ int number = void;
+ if (getEntropy(&number, number.sizeof).isOK)
+ {
+ // Success; value of `number` may be used.
+ }
+}
+
+/++
+ Manually set the entropy source to use for the current thread.
+
+ As a rule of thumb, this SHOULD NOT be done.
+
+ It might be useful in cases where the default entropy source — as chosen by
+ the maintainer of the used compiler package — is unavailable on a system.
+ Usually, `EntropySource.tryAll` will be the most reasonable option
+ in such cases.
+
+ Params:
+ source = The requested default entropy source to use for the current thread.
+
+ Examples:
+
+ ---
+ // Using `forceEntropySource` almost always is a bad idea.
+ // As a rule of thumb, this SHOULD NOT be done.
+ forceEntropySource(EntropySource.none);
+ ---
+ +/
+void forceEntropySource(EntropySource source) @safe
+{
+ _entropySource = source;
+}
+
+// (In-)Convenience wrapper
+/++
+ Retrieves random data from the requested entropy source.
+
+ In general, it’s a $(B bad idea) to let users pick sources themselves.
+ A sane option should be used by default instead.
+
+ This overload only exists because its used by Phobos.
+
+ See_also:
+ Use `forceEntropySource` instead.
+
+ Params:
+ buffer = An output buffer to store the retrieved entropy in.
+ The length of it will determine the amount of entropy to be
+ obtained.
+ length = Length of the provided `buffer`.
+ Specifying a wrong value here, will lead to memory corruption.
+ source = The entropy source to use for the operation.
+
+ Returns:
+ An `EntropyResult` that either reports success
+ or the type of error that has occurred.
+
+ In case of an error, the data in `buffer` MUST NOT be used.
+ The recommended way to check for success is through the `isOK()`
+ helper function.
+ +/
+EntropyResult getEntropy(scope void* buffer, size_t length, EntropySource source) @system
+{
+ const sourcePrevious = _entropySource;
+ scope (exit) _entropySource = sourcePrevious;
+
+ _entropySource = source;
+ return getEntropy(buffer[0 .. length]);
+}
+
+///
+@system unittest
+{
+ ubyte[4] bytes;
+
+ // `EntropySource.none` always fails.
+ assert(!getEntropy(bytes.ptr, bytes.length, EntropySource.none).isOK);
+}
+
+/++
+ A CSPRNG suitable to retrieve cryptographically-secure random data from.
+
+ (No actual low-level entropy sources are provided on purpose.)
+ +/
+enum EntropySource
+{
+ /// Implements a $(I hunting) strategy for finding an entropy source that
+ /// is available at runtime.
+ ///
+ /// Try supported sources one-by-one until one is available.
+ /// This exists to enable the use of this the entropy library
+ /// in a backwards compatibility way.
+ ///
+ /// It is recommended against using this in places that do not strictly
+ /// have to to meet compatibility requirements.
+ /// Like any kind of crypto-agility, this approach may suffer from
+ /// practical issues.
+ ///
+ /// See_also:
+ /// While the following article focuses on cipher agility in protocols,
+ /// it elaborates why agility can lead to problems:
+ /// $(LINK https://web.archive.org/web/20191102211148/https://paragonie.com/blog/2019/10/against-agility-in-cryptography-protocols)
+ tryAll = -1,
+
+ /// Always fail.
+ none = 0,
+
+ /// `/dev/urandom`
+ charDevURandom = 1,
+
+ /// `/dev/random`
+ charDevRandom = 2,
+
+ /// `getrandom` syscall or wrapper
+ getrandom = 3,
+
+ /// `arc4random`
+ arc4random = 4,
+
+ // `getentropy`
+ getentropy = 5,
+
+ /// Windows legacy CryptoAPI
+ cryptGenRandom = 6,
+
+ /// Windows Cryptography API: Next Generation (“BCrypt”)
+ bcryptGenRandom = 7,
+}
+
+///
+enum EntropyStatus
+{
+ /// success
+ ok = 0,
+
+ /// catch-all error
+ unknownError = 1,
+
+ /// An entropy source was unavailable.
+ unavailable,
+
+ /// A dependency providing the entropy source turned out unavailable.
+ unavailableLibrary,
+
+ /// The requested entropy source is not supported on this platform.
+ unavailablePlatform,
+
+ /// Could not retrieve entropy from the selected source.
+ readError,
+}
+
+/++
+ Status report returned by `getEntropy` functions.
+
+ Use the `isOK` helper function to test for success.
+ +/
+struct EntropyResult
+{
+ ///
+ EntropyStatus status;
+
+ ///
+ EntropySource source;
+
+ /++
+ Returns:
+ A human-readable status message.
+ +/
+ string toString() const @nogc nothrow pure @safe
+ {
+ if (status == EntropyStatus.ok)
+ return "getEntropy(): OK.";
+
+ if (source == EntropySource.none)
+ {
+ if (status == EntropyStatus.unavailable)
+ return "getEntropy(): Error - No suitable entropy source was available.";
+ }
+ else if (source == EntropySource.getrandom)
+ {
+ if (status == EntropyStatus.unavailableLibrary)
+ return "getEntropy(): `dlopen(\"libc\")` failed.";
+ if (status == EntropyStatus.unavailable)
+ return "getEntropy(): `dlsym(\"libc\", \"getrandom\")` failed.";
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): `getrandom()` failed.";
+ }
+ else if (source == EntropySource.getentropy)
+ {
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): `getentropy()` failed.";
+ }
+ else if (source == EntropySource.charDevURandom)
+ {
+ if (status == EntropyStatus.unavailable)
+ return "getEntropy(): `/dev/urandom` is unavailable.";
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): Reading from `/dev/urandom` failed.";
+ }
+ else if (source == EntropySource.charDevURandom)
+ {
+ if (status == EntropyStatus.unavailable)
+ return "getEntropy(): `/dev/random` is unavailable.";
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): Reading from `/dev/random` failed.";
+ }
+ else if (source == EntropySource.bcryptGenRandom)
+ {
+ if (status == EntropyStatus.unavailableLibrary)
+ return "getEntropy(): `LoadLibraryA(\"Bcrypt.dll\")` failed.";
+ if (status == EntropyStatus.unavailable)
+ return "getEntropy(): `GetProcAddress(hBcrypt , \"BCryptGenRandom\")` failed.";
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): `BCryptGenRandom()` failed.";
+ }
+
+ // generic errors
+ {
+ if (status == EntropyStatus.unavailable ||
+ status == EntropyStatus.unavailableLibrary)
+ return "getEntropy(): An entropy source was unavailable.";
+ if (status == EntropyStatus.unavailablePlatform)
+ return "getEntropy(): The requested entropy source is not supported on this platform.";
+ if (status == EntropyStatus.readError)
+ return "getEntropy(): Could not retrieve entropy from the selected source.";
+
+ return "getEntropy(): An unknown error occurred.";
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ ubyte[4] data;
+ EntropyResult result = getEntropy(data[]);
+
+ if (result.isOK)
+ {
+ // Success; data in `bytes` may be used.
+ }
+ else
+ {
+ // Failure
+
+ if (result.isUnavailable)
+ {
+ // System’s entropy source was unavailable.
+ }
+
+ // Call `toString` to obtain a user-readable error message.
+ assert(result.toString() !is null);
+ assert(result.toString().length > 0);
+ }
+}
+
+/++
+ Determines whether an `EntropyResult` reports the success of an operation.
+
+ Params:
+ value = test subject
+
+ Returns:
+ `true` on success
+ +/
+pragma(inline, true) bool isOK(const EntropyResult value) pure @safe
+{
+ return value.status == EntropyStatus.ok;
+}
+
+/++
+ Determines whether an `EntropyResult` reports the unvailability of the
+ requested entropy source.
+
+ Params:
+ value = test subject
+
+ Returns:
+ `true` if entropy source requested to use with the operation was unavailable.
+ +/
+pragma(inline, true) bool isUnavailable(const EntropyResult value) pure @safe
+{
+ return (
+ value.status == EntropyStatus.unavailable ||
+ value.status == EntropyStatus.unavailableLibrary ||
+ value.status == EntropyStatus.unavailablePlatform
+ );
+}
+
+package(std):
+
+// If the system let us down, we'll let the system down.
+pragma(inline, true) void crashOnError(const EntropyResult value) pure @safe
+{
+ if (value.isOK)
+ return;
+
+ assert(false, value.toString());
+}
+
+/+
+ Building blocks and implementation helpers
+ +/
+private
+{
+ /++
+ A “Chunks” implementation that works with `void[]`.
+ +/
+ struct VoidChunks
+ {
+ void[] _data;
+ size_t _chunkSize;
+
+ @nogc nothrow pure @safe:
+
+ this(void[] data, size_t chunkSize)
+ {
+ _data = data;
+ _chunkSize = chunkSize;
+ }
+
+ bool empty() const
+ {
+ return _data.length == 0;
+ }
+
+ inout(void)[] front() inout
+ {
+ if (_data.length < _chunkSize)
+ return _data;
+
+ return _data[0 .. _chunkSize];
+ }
+
+ void popFront()
+ {
+ if (_data.length <= _chunkSize)
+ {
+ _data = null;
+ return;
+ }
+
+ _data = _data[_chunkSize .. $];
+ }
+ }
+
+ struct SrcFunPair(EntropySource source, alias func)
+ {
+ enum src = source;
+ alias fun = func;
+ }
+
+ template isValidSupportedSource(SupportedSource)
+ {
+ enum isValidSupportedSource = (
+ is(SupportedSource == SrcFunPair!Args, Args...) &&
+ SupportedSource.src != EntropySource.tryAll &&
+ SupportedSource.src != EntropySource.none
+ );
+ }
+
+ /++
+ `getEntropyImpl()` implementation helper.
+ To be instantiated and mixed in with platform-specific configuration.
+
+ Params:
+ defaultSource = Default entropy source of the platform
+ SupportedSources = Sequence of `SrcFunPair`
+ representing the supported sources of the platform
+ +/
+ mixin template entropyImpl(EntropySource defaultSource, SupportedSources...)
+ if (allSatisfy!(isValidSupportedSource, SupportedSources))
+ {
+ private:
+ /// Preconfigured entropy source preset of the platform.
+ enum defaultEntropySource = defaultSource;
+
+ EntropyResult getEntropyImpl(scope void[] buffer) @safe
+ {
+ switch (_entropySource)
+ {
+ static foreach (source; SupportedSources)
+ {
+ case source.src:
+ return source.fun(buffer);
+ }
+
+ case EntropySource.tryAll:
+ {
+ const result = _tryEntropySources(buffer);
+ result.saveSourceForNextUse();
+ return result;
+ }
+
+ case EntropySource.none:
+ return getEntropyViaNone(buffer);
+
+ default:
+ return EntropyResult(EntropyStatus.unavailablePlatform, _entropySource);
+ }
+ }
+
+ EntropyResult _tryEntropySources(scope void[] buffer) @safe
+ {
+ EntropyResult result;
+
+ static foreach (source; SupportedSources)
+ {
+ result = source.fun(buffer);
+ if (!result.isUnavailable)
+ return result;
+ }
+
+ result = EntropyResult(
+ EntropyStatus.unavailable,
+ EntropySource.none,
+ );
+
+ return result;
+ }
+ }
+}
+
+version (Darwin) mixin entropyImpl!(
+ EntropySource.arc4random,
+ SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (DragonFlyBSD) mixin entropyImpl!(
+ EntropySource.getentropy,
+ SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (FreeBSD) mixin entropyImpl!(
+ EntropySource.getentropy,
+ SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (linux) mixin entropyImpl!(
+ EntropySource.getrandom,
+ SrcFunPair!(EntropySource.getrandom, getEntropyViaGetrandom),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (NetBSD) mixin entropyImpl!(
+ EntropySource.arc4random,
+ SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (OpenBSD) mixin entropyImpl!(
+ EntropySource.arc4random,
+ SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (Posix) mixin entropyImpl!(
+ EntropySource.charDevURandom,
+ SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+ SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (Windows) mixin entropyImpl!(
+ EntropySource.bcryptGenRandom,
+ SrcFunPair!(EntropySource.bcryptGenRandom, getEntropyViaBCryptGenRandom),
+);
+else mixin entropyImpl!(
+ EntropySource.none,
+);
+
+private
+{
+ static EntropySource _entropySource = defaultEntropySource;
+
+ void saveSourceForNextUse(const EntropyResult result) @safe
+ {
+ if (!result.isOK)
+ return;
+
+ _entropySource = result.source;
+ }
+}
+
+version (all)
+{
+private:
+
+ EntropyResult getEntropyViaNone(scope void[]) @safe
+ {
+ return EntropyResult(EntropyStatus.unavailable, EntropySource.none);
+ }
+}
+
+version (Posix)
+{
+private:
+
+ EntropyResult getEntropyViaCharDevURandom(scope void[] buffer) @trusted
+ {
+ const status = getEntropyViaCharDev(buffer, "/dev/urandom".ptr);
+ return EntropyResult(status, EntropySource.charDevURandom);
+ }
+
+ EntropyResult getEntropyViaCharDevRandom(scope void[] buffer) @trusted
+ {
+ const status = getEntropyViaCharDev(buffer, "/dev/random".ptr);
+ return EntropyResult(status, EntropySource.charDevRandom);
+ }
+
+ EntropyStatus getEntropyViaCharDev(scope void[] buffer, const(char)* charDevName) @system
+ {
+ import core.stdc.stdio : fclose, fopen, fread;
+
+ auto charDev = fopen(charDevName, "r");
+ if (charDev is null)
+ return EntropyStatus.unavailable;
+
+ scope (exit)
+ fclose(charDev);
+
+ const bytesRead = fread(buffer.ptr, 1, buffer.length, charDev);
+ if (bytesRead != buffer.length)
+ return EntropyStatus.readError;
+
+ return EntropyStatus.ok;
+ }
+}
+
+version (linux)
+{
+private:
+
+ EntropyResult getEntropyViaGetrandom(scope void[] buffer) @trusted
+ {
+ const status = syscallGetrandom(buffer, 0);
+ return EntropyResult(status, EntropySource.getrandom);
+ }
+
+ EntropyStatus syscallGetrandom(scope void[] buffer, uint flags) @system
+ {
+ import core.sys.linux.errno : EINTR, ENOSYS, errno;
+ import core.sys.linux.sys.syscall : SYS_getrandom;
+ import core.sys.linux.unistd : syscall;
+
+ while (buffer.length > 0)
+ {
+ const got = syscall(SYS_getrandom, buffer.ptr, buffer.length, flags);
+
+ if (got == -1)
+ {
+ switch (errno)
+ {
+ case EINTR:
+ break; // That’s fine.
+ case ENOSYS:
+ return EntropyStatus.unavailable;
+ default:
+ return EntropyStatus.readError;
+ }
+ }
+
+ if (got > 0)
+ buffer = buffer[got .. $];
+ }
+
+ return EntropyStatus.ok;
+ }
+}
+
+// BSD
+private
+{
+ version (Darwin)
+ version = SecureARC4Random;
+ version (DragonFlyBSD)
+ version = UseGetentropy;
+ version (FreeBSD)
+ version = UseGetentropy;
+ version (NetBSD)
+ version = SecureARC4Random;
+ version (OpenBSD)
+ version = SecureARC4Random;
+
+ version (SecureARC4Random)
+ {
+ EntropyResult getEntropyViaARC4Random(scope void[] buffer) @trusted
+ {
+ arc4random_buf(buffer.ptr, buffer.length);
+ return EntropyResult(EntropyStatus.ok, EntropySource.arc4random);
+ }
+
+ private extern(C) void arc4random_buf(scope void* buf, size_t nbytes) @system;
+ }
+
+ version (UseGetentropy)
+ {
+ EntropyResult getEntropyViaGetentropy(scope void[] buffer) @trusted
+ {
+ const status = callGetentropy(buffer);
+ return EntropyResult(status, EntropySource.getentropy);
+ }
+
+ private EntropyStatus callGetentropy(scope void[] buffer) @system
+ {
+ /+
+ genentropy(3):
+ The maximum buflen permitted is 256 bytes.
+ +/
+ foreach (chunk; VoidChunks(buffer, 256))
+ {
+ const status = getentropy(buffer.ptr, buffer.length);
+ if (status != 0)
+ return EntropyStatus.readError;
+ }
+
+ return EntropyStatus.ok;
+ }
+
+ private extern(C) int getentropy(scope void* buf, size_t buflen) @system;
+ }
+}
+
+version (Windows)
+{
+ import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG;
+ import core.sys.windows.windef : HMODULE, PUCHAR, ULONG;
+ import core.sys.windows.ntdef : NT_SUCCESS;
+
+private:
+
+ EntropyResult getEntropyViaBCryptGenRandom(scope void[] buffer) @trusted
+ {
+ const loaded = loadBcrypt();
+ if (loaded != EntropyStatus.ok)
+ return EntropyResult(loaded, EntropySource.bcryptGenRandom);
+
+ const status = callBcryptGenRandom(buffer);
+ return EntropyResult(status, EntropySource.bcryptGenRandom);
+ }
+
+ EntropyStatus callBcryptGenRandom(scope void[] buffer) @system
+ {
+ foreach (chunk; VoidChunks(buffer, ULONG.max))
+ {
+ assert(chunk.length <= ULONG.max, "Bad chunk length.");
+
+ const gotRandom = ptrBCryptGenRandom(
+ null,
+ cast(PUCHAR) buffer.ptr,
+ cast(ULONG) buffer.length,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+ );
+
+ if (!NT_SUCCESS(gotRandom))
+ return EntropyStatus.readError;
+ }
+
+ return EntropyStatus.ok;
+ }
+
+ static
+ {
+ HMODULE hBcrypt = null;
+ typeof(BCryptGenRandom)* ptrBCryptGenRandom;
+ }
+
+ EntropyStatus loadBcrypt() @system
+ {
+ import core.sys.windows.winbase : GetProcAddress, LoadLibraryA;
+
+ if (hBcrypt !is null)
+ return EntropyStatus.ok;
+
+ hBcrypt = LoadLibraryA("Bcrypt.dll");
+ if (!hBcrypt)
+ return EntropyStatus.unavailableLibrary;
+
+ ptrBCryptGenRandom = cast(typeof(ptrBCryptGenRandom)) GetProcAddress(hBcrypt, "BCryptGenRandom");
+ if (!ptrBCryptGenRandom)
+ return EntropyStatus.unavailable;
+
+ return EntropyStatus.ok;
+ }
+
+ // Will free `Bcrypt.dll`.
+ void freeBcrypt() @system
+ {
+ import core.sys.windows.winbase : FreeLibrary;
+
+ if (hBcrypt is null)
+ return;
+
+ if (!FreeLibrary(hBcrypt))
+ {
+ return; // Error
+ }
+
+ hBcrypt = null;
+ ptrBCryptGenRandom = null;
+ }
+
+ static ~this() @system
+ {
+ freeBcrypt();
+ }
+}
negative = 0;
nz = 0.0;
- if ( x <= 0.0 )
+ if ( x == 0.0 )
+ {
+ return signbit(x) == 1 ? real.infinity : -real.infinity;
+ }
+
+ if ( x < 0.0 )
{
negative = 1;
q = x;
assert(digamma(1.0)== -EULERGAMMA);
assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7);
assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA) >= real.mant_dig-7);
+ assert(digamma(-0.0) == real.infinity);
+ assert(!digamma(nextDown(-0.0)).isNaN());
+ assert(digamma(+0.0) == -real.infinity);
+ assert(!digamma(nextUp(+0.0)).isNaN());
assert(digamma(-5.0).isNaN());
assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3) >= real.mant_dig-9);
assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC)));
* NAN = $(RED NAN)
* SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
* GAMMA = Γ
+ * PSI = Ψ
* THETA = θ
* INTEGRAL = ∫
* INTEGRATE = $(BIG ∫<sub>$(SMALL $1)</sub><sup>$2</sup>)
* BIGSUM = $(BIG Σ <sup>$2</sup><sub>$(SMALL $1)</sub>)
* CHOOSE = $(BIG () <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG ))
* PLUSMN = ±
+ * MNPLUS = ∓
* INFIN = ∞
* PLUSMNINF = ±∞
+ * MNPLUSINF = ∓∞
* PI = π
* LT = <
* GT = >
assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
}
-/** Digamma function
+/** Digamma function, $(PSI)(x)
*
- * The digamma function is the logarithmic derivative of the gamma function.
+ * $(PSI)(x), is the logarithmic derivative of the gamma function, $(GAMMA)(x).
*
- * digamma(x) = d/dx logGamma(x)
+ * $(PSI)(x) = $(SUP d)$(SUB /, dx) ln|$(GAMMA)(x)| (the derivative of `logGamma(x)`)
*
- * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse).
+ * Params:
+ * x = the domain value
+ *
+ * Returns:
+ * It returns $(PSI)(x).
+ *
+ * $(TABLE_SV
+ * $(SVH x, digamma(x) )
+ * $(SV integer < 0, $(NAN) )
+ * $(SV $(PLUSMN)0.0, $(MNPLUSINF) )
+ * $(SV +$(INFIN), +$(INFIN) )
+ * $(SV -$(INFIN), $(NAN) )
+ * $(SV $(NAN), $(NAN) )
+ * )
+ *
+ * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse).
*/
real digamma(real x)
{
return std.internal.math.gammafunction.digamma(x);
}
+///
+@safe unittest
+{
+ const euler = 0.57721_56649_01532_86060_65121L;
+
+ assert(isClose(digamma(1), -euler));
+ assert(digamma(+0.) == -real.infinity);
+ assert(digamma(-0.) == +real.infinity);
+ assert(digamma(+real.infinity) == +real.infinity);
+ assert(isNaN(digamma(-1)));
+ assert(isNaN(digamma(-real.infinity)));
+}
+
/** Log Minus Digamma function
*
* logmdigamma(x) = log(x) - digamma(x)
}
else if (range.length == 1)
{
- buf[0] = range[0];
+ static if (isNumeric!(ElementType!R))
+ {
+ buf[0].re = range[0];
+ buf[0].im = 0;
+ }
+ else
+ {
+ buf[0].re = range[0].re;
+ buf[0].im = range[0].im;
+ }
return;
}
else if (range.length == 2)
assert(isClose(twoInv[1].im, 0, 0.0, 1e-10));
}
+// https://github.com/dlang/phobos/issues/10796
+@system unittest
+{
+ import std.algorithm;
+ import std.range;
+ static struct C { float re, im; } // User-defined complex
+
+ float[8] arr = [1,2,3,4,5,6,7,8];
+ C[8] fft1;
+ fft(arr[], fft1[]);
+ assert(isClose(fft1[].map!"a.re",
+ [36.0, -4, -4, -4, -4, -4, -4, -4], 1e-4));
+ assert(isClose(fft1[].map!"a.im",
+ [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568], 1e-4));
+
+ auto inv = inverseFft(fft1[]);
+ assert(isClose(inv[].map!"a.re", arr[], 1e-6));
+ assert(inv[].map!"a.im".maxElement < 1e-10);
+}
+
// Swaps the real and imaginary parts of a complex number. This is useful
// for inverse FFTs.
C swapRealImag(C)(C input)
// using a generic slow DFT. This seems to be the best base case. (Size 1
// can be coded inline as buf[0] = range[0]).
void slowFourier2(Ret, R)(R range, Ret buf)
+if (isComplexLike!(ElementType!Ret))
+in (range.length == 2)
+in (buf.length == 2)
{
- assert(range.length == 2);
- assert(buf.length == 2);
- buf[0] = range[0] + range[1];
- buf[1] = range[0] - range[1];
+ static if (isNumeric!(ElementType!R))
+ {
+ buf[0].re = range[0] + range[1];
+ buf[0].im = 0;
+ buf[1].re = range[0] - range[1];
+ buf[1].im = 0;
+ }
+ else
+ {
+ buf[0].re = range[0].re + range[1].re;
+ buf[0].im = range[0].im + range[1].im;
+ buf[1].re = range[0].re - range[1].re;
+ buf[1].im = range[0].im - range[1].im;
+ }
}
// Hard-coded base case for FFT of size 4. Doesn't work as well as the size
}
version (linux)
-{
- // `getrandom()` was introduced in Linux 3.17.
-
- // Shim for missing bindings in druntime
- version (none)
- import core.sys.linux.sys.random : getrandom;
- else
- {
- import core.sys.posix.sys.types : ssize_t;
- extern extern(C) ssize_t getrandom(
- void* buf,
- size_t buflen,
- uint flags,
- ) @system nothrow @nogc;
- }
-}
-
+ version = SeedUseGetEntropy;
version (Windows)
-{
- pragma(lib, "Bcrypt.lib");
-
- private bool bcryptGenRandom(T)(out T result) @trusted
- {
- import core.sys.windows.windef : PUCHAR, ULONG;
- import core.sys.windows.ntdef : NT_SUCCESS;
- import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG;
-
- const gotRandom = BCryptGenRandom(
- null,
- cast(PUCHAR) &result,
- ULONG(T.sizeof),
- BCRYPT_USE_SYSTEM_PREFERRED_RNG,
- );
-
- return NT_SUCCESS(gotRandom);
- }
-}
+ version = SeedUseGetEntropy;
/**
A "good" seed for initializing random number engines. Initializing
*/
@property uint unpredictableSeed() @trusted nothrow @nogc
{
- version (linux)
+ version (SeedUseGetEntropy)
{
- uint buffer;
-
- /*
- getrandom(2):
- If the _urandom_ source has been initialized, reads of up to
- 256 bytes will always return as many bytes as requested and
- will not be interrupted by signals. No such guarantees apply
- for larger buffer sizes.
- */
- static assert(buffer.sizeof <= 256);
-
- const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
- assert(status == buffer.sizeof);
+ import std.internal.entropy : crashOnError, EntropySource, getEntropy;
+ uint buffer;
+ const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))();
+ crashOnError(status);
return buffer;
}
- else version (Windows)
- {
- uint result;
- if (!bcryptGenRandom!uint(result))
- {
- version (none)
- return fallbackSeed();
- else
- assert(false, "BCryptGenRandom() failed.");
- }
- return result;
- }
else version (AnyARC4Random)
{
return arc4random();
/// ditto
@property UIntType unpredictableSeed() @nogc nothrow @trusted
{
- version (linux)
+ version (SeedUseGetEntropy)
{
- UIntType buffer;
-
- /*
- getrandom(2):
- If the _urandom_ source has been initialized, reads of up to
- 256 bytes will always return as many bytes as requested and
- will not be interrupted by signals. No such guarantees apply
- for larger buffer sizes.
- */
- static assert(buffer.sizeof <= 256);
-
- const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
- assert(status == buffer.sizeof);
+ import std.internal.entropy : crashOnError, EntropySource, getEntropy;
+ UIntType buffer;
+ const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))();
+ crashOnError(status);
return buffer;
}
- else version (Windows)
- {
- UIntType result;
- if (!bcryptGenRandom!UIntType(result))
- {
- version (none)
- return fallbackSeed();
- else
- assert(false, "BCryptGenRandom() failed.");
- }
- return result;
- }
else version (AnyARC4Random)
{
static if (UIntType.sizeof <= uint.sizeof)
}
/++
- Convenience function which calls
+ `drop` is a convenience function which calls
$(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`.
- `drop` makes it easier to pop elements from a range
+ Unlike `popFrontN`, the range argument is passed by copy, not by `ref`.
+
+ `drop` makes it easier to pop elements from a range rvalue
and then pass it to another function within a single expression,
whereas `popFrontN` would require multiple statements.
{
import std.algorithm.comparison : equal;
- assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
+ auto a = [0, 2, 1, 5, 0, 3];
+ assert(a.drop(3) == [5, 0, 3]);
+ assert(a.length == 6); // original unchanged
+
assert("hello world".drop(6) == "world");
assert("hello world".drop(50).empty);
assert("hello world".take(6).drop(3).equal("lo "));
`range` with `n` elements dropped
See_Also:
- $(REF popFrontExcatly, std, range, primitives),
- $(REF popBackExcatly, std, range, primitives)
+ $(REF popFrontExactly, std, range, primitives),
+ $(REF popBackExactly, std, range, primitives)
+/
R dropExactly(R)(R range, size_t n)
if (isInputRange!R)
}
/++
- Convenience function which calls
- `range.popFront()` and returns `range`. `dropOne`
- makes it easier to pop an element from a range
+ `dropOne` is a convenience function which calls
+ `range.popFront()` and returns `range`.
+ Unlike `popFront`, the range argument is passed by copy, not by `ref`.
+
+ `dropOne` makes it easier to pop an element from a range rvalue
and then pass it to another function within a single expression,
whereas `popFront` would require multiple statements.
* tabsize = column spacing of tabs in firstindent[] and indent[]
* Returns:
* resulting paragraph as an allocated string
+ * Bugs:
+ * Columns are counted as the number of code points in the string. This may
+ * not correspond with the actual number of columns displayed if the string
+ * contains combining characters, modifiers, zero-width characters, or
+ * double-width characters.
*/
S wrap(S)(S s, in size_t columns = 80, S firstindent = null,
/**
Detect whether `T` is a callable object, which can be called with the
function call operator `$(LPAREN)...$(RPAREN)`.
+
+$(NOTE Implicit Function Template Instantiation is *not* attempted - see below.)
*/
template isCallable(alias callable)
{
}
}
-/// Functions, lambdas, and aggregate types with (static) opCall.
+/// Functions, function pointers, delegates, lambdas.
@safe unittest
{
void f() { }
static assert( isCallable!f);
static assert( isCallable!g);
+ auto fp = &f;
+ static assert( isCallable!fp);
+ static assert( isCallable!((int x) {}));
+
+ int x;
+ static assert(!isCallable!x);
+}
+
+/// Aggregate types with (static) opCall.
+@safe unittest
+{
class C { int opCall(int) { return 0; } }
auto c = new C;
struct S { static int opCall(int) { return 0; } }
static assert(!isCallable!I);
}
-/// Templates
+/// Template functions are only detected if they are instantiable with `!()`.
@safe unittest
{
void f()() { }
static assert( isCallable!g);
static assert( isCallable!S1);
static assert( isCallable!S2);
+
+ static assert(!isCallable!((x) {}));
}
-/// Overloaded functions and function templates.
+/// Overloaded functions and function templates instantiable with `!()`.
@safe unittest
{
static struct Wrapper
* Returns:
* A `string` if `writer` and `fmt` are not set; `void` otherwise.
*/
- string toString()
+ string toString()()
{
import std.array : appender;
auto app = appender!string();
auto spec = singleSpec("%s");
- toString(app, spec);
+ this.toString(app, spec);
return app.data;
}
/// ditto
- string toString() const
+ string toString()() const
{
import std.array : appender;
auto app = appender!string();
auto spec = singleSpec("%s");
- toString(app, spec);
+ this.toString(app, spec);
return app.data;
}
if (atts & FA.property) poatts ~= " @property";
if (atts & FA.safe ) poatts ~= " @safe";
if (atts & FA.trusted ) poatts ~= " @trusted";
- if (atts & FA.scope_ ) poatts ~= " scope";
if (atts & FA.return_ ) poatts ~= " return";
+ if (atts & FA.scope_ ) poatts ~= " scope";
return poatts;
}
enum postAtts = make_postAtts();
{
if (i)
result ~= ", ";
+ if (storageClasses[i] & ParameterStorageClass.return_)
+ result ~= "return ";
if (storageClasses[i] & ParameterStorageClass.scope_)
result ~= "scope ";
- if (storageClasses[i] & ParameterStorageClass.in_)
- result ~= "in ";
if (storageClasses[i] & ParameterStorageClass.out_)
result ~= "out ";
if (storageClasses[i] & ParameterStorageClass.ref_)
result ~= "ref ";
+ if (storageClasses[i] & ParameterStorageClass.in_)
+ result ~= "in ";
if (storageClasses[i] & ParameterStorageClass.lazy_)
result ~= "lazy ";
- if (storageClasses[i] & ParameterStorageClass.return_)
- result ~= "return ";
result ~= "PX[" ~ i.stringof ~ "]";
}
testTombstonePurging();
testClear();
testTypeInfoCollect();
+ testNew();
+ testAliasThis();
}
void testKeysValues1()
s = null; // clear any reference to the entry
GC.collect(); // used to segfault.
}
+
+void testNew()
+{
+ auto aa = new long[int]; // call _d_newAA
+ assert(aa.length == 0);
+ foreach (i; 0 .. 100)
+ aa[i] = i * 2;
+ assert(aa.length == 100);
+
+ // not supported in CTFE (it doesn't do much anyway):
+ // static auto aa = new long[int];
+}
+
+void testAliasThis()
+{
+ static struct S
+ {
+ __gshared int numCopies;
+
+ ubyte[long] aa;
+ S* next;
+
+ this(this) { numCopies++; }
+
+ alias aa this;
+ }
+ S s;
+ long key = 1;
+ s.aa[1] = 1; // create and insert
+ assert(S.numCopies == 0);
+ if (auto p = 1 in s)
+ *p = 2;
+ if (auto p = key in s)
+ *p = 3;
+ if (auto p = () { return 1; }() in s)
+ *p = 4;
+ s.remove(1);
+ assert(S.numCopies == 0);
+}
static import core.memory;
+import core.thread.threadbase : ThreadBase;
+
extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"];
extern (C) pragma(crt_constructor) void register_mygc()
return false;
}
+ void initThread(ThreadBase thread) nothrow @nogc
+ {
+ }
+
+ void cleanupThread(ThreadBase thread) nothrow @nogc
+ {
+ }
+
private:
// doesn't care for alignment
static void* sentinelAdd(void* p, size_t value)