return build_target_expr (decl, exp);
}
+/* Determine whether expression EXP can have a copy of its value elided. */
+
+static bool
+can_elide_copy_p (Expression *exp)
+{
+ /* Explicit `__rvalue(exp)'. */
+ if (exp->rvalue)
+ return true;
+
+ /* Look for variable expression. */
+ Expression *last = exp;
+ while (CommaExp *ce = last->isCommaExp ())
+ last = ce->e2;
+
+ if (VarExp *ve = last->isVarExp ())
+ {
+ if (VarDeclaration *vd = ve->var->isVarDeclaration ())
+ {
+ /* Variable is an implicit copy of an lvalue. */
+ if (vd->storage_class & STCrvalue)
+ return true;
+
+ /* The destructor is going to run on the variable. */
+ if (vd->isArgDtorVar ())
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Returns the address of the expression EXP. */
tree
- The ABI of the function expects the callee to destroy its
arguments; when the caller is handles destruction, then `targ'
has already been made into a temporary. */
- if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor)
- || target.isCalleeDestroyingArgs (tf))
+ if (!can_elide_copy_p (arg)
+ && (arg->op == EXP::structLiteral
+ || (!sd->postblit && !sd->dtor)
+ || target.isCalleeDestroyingArgs (tf)))
targ = force_target_expr (targ);
targ = convert (build_reference_type (TREE_TYPE (targ)),
global.params.v.errorLimit = flag_max_errors;
global.params.v.showColumns = flag_show_column;
- global.params.v.printErrorContext = flag_diagnostics_show_caret;
+ global.params.v.errorPrintMode = flag_diagnostics_show_caret
+ ? ErrorPrintMode::printErrorContext : ErrorPrintMode::simpleError;
/* Keep the front-end location type in sync with params. */
Loc::set (global.params.v.showColumns, global.params.v.messageStyle);
-82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
+c57da0cf5945cfb45eed06f1fd820435cda3ee3a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
enum TRUST : ubyte
{
default_ = 0,
- system = 1, // @system (same as TRUST.default)
+ system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled)
trusted = 2, // @trusted
safe = 3, // @safe
}
return sc2;
}
-
- override void addComment(const(char)* comment)
- {
- //printf("AttribDeclaration::addComment %s\n", comment);
- if (comment)
- {
- this.include(null).foreachDsymbol( s => s.addComment(comment) );
- }
- }
-
override const(char)* kind() const
{
return "attribute";
}
}
- override final void addComment(const(char)* comment)
- {
- /* Because addComment is called by the parser, if we called
- * include() it would define a version before it was used.
- * But it's no problem to drill down to both decl and elsedecl,
- * so that's the workaround.
- */
- if (comment)
- {
- decl .foreachDsymbol( s => s.addComment(comment) );
- elsedecl.foreachDsymbol( s => s.addComment(comment) );
- }
- }
-
override void accept(Visitor v)
{
v.visit(this);
return false;
}
- override void addComment(const(char)* comment)
- {
- // do nothing
- // change this to give semantics to documentation comments on static foreach declarations
- }
-
override const(char)* kind() const
{
return "static foreach";
{
public:
Dsymbols *decl; // array of Dsymbol's
- void addComment(const utf8_t *comment) override;
const char *kind() const override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override;
bool hasPointers() override final;
ConditionalDeclaration *syntaxCopy(Dsymbol *s) override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override final;
- void addComment(const utf8_t *comment) override final;
void accept(Visitor *v) override { v->visit(this); }
};
StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override;
bool oneMember(Dsymbol *&ps, Identifier *ident) override;
- void addComment(const utf8_t *comment) override;
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
};
* Params:
* sd = the `struct` for which the copy constructor is generated
* hasCpCtor = set to true if a copy constructor is already present
+ * hasMoveCtor = set to true if a move constructor is already present
*
* Returns:
* `true` if one needs to be generated
* `false` otherwise
*/
-bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
+bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor, out bool hasMoveCtor)
{
+ //printf("needCopyCtor() %s\n", sd.toChars());
if (global.errors)
return false;
return 0;
}
- if (isRvalueConstructor(sd, ctorDecl))
+ if (ctorDecl.isMoveCtor)
rvalueCtor = ctorDecl;
return 0;
});
+ if (rvalueCtor)
+ hasMoveCtor = true;
+
if (cpCtor)
{
- if (rvalueCtor)
+ if (0 && rvalueCtor)
{
.error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
* Params:
* sd = the `struct` for which the copy constructor is generated
* sc = the scope where the copy constructor is generated
+ * hasMoveCtor = set to true when a move constructor is also detected
*
* Returns:
* `true` if `struct` sd defines a copy constructor (explicitly or generated),
* References:
* https://dlang.org/spec/struct.html#struct-copy-constructor
*/
-bool buildCopyCtor(StructDeclaration sd, Scope* sc)
+bool buildCopyCtor(StructDeclaration sd, Scope* sc, out bool hasMoveCtor)
{
bool hasCpCtor;
- if (!needCopyCtor(sd, hasCpCtor))
+ if (!needCopyCtor(sd, hasCpCtor, hasMoveCtor))
return hasCpCtor;
//printf("generating copy constructor for %s\n", sd.toChars());
UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2)
{
+ //printf("Identity %s %s\n", e1.toChars(), e2.toChars());
UnionExp ue = void;
int cmp;
if (e1.op == EXP.null_)
{
SymOffExp es1 = e1.isSymOffExp();
SymOffExp es2 = e2.isSymOffExp();
- cmp = (es1.var == es2.var && es1.offset == es2.offset);
+ cmp = es1.offset == es2.offset;
+ if (cmp)
+ {
+ cmp = es1.var == es2.var;
+ if (!cmp && (es1.var.isParameter() || es2.var.isParameter()))
+ {
+ // because of ref's, they may still be the same, we cannot tell
+ cantExp(ue);
+ return ue;
+ }
+ }
}
else
{
import dmd.funcsem : overloadApply, getLevelAndCheck;
import dmd.globals;
import dmd.gluelayer;
+import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.init;
enum ignoreRead = 2; // ignore any reads of AliasDeclaration
enum nounderscore = 4; // don't prepend _ to mangled name
enum hidden = 8; // don't print this in .di files
+ enum nrvo = 0x10; /// forward to fd.nrvo_var when generating code
// overridden symbol with pragma(mangle, "...")
const(char)[] mangleOverride;
extern (D) this(const ref Loc loc, Identifier ident, Type type) @safe
{
super(loc, ident);
- //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type);
- //printf("type = '%s'\n", type.toChars());
+ //debug printf("AliasDeclaration(id = '%s', type = `%s`, %p)\n", ident.toChars(), dmd.hdrgen.toChars(type), type.isTypeIdentifier());
this.type = type;
assert(type);
}
extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) @safe
{
super(loc, ident);
- //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s);
+ //debug printf("AliasDeclaration(id = '%s', s = `%s`)\n", ident.toChars(), s.toChars());
assert(s != this);
this.aliassym = s;
assert(s);
override Dsymbol toAlias()
{
- //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n",
- // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse);
+ 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;
{
public:
d_bool isCpCtor;
+ d_bool isMoveCtor;
CtorDeclaration *syntaxCopy(Dsymbol *) override;
const char *kind() const override;
const char *toChars() const override;
import dmd.declaration;
import dmd.dmodule;
import dmd.doc;
+import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
/// do not set wasRead for it
+ StructDeclaration argStruct; /// elimiate recursion when looking for rvalue construction
extern (D) __gshared Scope* freelist;
bool hasIdentityEquals; // true if has identity opEquals
bool hasNoFields; // has no fields
bool hasCopyCtor; // copy constructor
+ bool hasMoveCtor; // move constructor
bool hasPointerField; // members with indirections
bool hasVoidInitPointers; // void-initialized unsafe fields
bool hasUnsafeBitpatterns; // @system members, pointers, bool
{
Dsymbol s = (*members)[i];
s.setFieldOffset(this, &fieldState, isunion);
- }
- if (type.ty == Terror)
- {
- errors = true;
- return;
+ if (type.ty == Terror)
+ {
+ errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars);
+ errors = true;
+ return;
+ }
}
if (structsize == 0)
import dmd.clone;
bool hasCpCtorLocal;
- needCopyCtor(this, hasCpCtorLocal);
+ bool hasMoveCtorLocal;
+ needCopyCtor(this, hasCpCtorLocal, hasMoveCtorLocal);
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;
*/
void addComment(const(char)* comment)
{
- if (!comment || !*comment)
- return;
-
- //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars());
- void* h = cast(void*)this; // just the pointer is the key
- auto p = h in commentHashTable;
- if (!p)
- {
- commentHashTable[h] = comment;
- return;
- }
- if (strcmp(*p, comment) != 0)
- {
- // Concatenate the two
- *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true);
- }
+ import dmd.dsymbolsem;
+ dmd.dsymbolsem.addComment(this, comment);
}
/// get documentation comment for this Dsymbol
/* Shell around addComment() to avoid disruption for the moment */
final void comment(const(char)* comment) { addComment(comment); }
- private extern (D) __gshared const(char)*[void*] commentHashTable;
+ extern (D) __gshared const(char)*[void*] commentHashTable;
/**********************************
Dsymbols *include(Dsymbol *d, Scope *sc);
void setScope(Dsymbol *d, Scope *sc);
void importAll(Dsymbol *d, Scope *sc);
+ void addComment(Dsymbol *d, const char *comment);
}
import dmd.initsem;
import dmd.intrange;
import dmd.hdrgen;
+import dmd.lexer;
import dmd.location;
import dmd.mtype;
import dmd.mustuse;
debug import dmd.printast;
import dmd.root.array;
import dmd.root.filename;
+import dmd.root.string;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.rootobject;
*/
bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
{
+ //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
+ /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
+ * so use isRvalueConstructor()
+ */
if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
{
.error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
*/
bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
{
+ // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
auto tf = ctor.type.isTypeFunction();
const dim = tf.parameterList.length;
if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
*/
Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
{
+ //printf("resolveAliasThis() %s\n", toChars(e));
import dmd.typesem : dotExp;
for (AggregateDeclaration ad = isAggregate(e.type); ad;)
{
override void visit(CtorDeclaration ctd)
{
- //printf("CtorDeclaration::semantic() %s\n", toChars());
+ //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
if (ctd.semanticRun >= PASS.semanticdone)
return;
if (ctd._scope)
sc.stc &= ~STC.static_; // not a static constructor
funcDeclarationSemantic(sc, ctd);
+ // Check short constructor: this() => expr;
+ if (ctd.fbody)
+ {
+ if (auto s = ctd.fbody.isExpStatement())
+ {
+ if (s.exp)
+ {
+ auto ce = s.exp.isCallExp();
+ // check this/super before semantic
+ if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp()))
+ {
+ s.exp = s.exp.expressionSemantic(sc);
+ if (s.exp.type.ty != Tvoid)
+ error(s.loc, "can only return void expression, `this` call or `super` call from constructor");
+ }
+ }
+ }
+ }
sc.pop();
}
else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
{
- //printf("tf: %s\n", tf.toChars());
+ //printf("tf: %s\n", toChars(tf));
auto param = tf.parameterList[0];
- if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+ if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
- //printf("copy constructor\n");
- ctd.isCpCtor = true;
+ //printf("copy constructor %p\n", ctd);
+ if (param.storageClass & STC.ref_)
+ ctd.isCpCtor = true; // copy constructor
+ else
+ ctd.isMoveCtor = true; // move constructor
}
}
}
buildDtors(sd, sc2);
- sd.hasCopyCtor = buildCopyCtor(sd, sc2);
+ bool hasMoveCtor;
+ sd.hasCopyCtor = buildCopyCtor(sd, sc2, hasMoveCtor);
+ sd.hasMoveCtor = hasMoveCtor;
+
sd.postblit = buildPostBlit(sd, sc2);
buildOpAssign(sd, sc2);
// function used to perform semantic on AliasDeclaration
void aliasSemantic(AliasDeclaration ds, Scope* sc)
{
- //printf("AliasDeclaration::semantic() %s\n", ds.toChars());
+ //printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym);
// as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first.
// see https://issues.dlang.org/show_bug.cgi?id=21001
Expression ec;
ec = new VarExp(vd.loc, vd);
e = new DeleteExp(vd.loc, ec, true);
- e.type = Type.tvoid;
break;
}
return e;
}
return false;
}
+
+extern (C++) void addComment(Dsymbol d, const(char)* comment)
+{
+ scope v = new AddCommentVisitor(comment);
+ d.accept(v);
+}
+
+extern (C++) class AddCommentVisitor: Visitor
+{
+ alias visit = Visitor.visit;
+
+ const(char)* comment;
+
+ this(const(char)* comment)
+ {
+ this.comment = comment;
+ }
+
+ override void visit(Dsymbol d)
+ {
+ if (!comment || !*comment)
+ return;
+
+ //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars());
+ void* h = cast(void*)d; // just the pointer is the key
+ auto p = h in d.commentHashTable;
+ if (!p)
+ {
+ d.commentHashTable[h] = comment;
+ return;
+ }
+ if (strcmp(*p, comment) != 0)
+ {
+ // Concatenate the two
+ *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true);
+ }
+ }
+ override void visit(AttribDeclaration atd)
+ {
+ if (comment)
+ {
+ atd.include(null).foreachDsymbol( s => s.addComment(comment) );
+ }
+ }
+ override void visit(ConditionalDeclaration cd)
+ {
+ if (comment)
+ {
+ cd.decl .foreachDsymbol( s => s.addComment(comment) );
+ cd.elsedecl.foreachDsymbol( s => s.addComment(comment) );
+ }
+ }
+ override void visit(StaticForeachDeclaration sfd) {}
+}
*/
extern (C++) abstract class Expression : ASTNode
{
- Type type; // !=null means that semantic() has been run
+ /// Usually, this starts out as `null` and gets set to the final expression type by
+ /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`,
+ /// `VarExp`), the field can get set to an assigned type before running semantic.
+ /// See `expressionSemanticDone`
+ Type type;
+
Loc loc; // file location
const EXP op; // to minimize use of dynamic_cast
bool parens; // if this is a parenthesized expression
+ bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue
extern (D) this(const ref Loc loc, EXP op) scope @safe
{
inout(TypeidExp) isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; }
inout(TraitsExp) isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; }
inout(HaltExp) isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; }
- inout(IsExp) isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
+ inout(IsExp) isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
override final bool isLvalue()
{
- return true;
+ return !this.rvalue;
}
override void accept(Visitor v)
override bool isLvalue()
{
- return true;
+ return !rvalue;
}
override void accept(Visitor v)
override final bool isLvalue()
{
// Class `this` should be an rvalue; struct `this` should be an lvalue.
- return type.toBasetype().ty != Tclass;
+ return !rvalue && type.toBasetype().ty != Tclass;
}
override void accept(Visitor v)
/* string literal is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
- return (type && type.toBasetype().ty == Tsarray);
+ return !rvalue && (type && type.toBasetype().ty == Tsarray);
}
/********************************
override bool isLvalue()
{
- if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
+ if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
return false;
return true;
}
override final bool isLvalue()
{
- return true;
+ return !rvalue;
}
override void accept(Visitor v)
extern (D) this(const ref Loc loc, Expression e)
{
super(loc, EXP.throw_, e);
- this.type = Type.tnoreturn;
}
override ThrowExp syntaxCopy()
override bool isLvalue()
{
+ if (rvalue)
+ return false;
if (e1.op != EXP.structLiteral)
return true;
auto vd = var.isVarDeclaration();
override bool isLvalue()
{
+ if (rvalue)
+ return false;
Type tb = e1.type.toBasetype();
if (tb.ty == Tdelegate || tb.ty == Tpointer)
tb = tb.nextOf();
override bool isLvalue()
{
- return true;
+ return !rvalue;
}
override void accept(Visitor v)
override bool isLvalue()
{
//printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
- if (!e1.isLvalue())
+ if (rvalue || !e1.isLvalue())
return false;
return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
override bool isLvalue()
{
- return e1.isLvalue();
+ return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
/* slice expression is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
- return (type && type.toBasetype().ty == Tsarray);
+ return !rvalue && (type && type.toBasetype().ty == Tsarray);
}
override Optional!bool toBool()
override bool isLvalue()
{
+ if (rvalue)
+ return false;
if (type && type.toBasetype().ty == Tvoid)
return false;
return true;
override bool isLvalue()
{
- return e2.isLvalue();
+ return !rvalue && e2.isLvalue();
}
override Optional!bool toBool()
override bool isLvalue()
{
- return e1.isLvalue();
+ return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
override bool isLvalue()
{
- return e1.isLvalue();
+ return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
override bool isLvalue()
{
+ if (rvalue)
+ return false;
auto t1b = e1.type.toBasetype();
if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
(e1.isIndexExp() && t1b != t1b.isTypeDArray()))
{
return false;
}
- return true;
+ return !rvalue;
}
override void accept(Visitor v)
override bool isLvalue()
{
- return e1.isLvalue() && e2.isLvalue();
+ return !rvalue && e1.isLvalue() && e2.isLvalue();
}
override void accept(Visitor v)
Loc loc; // file location
EXP op; // to minimize use of dynamic_cast
d_bool parens; // if this is a parenthesized expression
+ d_bool rvalue; // consider this an rvalue, even if it is an lvalue
size_t size() const;
static void _init();
TypeidExp* isTypeidExp();
TraitsExp* isTraitsExp();
HaltExp* isHaltExp();
- IsExp* isExp();
+ IsExp* isIsExp();
MixinExp* isMixinExp();
ImportExp* isImportExp();
AssertExp* isAssertExp();
* Params:
* sc = the scope where the expression is encountered
* e = the expression the needs to be moved or copied (source)
- * t = if the struct defines a copy constructor, the type of the destination
- *
+ * t = if the struct defines a copy constructor, the type of the destination (can be NULL)
+ * nrvo = true if the generated copy can be treated as NRVO
+ * move = true to allow a move constructor to be used, false to prevent infinite recursion
* Returns:
* The expression that copy constructs or moves the value.
*/
-extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
+extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo, bool move = false)
{
+ //printf("doCopyOrMove() %s\n", toChars(e));
+ StructDeclaration sd;
+ if (t)
+ {
+ if (auto ts = t.isTypeStruct())
+ sd = ts.sym;
+ }
+
if (auto ce = e.isCondExp())
{
- ce.e1 = doCopyOrMove(sc, ce.e1);
- ce.e2 = doCopyOrMove(sc, ce.e2);
+ ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo);
+ ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo);
+ }
+ else if (e.isLvalue())
+ {
+ e = callCpCtor(sc, e, t, nrvo);
+ }
+ else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp())
+ {
+ // #move
+ /* Rewrite as:
+ * S __copyrvalue;
+ * __copyrvalue.moveCtor(e);
+ * __copyrvalue;
+ */
+ VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null);
+ if (nrvo)
+ vd.adFlags |= Declaration.nrvo;
+ vd.storage_class |= STC.nodtor;
+ vd.dsymbolSemantic(sc);
+ Expression de = new DeclarationExp(e.loc, vd);
+ Expression ve = new VarExp(e.loc, vd);
+
+ Expression er;
+ er = new DotIdExp(e.loc, ve, Id.ctor); // ve.ctor
+ er = new CallExp(e.loc, er, e); // ve.ctor(e)
+ er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd
+ er = Expression.combine(de, er); // de,ve.ctor(e),vd
+
+ e = er.expressionSemantic(sc);
}
else
{
- e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
+ e = valueNoDtor(e);
}
return e;
}
* If e is an instance of a struct, and that struct has a copy constructor,
* rewrite e as:
* (tmp = e),tmp
- * Input:
+ * Params:
* sc = just used to specify the scope of created temporary variable
* destinationType = the type of the object on which the copy constructor is called;
* may be null if the struct defines a postblit
+ * nrvo = true if the generated copy can be treated as NRVO
*/
-private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
+private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo)
{
+ //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType));
auto ts = e.type.baseElemOf().isTypeStruct();
if (!ts)
* This is not the most efficient, ideally tmp would be constructed
* directly onto the stack.
*/
- auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+ VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+ if (nrvo)
+ tmp.adFlags |= Declaration.nrvo;
if (sd.hasCopyCtor && destinationType)
{
// https://issues.dlang.org/show_bug.cgi?id=22619
*/
Expression valueNoDtor(Expression e)
{
+ //printf("valueNoDtor() %s\n", toChars(e));
auto ex = lastComma(e);
if (auto ce = ex.isCallExp())
continue;
}
- e = doCopyOrMove(sc, e);
+ e = doCopyOrMove(sc, e, null, false);
if (!foundType && t0 && !t0.equals(e.type))
{
Type* prettype, Expression* peprefix)
{
Expressions* arguments = argumentList.arguments;
- //printf("functionParameters() %s\n", fd ? fd.toChars() : "");
+ //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf));
assert(arguments);
assert(fd || tf.next);
const size_t nparams = tf.parameterList.length;
*/
Type tv = arg.type.baseElemOf();
if (!isRef && tv.ty == Tstruct)
- arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null);
+ arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false);
}
(*arguments)[i] = arg;
{
printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
}
- if (exp.type) // This is used as the dummy expression
- {
- result = exp;
- return;
- }
+
+ scope (exit) result.rvalue = exp.rvalue;
Dsymbol scopesym;
Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
{
printf("ThisExp::semantic()\n");
}
- if (e.type)
- {
- result = e;
- return;
- }
FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
AggregateDeclaration ad;
{
printf("SuperExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
FuncDeclaration fd = hasThis(sc);
ClassDeclaration cd;
printf("NullExp::semantic('%s')\n", e.toChars());
}
// NULL is the same as (void *)0
- if (e.type)
- {
- result = e;
- return;
- }
e.type = Type.tnull;
result = e;
}
{
printf("StringExp::semantic() %s\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
OutBuffer buffer;
size_t newlen = 0;
{
printf("+TupleExp::semantic(%s)\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (exp.e0)
exp.e0 = exp.e0.expressionSemantic(sc);
{
printf("ArrayLiteralExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
/* Perhaps an empty array literal [ ] should be rewritten as null?
*/
{
printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
// Run semantic() on each element
bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc);
{
printf("StructLiteralExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
e.sd.size(e.loc);
if (e.sd.sizeok != Sizeok.done)
{
printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
ScopeDsymbol sds2 = exp.sds;
TemplateInstance ti = sds2.isTemplateInstance();
printf("\tthisexp = %s\n", exp.thisexp.toChars());
printf("\tnewtype: %s\n", exp.newtype.toChars());
}
- if (exp.type) // if semantic() already run
- {
- result = exp;
- return;
- }
//for error messages if the argument in [] is not convertible to size_t
const originalNewtype = exp.newtype;
symtab = sds.symtab;
}
assert(symtab);
- Identifier id = Identifier.generateIdWithLoc(s, exp.loc);
+ Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars()));
exp.fd.ident = id;
if (exp.td)
exp.td.ident = id;
printf(" treq = %s\n", exp.fd.treq.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp;
{
printf("CallExp::semantic() %s\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return; // semantic() already run
- }
Objects* tiargs = null; // initial list of template arguments
Expression ethis = null;
errorSupplemental(exp.loc, "%s", failMessage);
}
- if (tf.callMatch(null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch)
+ if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch)
return setError();
// Purity and safety check should run after testing arguments matching
exp.f = null;
}
- if (tf.callMatch(null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch)
+ if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch)
exp.f = null;
}
if (!exp.f || exp.f.errors)
override void visit(DeclarationExp e)
{
- if (e.type)
- {
- result = e;
- return;
- }
static if (LOGSEMANTIC)
{
printf("DeclarationExp::semantic() %s\n", e.toChars());
override void visit(BinAssignExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp.op_overload(sc);
if (e)
{
import dmd.statementsem;
+ te.type = Type.tnoreturn;
if (throwSemantic(te.loc, te.e1, sc))
result = te;
else
static if (LOGSEMANTIC)
{
printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
- //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op));
+ printAST(exp);
}
if (sc.inCfile)
override void visit(DotTemplateExp e)
{
- if (e.type)
- {
- result = e;
- return;
- }
if (Expression ex = unaSemantic(e, sc))
{
result = ex;
{
printf("DotVarExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.var = exp.var.toAlias().isDeclaration();
{
printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
// Indicate we need to resolve by UFCS.
Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag);
if (!e)
{
printf("DelegateExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
e.e1 = e.e1.expressionSemantic(sc);
{
printf("DotTypeExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (auto e = unaSemantic(exp, sc))
{
{
printf("AddrExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = unaSemantic(exp, sc))
{
{
printf("PtrExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp.op_overload(sc);
if (e)
{
printf("NegExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp.op_overload(sc);
if (e)
{
printf("UAddExp::semantic('%s')\n", exp.toChars());
}
- assert(!exp.type);
Expression e = exp.op_overload(sc);
if (e)
override void visit(ComExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp.op_overload(sc);
if (e)
override void visit(NotExp e)
{
- if (e.type)
- {
- result = e;
- return;
- }
e.setNoderefOperand();
printf("CastExp::semantic('%s')\n", exp.toChars());
}
//static int x; assert(++x < 10);
- if (exp.type)
- {
- result = exp;
- return;
- }
if ((sc && sc.inCfile) &&
exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) &&
{
printf("VectorExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.e1 = exp.e1.expressionSemantic(sc);
exp.type = exp.to.typeSemantic(exp.loc, sc);
{
printf("SliceExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
// operator overloading should be handled in ArrayExp already.
if (Expression ex = unaSemantic(exp, sc))
{
printf("ArrayLengthExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
if (Expression ex = unaSemantic(e, sc))
{
{
printf("ArrayExp::semantic('%s')\n", exp.toChars());
}
- assert(!exp.type);
if (sc.inCfile)
{
override void visit(CommaExp e)
{
//printf("Semantic.CommaExp() %s\n", e.toChars());
- if (e.type)
- {
- result = e;
- return;
- }
// Allow `((a,b),(x,y))`
if (e.allowCommaExp)
{
printf("IntervalExp::semantic('%s')\n", e.toChars());
}
- if (e.type)
- {
- result = e;
- return;
- }
Expression le = e.lwr;
le = le.expressionSemantic(sc);
{
printf("IndexExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
// operator overloading should be handled in ArrayExp already.
if (!exp.e1.type)
{
printf("PostExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (sc.inCfile)
{
{
static if (LOGSEMANTIC)
{
- if (exp.op == EXP.blit) printf("BlitExp.toElem('%s')\n", exp.toChars());
- if (exp.op == EXP.assign) printf("AssignExp.toElem('%s')\n", exp.toChars());
- if (exp.op == EXP.construct) printf("ConstructExp.toElem('%s')\n", exp.toChars());
+ if (exp.op == EXP.blit) printf("BlitExp.semantic('%s')\n", exp.toChars());
+ if (exp.op == EXP.assign) printf("AssignExp.semantic('%s')\n", exp.toChars());
+ if (exp.op == EXP.construct) printf("ConstructExp.semantic('%s')\n", exp.toChars());
}
void setResult(Expression e, int line = __LINE__)
result = e;
}
- if (exp.type)
- {
- return setResult(exp);
- }
-
Expression e1old = exp.e1;
if (auto e2comma = exp.e2.isCommaExp())
/* We have a copy constructor for this
*/
+ //printf("exp: %s\n", toChars(exp));
+ //printf("e2x: %s\n", toChars(e2x));
if (e2x.isLvalue())
{
if (sd.hasCopyCtor)
return;
}
}
+ else if (sd.hasMoveCtor && !e2x.isCallExp() && !e2x.isStructLiteralExp())
+ {
+ // #move
+ /* The !e2x.isCallExp() is because it is already an rvalue
+ and the move constructor is unnecessary:
+ struct S {
+ alias TT this;
+ long TT();
+ this(T)(int x) {}
+ this(S);
+ this(ref S);
+ ~this();
+ }
+ S fun(ref S arg);
+ void test() { S st; fun(st); }
+ */
+ /* Rewrite as:
+ * e1 = init, e1.moveCtor(e2);
+ */
+ Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1));
+ einit.type = e1x.type;
+
+ Expression e;
+ e = new DotIdExp(exp.loc, e1x, Id.ctor);
+ e = new CallExp(exp.loc, e, e2x);
+ e = new CommaExp(exp.loc, einit, e);
+
+ //printf("e: %s\n", e.toChars());
+
+ result = e.expressionSemantic(sc);
+ return;
+ }
else
{
/* The struct value returned from the function is transferred
override void visit(PowAssignExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
Expression e = exp.op_overload(sc);
if (e)
override void visit(CatAssignExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
//printf("CatAssignExp::semantic() %s\n", exp.toChars());
Expression e = exp.op_overload(sc);
ce.trusted = true;
exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast);
- exp.e2 = doCopyOrMove(sc, exp.e2);
+ exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
}
else if (tb1.ty == Tarray &&
(tb1next.ty == Tchar || tb1next.ty == Twchar) &&
{
printf("AddExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
{
printf("MinExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
{
// https://dlang.org/spec/expression.html#cat_expressions
//printf("CatExp.semantic() %s\n", toChars());
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
{
if (exp.e1.op == EXP.arrayLiteral)
{
- exp.e2 = doCopyOrMove(sc, exp.e2);
+ exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
// https://issues.dlang.org/show_bug.cgi?id=14686
// Postblit call appears in AST, and this is
// finally translated to an ArrayLiteralExp in below optimize().
{
if (exp.e2.op == EXP.arrayLiteral)
{
- exp.e1 = doCopyOrMove(sc, exp.e1);
+ exp.e1 = doCopyOrMove(sc, exp.e1, null, false);
}
else if (exp.e2.op == EXP.string_)
{
{
printf("MulExp::semantic() %s\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
override void visit(DivExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
override void visit(ModExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
override void visit(PowExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
//printf("PowExp::semantic() %s\n", toChars());
if (Expression ex = binSemanticProp(exp, sc))
private void visitShift(BinExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
private void visitBinaryBitOp(BinExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
printf("LogicalExp::semantic() %s\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.setNoderefOperands();
{
printf("CmpExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.setNoderefOperands();
override void visit(InExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
if (Expression ex = binSemanticProp(exp, sc))
{
override void visit(EqualExp exp)
{
//printf("EqualExp::semantic('%s')\n", exp.toChars());
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.setNoderefOperands();
override void visit(IdentityExp exp)
{
- if (exp.type)
- {
- result = exp;
- return;
- }
exp.setNoderefOperands();
{
printf("CondExp::semantic('%s')\n", exp.toChars());
}
- if (exp.type)
- {
- result = exp;
- return;
- }
if (auto die = exp.econd.isDotIdExp())
die.noderef = true;
return null;
}
+/// Returns: whether expressionSemantic() has been run on expression `e`
+private bool expressionSemanticDone(Expression e)
+{
+ // Usually, Expression.type gets set by expressionSemantic and is `null` beforehand
+ // There are some exceptions however:
+ return e.type !is null && !(
+ e.isRealExp() // type sometimes gets set already before semantic
+ || 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
+ );
+}
+
// entrypoint for semantic ExpressionSemanticVisitor
Expression expressionSemantic(Expression e, Scope* sc)
{
+ if (e.expressionSemanticDone)
+ return e;
+
scope v = new ExpressionSemanticVisitor(sc);
e.accept(v);
return v.result;
private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
{
- //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
+ //printf("dotIdSemanticPropX() %s\n", toChars(exp));
if (Expression ex = unaSemantic(exp, sc))
return ex;
*/
Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
{
- //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
+ //printf("dotIdSemanticProp('%s')\n", exp.toChars());
//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag);
- Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag);
+ Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag);
if (e)
{
e = e.expressionSemantic(sc);
*/
Expression addDtorHook(Expression e, Scope* sc)
{
+ //printf("addDtorHook() %s\n", toChars(e));
Expression visit(Expression exp)
{
return exp;
if (e.op == EXP.error)
return false;
- (*elements)[i] = doCopyOrMove(sc, e);
+ (*elements)[i] = doCopyOrMove(sc, e, null, false);
}
return true;
}
*/
VarDeclarations outerVars;
+ // Most recent encountered `main` (`WinMain` or `DllMain`) function.
+ // Track it to give error messages for multiple entrypoints
+ __gshared FuncDeclaration lastMain;
+
/// Sibling nested functions which called this one
FuncDeclarations siblingCallers;
*/
extern (C++) final class CtorDeclaration : FuncDeclaration
{
- bool isCpCtor;
- extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false)
+ bool isCpCtor; // copy constructor
+ bool isMoveCtor; // move constructor (aka rvalue constructor)
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false, bool isMoveCtor = false)
{
super(loc, endloc, Id.ctor, stc, type);
this.isCpCtor = isCpCtor;
+ this.isMoveCtor = isMoveCtor;
//printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this);
}
}
}
+/****************************************
+ * Only one entry point function is allowed. Print error if more than one.
+ * Params:
+ * fd = a "main" function
+ * Returns:
+ * true if haven't seen "main" before
+ */
+extern (C++) bool onlyOneMain(FuncDeclaration fd)
+{
+ if (auto lastMain = FuncDeclaration.lastMain)
+ {
+ const format = (target.os == Target.OS.Windows)
+ ? "only one entry point `main`, `WinMain` or `DllMain` is allowed"
+ : "only one entry point `main` is allowed";
+ error(fd.loc, format.ptr);
+ errorSupplemental(lastMain.loc, "previously found `%s` here", lastMain.toFullSignature());
+ return false;
+ }
+ FuncDeclaration.lastMain = fd;
+ return true;
+}
+
/**********************************
* Main semantic routine for functions.
*/
FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags)
{
+ //printf("resolveFuncCall() %s\n", s.toChars());
auto fargs = argumentList.arguments;
if (!s)
return null; // no match
args.push(e);
}
- MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1);
+ MATCH m = callMatch(g, tg, null, ArgumentList(&args, names), 1);
if (m > MATCH.nomatch)
{
/* A variadic parameter list is less specialized than a
*/
extern (D) bool checkNRVO(FuncDeclaration fd)
{
+ //printf("checkNRVO*() %s\n", fd.ident.toChars());
if (!fd.isNRVO() || fd.returns is null)
return false;
All = 4, /// The least restrictive set of all other tables
}
+/// Specifies the mode for error printing
+enum ErrorPrintMode : ubyte
+{
+ simpleError, // Print errors without squiggles and carets
+ printErrorContext, // Print errors with context (source line and caret)
+}
+
extern(C++) struct Output
{
bool doOutput; // Output is enabled
bool complex = true; // identify complex/imaginary type usage
bool vin; // identify 'in' parameters
bool showGaggedErrors; // print gagged errors anyway
- bool printErrorContext; // print errors with the error context (the error line in the source file)
bool logo; // print compiler logo
bool color; // use ANSI colors in console output
bool cov; // generate code coverage data
+ ErrorPrintMode errorPrintMode; // enum for error printing mode
MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages
uint errorLimit = 20;
uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited)
All = 4, /// The least restrictive set of all other tables
};
+/// Specifies the mode for error printing
+enum class ErrorPrintMode : unsigned char
+{
+ simpleError, // Print errors without squiggles and carets
+ printErrorContext, // Print errors with the error line and caret
+};
+
struct Output
{
/// Configuration for the compiler generator
d_bool complex = true; // identify complex/imaginary type usage
d_bool vin; // identify 'in' parameters
d_bool showGaggedErrors; // print gagged errors anyway
- d_bool printErrorContext; // print errors with the error context (the error line in the source file)
d_bool logo; // print compiler logo
d_bool color; // use ANSI colors in console output
d_bool cov; // generate code coverage data
+ ErrorPrintMode errorPrintMode; // enum for error printing mode
MessageStyle messageStyle; // style of file/line annotations on messages
unsigned errorLimit;
unsigned errorSupplementLimit; // Limit the number of supplemental messages for each error (0 means unlimited)
// https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
// Implementation: https://github.com/dlang/dmd/pull/9817
FeatureState safer; // safer by default (more @safe checks in unattributed code)
- // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
+ // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
FeatureState noSharedAccess; // read/write access to shared memory objects
d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues
buf.writenl();
}
- void visitWhile(WhileStatement s)
+ void printConditionAssignment(Parameter p, Expression condition)
{
- buf.writestring("while (");
- if (auto p = s.param)
+ if (p)
{
// Print condition assignment
StorageClass stc = p.storageClass;
buf.writestring(p.ident.toString());
buf.writestring(" = ");
}
- s.condition.expressionToBuffer(buf, hgs);
+ condition.expressionToBuffer(buf, hgs);
+ }
+
+ void visitWhile(WhileStatement s)
+ {
+ buf.writestring("while (");
+ printConditionAssignment(s.param, s.condition);
buf.writeByte(')');
buf.writenl();
if (s._body)
void visitIf(IfStatement s)
{
buf.writestring("if (");
- if (Parameter p = s.prm)
- {
- StorageClass stc = p.storageClass;
- if (!p.type && !stc)
- stc = STC.auto_;
- if (stcToBuffer(buf, stc))
- buf.writeByte(' ');
- if (p.type)
- typeToBuffer(p.type, p.ident, buf, hgs);
- else
- buf.writestring(p.ident.toString());
- buf.writestring(" = ");
- }
- s.condition.expressionToBuffer(buf, hgs);
+ printConditionAssignment(s.prm, s.condition);
buf.writeByte(')');
buf.writenl();
if (s.ifbody.isScopeStatement())
void visitSwitch(SwitchStatement s)
{
buf.writestring(s.isFinal ? "final switch (" : "switch (");
- if (auto p = s.param)
- {
- // Print condition assignment
- StorageClass stc = p.storageClass;
- if (!p.type && !stc)
- stc = STC.auto_;
- if (stcToBuffer(buf, stc))
- buf.writeByte(' ');
- if (p.type)
- typeToBuffer(p.type, p.ident, buf, hgs);
- else
- buf.writestring(p.ident.toString());
- buf.writestring(" = ");
- }
- s.condition.expressionToBuffer(buf, hgs);
+ printConditionAssignment(s.param, s.condition);
buf.writeByte(')');
buf.writenl();
if (s._body)
void visitFuncDeclaration(FuncDeclaration f)
{
//printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
- if (stcToBuffer(buf, f.storage_class))
+
+ // 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(' ');
typeToBuffer(f.type, f.ident, buf, hgs);
auto tf = f.type.isTypeFunction();
buf.writestring(e.value.toChars());
}
+ if (e.rvalue)
+ buf.writestring("__rvalue(");
+
+ scope (exit)
+ if (e.rvalue)
+ buf.writeByte(')');
+
switch (e.op)
{
default:
case EXP.typeid_: return visitTypeid(e.isTypeidExp());
case EXP.traits: return visitTraits(e.isTraitsExp());
case EXP.halt: return visitHalt(e.isHaltExp());
- case EXP.is_: return visitIs(e.isExp());
+ case EXP.is_: return visitIs(e.isIsExp());
case EXP.comma: return visitComma(e.isCommaExp());
case EXP.mixin_: return visitMixin(e.isMixinExp());
case EXP.import_: return visitImport(e.isImportExp());
{ "getLocation" },
{ "hasPostblit" },
{ "hasCopyConstructor" },
+ { "hasMoveConstructor" },
{ "isCopyable" },
{ "toType" },
{ "parameters" },
* Params:
* prefix = first part of the identifier name.
* loc = source location to use in the identifier name.
+ * parent = (optional) extra part to be used in uniqueness check,
+ * if (prefix1, loc1) == (prefix2, loc2), but
+ * parent1 != parent2, no new name will be generated.
* Returns:
* Identifier (inside Identifier.idPool) with deterministic name based
* on the source location.
*/
- extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc)
+ extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc, string parent = "")
{
// generate `<prefix>_L<line>_C<col>`
OutBuffer idBuf;
* https://issues.dlang.org/show_bug.cgi?id=18880
* https://issues.dlang.org/show_bug.cgi?id=18868
* https://issues.dlang.org/show_bug.cgi?id=19058
+ *
+ * It is a bit trickier for lambdas/dgliterals: we want them to be unique per
+ * module/mixin + function/template instantiation context. So we use extra parent
+ * argument for that when dealing with lambdas. We could have added it to prefix
+ * directly, but that would unnecessary lengthen symbols names. See issue:
+ * https://issues.dlang.org/show_bug.cgi?id=23722
*/
- static struct Key { Loc loc; string prefix; }
+ static struct Key { Loc loc; string prefix; string parent; }
__gshared uint[Key] counters;
static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
{
// 2.082+
- counters.update(Key(loc, prefix),
+ counters.update(Key(loc, prefix, parent),
() => 1u, // insertion
(ref uint counter) // update
{
}
else
{
- const key = Key(loc, prefix);
+ const key = Key(loc, prefix, parent);
if (auto pCounter = key in counters)
{
idBuf.writestring("_");
continue;
}
- elems[fieldi] = doCopyOrMove(sc, ex);
+ elems[fieldi] = doCopyOrMove(sc, ex, null, false);
++fieldi;
}
if (errors)
import dmd.visitor;
version(Windows) {
- extern (C) char* getcwd(char* buffer, size_t maxlen);
+ extern (C) char* _getcwd(char* buffer, size_t maxlen);
+ alias getcwd = _getcwd;
} else {
import core.sys.posix.unistd : getcwd;
}
/*******************************
* For TypeFunction, nextOf() can return NULL if the function return
- * type is meant to be inferred, and semantic() hasn't yet ben run
+ * type is meant to be inferred, and semantic() hasn't yet been run
* on the function. After semantic(), it must no longer be NULL.
*/
override final Type nextOf() @safe
extern (D) this(Expressions* exps)
{
super(Ttuple);
- auto arguments = new Parameters(exps ? exps.length : 0);
- if (exps)
+ if (!exps)
{
- for (size_t i = 0; i < exps.length; i++)
- {
- Expression e = (*exps)[i];
- if (e.type.ty == Ttuple)
- error(e.loc, "cannot form sequence of sequences");
- auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null);
- (*arguments)[i] = arg;
- }
+ this.arguments = new Parameters(0);
+ return;
+ }
+ auto arguments = new Parameters(exps.length);
+
+ for (size_t i = 0; i < exps.length; i++)
+ {
+ Expression e = (*exps)[i];
+ assert(e.type.ty != Ttuple);
+ auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null);
+ (*arguments)[i] = arg;
}
this.arguments = arguments;
//printf("TypeTuple() %p, %s\n", this, toChars());
enum class TRUST : unsigned char
{
default_ = 0,
- system = 1, // @system (same as TRUSTdefault)
+ system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled)
trusted = 2, // @trusted
safe = 3 // @safe
};
return sel;
}
+ static const(char)[] toPascalCase(const(char)[] id) {
+ OutBuffer buf;
+ char firstChar = id[0];
+ if (firstChar >= 'a' && firstChar <= 'z')
+ firstChar = cast(char)(firstChar - 'a' + 'A');
+
+ buf.writeByte(firstChar);
+ buf.writestring(id[1..$]);
+ return cast(const(char)[])buf.extractSlice(false);
+ }
+
extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
{
OutBuffer buf;
auto ftype = cast(TypeFunction)fdecl.type;
const id = fdecl.ident.toString();
const nparams = ftype.parameterList.length;
+
// Special case: property setter
if (ftype.isProperty && nparams == 1)
{
- // rewrite "identifier" as "setIdentifier"
- char firstChar = id[0];
- if (firstChar >= 'a' && firstChar <= 'z')
- firstChar = cast(char)(firstChar - 'a' + 'A');
- buf.writestring("set");
- buf.writeByte(firstChar);
- buf.write(id[1 .. id.length - 1]);
+
+ // Special case: "isXYZ:"
+ if (id.length >= 2 && id[0..2] == "is")
+ {
+ buf.writestring("set");
+ buf.write(toPascalCase(id[2..$]));
+ }
+ else
+ {
+ buf.writestring("set");
+ buf.write(toPascalCase(id));
+ }
buf.writeByte(':');
goto Lcomplete;
}
+
// write identifier in selector
buf.write(id[]);
- // add mangled type and colon for each parameter
- if (nparams)
+
+ // To make it easier to match the selectors of objects nicely,
+ // the implementation has been replaced so that the parameter name followed by a colon
+ // is used instead.
+ // eg. void myFunction(int a, int b, int c) would be mangled to a selector as `myFunction:b:c:
+ if (nparams > 1)
{
- buf.writeByte('_');
- foreach (i, fparam; ftype.parameterList)
+ buf.writeByte(':');
+ foreach(i; 1..nparams)
{
- mangleToBuffer(fparam.type, buf);
+ buf.write(ftype.parameterList[i].ident.toString());
buf.writeByte(':');
}
}
+ else if (nparams == 1)
+ {
+ buf.writeByte(':');
+ }
Lcomplete:
buf.writeByte('\0');
+
// the slice is not expected to include a terminating 0
return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams);
}
return 0;
});
+
+ // Avoid attempting to generate selectors for template instances.
+ if (fd.parent && fd.parent.isTemplateInstance())
+ return;
+
+ // No selector declared, generate one.
+ if (fd._linkage == LINK.objc && !fd.objc.selector)
+ {
+ fd.objc.selector = ObjcSelector.create(fd);
+ }
}
override void validateSelector(FuncDeclaration fd)
{
const attrLoc = token.loc;
- nextToken();
-
- AST.Expression e = null; // default
- if (token.value == TOK.leftParenthesis)
- {
- nextToken();
- e = parseAssignExp();
- check(TOK.rightParenthesis);
- }
+ AST.Expression e = parseAlign();
if (pAttrs.setAlignment)
{
if (e)
error("redundant alignment attribute `align(%s)`", e.toChars());
else
- error("redundant alignment attribute `align`");
+ error("redundant alignment attribute `align(default)`");
}
pAttrs.setAlignment = true;
}
case TOK.align_:
{
- nextToken();
setAlignment = true;
- if (token.value == TOK.leftParenthesis)
- {
- nextToken();
- ealign = parseExpression();
- check(TOK.rightParenthesis);
- }
+ ealign = parseAlign();
continue;
}
default:
}
}
+ /**
+ * Parse `align` or `align(n)`
+ * Returns:
+ * expression `n` if it is present, or `null` otherwise.
+ */
+ private AST.Expression parseAlign()
+ {
+ assert(token.value == TOK.align_);
+ AST.Expression e = null;
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ if (token.value == TOK.default_)
+ nextToken();
+ else
+ e = parseAssignExp();
+ check(TOK.rightParenthesis);
+ }
+ return e;
+ }
/**********************************
* Parse Declarations.
* These can be:
error("missing `do { ... }` after `in` or `out`");
const returnloc = token.loc;
nextToken();
- f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
+ if (f.isCtorDeclaration)
+ f.fbody = new AST.ExpStatement(returnloc, parseExpression());
+ else
+ f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
f.endloc = token.loc;
check(TOK.semicolon);
break;
case TOK.moduleString:
case TOK.functionString:
case TOK.prettyFunction:
+ case TOK.rvalue:
Lexp:
{
AST.Expression exp = parseExpression();
e = new AST.TypeidExp(loc, o);
break;
}
+ case TOK.rvalue:
+ {
+ nextToken();
+ check(TOK.leftParenthesis, "`__rvalue`");
+ e = parseAssignExp();
+ e.rvalue = true;
+ check(TOK.rightParenthesis);
+ break;
+ }
case TOK.traits:
{
/* __traits(identifier, args...)
printf("%.*s %s\n", cast(int)s.length, s.ptr, e.type ? e.type.toChars() : "");
}
+ override void visit(IdentifierExp e)
+ {
+ printIndent(indent);
+ printf("Identifier `%s` %s\n", e.ident.toChars(), e.type ? e.type.toChars() : "");
+ }
+
override void visit(IntegerExp e)
{
printIndent(indent);
extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc;
extern (Windows) void SetLastError(DWORD) nothrow @nogc;
- extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow;
+ extern (C) char* _getcwd(char* buffer, size_t maxlen) nothrow;
+ alias getcwd = _getcwd;
}
version (CRuntime_Glibc)
AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value,
// do not set wasRead for it
+ StructDeclaration *argStruct; // elimiate recursion when looking for rvalue construction
Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all);
};
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
if (!funcdecl.isNRVO())
- exp = doCopyOrMove(sc2, exp, f.next);
+ exp = doCopyOrMove(sc2, exp, f.next, true, true);
if (tret.hasPointers())
checkReturnEscape(*sc2, exp, false);
/* https://dlang.org/spec/statement.html#return-statement
*/
- //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
+ //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs));
FuncDeclaration fd = sc.parent.isFuncDeclaration();
if (fd.fes)
enum LOG_LEASTAS = 0;
static if (LOG_LEASTAS)
{
- printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars());
+ printf("%s.leastAsSpecialized(%s)\n", td.toChars(), td2.toChars());
}
/* This works by taking the template parameters to this template
{
version (none)
{
- printf("functionResolve() dstart = %s\n", dstart.toChars());
- printf(" tiargs:\n");
- if (tiargs)
+ printf("functionResolve() dstart: %s (", dstart.toChars());
+ for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
{
- for (size_t i = 0; i < tiargs.length; i++)
- {
- RootObject arg = (*tiargs)[i];
- printf("\t%s\n", arg.toChars());
- }
+ if (i) printf(", ");
+ RootObject arg = (*tiargs)[i];
+ printf("%s", arg.toChars());
}
- printf(" fargs:\n");
- for (size_t i = 0; i < (fargs ? fargs.length : 0); i++)
+ printf(")(");
+ for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
{
- Expression arg = (*fargs)[i];
- printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
+ 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);
}
tf.mod = tthis_fd.mod;
}
const(char)* failMessage;
- MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc);
+ MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, errorHelper, sc);
//printf("test1: mfa = %d\n", mfa);
if (failMessage)
errorHelper(failMessage);
Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
auto tf = fd.type.isTypeFunction();
- MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc);
+ MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, null, sc);
if (mfa < m.last)
return 0;
// Disambiguate by tf.callMatch
auto tf1 = fd.type.isTypeFunction();
auto tf2 = m.lastf.type.isTypeFunction();
- MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc);
- MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc);
+ MATCH c1 = callMatch(fd, tf1, tthis_fd, argumentList, 0, null, sc);
+ MATCH c2 = callMatch(m.lastf, tf2, tthis_best, argumentList, 0, null, sc);
//printf("2: c1 = %d, c2 = %d\n", c1, c2);
if (c1 > c2) goto Ltd;
if (c1 < c2) goto Ltd_best;
if (m.lastf.type.ty == Terror)
goto Lerror;
auto tf = m.lastf.type.isTypeFunction();
- if (!tf.callMatch(tthis_best, argumentList, 0, null, sc))
+ if (callMatch(m.lastf, tf, tthis_best, argumentList, 0, null, sc) == MATCH.nomatch)
goto Lnomatch;
/* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
{
reserved,
+ // if this list changes, update
+ // tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match
+
// Other
leftParenthesis,
rightParenthesis,
wchar_tLiteral,
endOfLine, // \n, \r, \u2028, \u2029
whitespace,
+ rvalue,
// C only keywords
inline,
interval,
loweredAssignExp,
+ rvalue,
}
enum FirstCKeyword = TOK.inline;
TOK.prettyFunction,
TOK.shared_,
TOK.immutable_,
+ TOK.rvalue,
// C only keywords
TOK.inline,
TOK.pragma_: "pragma",
TOK.typeof_: "typeof",
TOK.typeid_: "typeid",
+ TOK.rvalue: "__rvalue",
TOK.template_: "template",
TOK.void_: "void",
TOK.int8: "byte",
wchar_tLiteral,
endOfLine, // \n, \r, \u2028, \u2029
whitespace,
+ rvalue,
// C only keywords
inline_,
}
return True();
}
- if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
+ if (e.ident == Id.hasCopyConstructor ||
+ e.ident == Id.hasMoveConstructor ||
+ e.ident == Id.hasPostblit)
{
if (dim != 1)
return dimError(1);
auto ts = tb.isTypeStruct();
if (auto sd = ts ? ts.sym : null)
{
- return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
- : (sd.hasCopyCtor ? True() : False());
+ bool result;
+ if (e.ident == Id.hasPostblit)
+ result = sd.postblit !is null;
+ else if (e.ident == Id. hasCopyConstructor)
+ result = sd.hasCopyCtor;
+ else
+ result = sd.hasMoveCtor;
+ return result ? True() : False();
}
return False();
}
* 'args' are being matched to function type 'tf'
* Determine match level.
* Params:
+ * fd = function being called, if a symbol
* tf = function type
* tthis = type of `this` pointer, null if not member function
* argumentList = arguments to function call
* Returns:
* MATCHxxxx
*/
-extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null)
+extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, ArgumentList argumentList,
+ int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null)
{
- //printf("TypeFunction::callMatch() %s\n", tf.toChars());
+ //printf("callMatch() fd: %s, tf: %s\n", fd ? fd.ident.toChars() : "null", toChars(tf));
MATCH match = MATCH.exact; // assume exact match
ubyte wildmatch = 0;
Expression arg = args[u];
if (!arg)
continue; // default argument
- m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage);
+ m = argumentMatchParameter(fd, tf, p, arg, wildmatch, flag, sc, pMessage);
if (failMessage)
{
buf.reset();
*
* This is done by seeing if a call to the copy constructor can be made:
* ```
- * typeof(tprm) __copytmp;
- * copytmp.__copyCtor(arg);
+ * typeof(tprm) __copytemp;
+ * copytemp.__copyCtor(arg);
* ```
*/
private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
Expression arg, Type tprm, Scope* sc, const(char)** pMessage)
{
- auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null);
+ //printf("isCopyConstructorCallable() argStruct: %s arg: %s tprm: %s\n", argStruct.toChars(), toChars(arg), toChars(tprm));
+ auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytemp"), null);
tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe;
tmp.dsymbolSemantic(sc);
Expression ve = new VarExp(arg.loc, tmp);
*
* This function is called by `TypeFunction.callMatch` while iterating over
* the list of parameter. Here we check if `arg` is a match for `p`,
- * which is mostly about checking if `arg.type` converts to `p`'s type
+ * which is mostly about checking if `arg.type` converts to type of `p`
* and some check about value reference.
*
* Params:
+ * fd = the function being called if symbol, null if not
* tf = The `TypeFunction`, only used for error reporting
* p = The parameter of `tf` being matched
* arg = Argument being passed (bound) to `p`
* wildmatch = Wild (`inout`) matching level, derived from the full argument list
- * flag = A non-zero value means we're doing a partial ordering check
+ * flag = A non-zero value means we are doing a partial ordering check
* (no value semantic check)
* sc = Scope we are in
* pMessage = A buffer to write the error in, or `null`
*
* Returns: Whether `trailingArgs` match `p`.
*/
-private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
+private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction tf, Parameter p,
Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage)
{
- //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars());
+ static if (0)
+ printf("argumentMatchParameter() sc: %p, fd: %s, tf: %s, p: %s, arg: %s, arg.type: %s\n",
+ sc, fd ? fd.ident.toChars() : "null", tf.toChars(), parameterToChars(p, tf, false), arg.toChars(), arg.type.toChars());
MATCH m;
Type targ = arg.type;
Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
else
{
const isRef = p.isReference();
- StructDeclaration argStruct, prmStruct;
- // first look for a copy constructor
- if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct)
+ StructDeclaration argStruct, prmStruct;
+ if (targ.ty == Tstruct && tprm.ty == Tstruct)
{
// if the argument and the parameter are of the same unqualified struct type
argStruct = (cast(TypeStruct)targ).sym;
prmStruct = (cast(TypeStruct)tprm).sym;
+
+ /* if both a copy constructor and move constructor exist, then match
+ * the lvalue to the copy constructor only and the rvalue to the move constructor
+ * only
+ */
+ if (argStruct == prmStruct && fd)
+ {
+ if (auto cfd = fd.isCtorDeclaration())
+ {
+ /* Get struct that constructor is making
+ */
+
+ auto t1 = cfd.type.toBasetype();
+ auto t2 = t1.nextOf();
+ auto t3 = t2.isTypeStruct();
+ if (t3)
+ {
+ auto ctorStruct = t3.sym;
+// StructDeclaration ctorStruct = cfd.type.toBasetype().nextOf().isTypeStruct().sym;
+
+ if (prmStruct == ctorStruct && ctorStruct.hasCopyCtor && ctorStruct.hasMoveCtor)
+ {
+ if (cfd.isCpCtor && !arg.isLvalue())
+ return MATCH.nomatch; // copy constructor is only for lvalues
+ else if (cfd.isMoveCtor && arg.isLvalue())
+ return MATCH.nomatch; // move constructor is only for rvalues
+ }
+ }
+ }
+ }
}
// check if the copy constructor may be called to copy the argument
- if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+ if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
{
if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
return MATCH.nomatch;
*/
Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag)
{
+ enum LOGDOTEXP = false;
+ if (LOGDOTEXP)
+ printf("dotExp()\n");
+
Expression visitType(Type mt)
{
VarDeclaration v = null;
return noMember(mt, sc, e, ident, flag);
}
// check before alias resolution; the alias itself might be deprecated!
- if (s.isAliasDeclaration)
+ if (auto ad = s.isAliasDeclaration)
+ {
s.checkDeprecated(e.loc, sc);
+
+ // Fix for https://github.com/dlang/dmd/issues/20610
+ if (ad.originalType)
+ {
+ if (auto tid = ad.originalType.isTypeIdentifier())
+ {
+ if (tid.idents.length)
+ {
+ static if (0)
+ {
+ printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ printf("AliasDeclaration: %s\n", ad.toChars());
+ if (ad.aliassym)
+ printf("aliassym: %s\n", ad.aliassym.toChars());
+ printf("tid type: %s\n", toChars(tid));
+ }
+ /* Rewrite e.s as e.(tid.ident).(tid.idents)
+ */
+ Expression die = new DotIdExp(e.loc, e, tid.ident);
+ foreach (id; tid.idents) // maybe use typeToExpressionHelper()
+ die = new DotIdExp(e.loc, die, cast(Identifier)id);
+ /* Ambiguous syntax, only way to disambiguate it to try it
+ */
+ die = dmd.expressionsem.trySemantic(die, sc);
+ if (die && die.isDotVarExp()) // shrink wrap around DotVarExp()
+ {
+ return die;
+ }
+ }
+ }
+ }
+ }
s = s.toAlias();
if (auto em = s.isEnumMember())
Dsymbol visitIdentifier(TypeIdentifier type)
{
- //printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
+ //printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type));
if (!sc)
return null;
s = t.toDsymbol(sc);
if (e)
s = getDsymbol(e);
-
return s;
}
--- /dev/null
+// { dg-do compile }
+// { dg-options "-fno-moduleinfo -fdump-tree-original" }
+struct S
+{
+ int i;
+ this(ref S);
+ void opAssign(S s)
+ {
+ i = s.i + 1;
+ }
+}
+
+void test_opAssign()
+{
+ S s;
+ S t;
+ // { dg-final { scan-tree-dump-not "&TARGET_EXPR" "original" } }
+ t = s;
+}
--- /dev/null
+
+struct S
+{
+ align(1)
+ {
+ short x1;
+ int y1;
+ long z1;
+
+ align(default)
+ {
+ short x;
+ int y;
+ long z;
+ }
+ }
+}
+
+void fun()
+{
+ static assert(S.x1.alignof == 1);
+ static assert(S.y1.alignof == 1);
+ static assert(S.z1.alignof == 1);
+
+ static assert(S.x.alignof == short.alignof);
+ static assert(S.y.alignof == int.alignof);
+ static assert(S.z.alignof == long.alignof);
+}
struct SafeS
{
+ this(int[1] x) scope {}
+ this(int[2] x) return scope {}
+ this(int[3] x) scope return {}
+ this(int[4] x) return {}
+
@safe:
ref SafeS foo() return
{
--- /dev/null
+/* PERMUTE_ARGS: -preview=rvaluerefparam
+ */
+
+struct S
+{
+ alias TT this;
+ long TT();
+ this(T)(int x) {} // works if `int` is `long`
+
+ this(S);
+ this(ref S);
+
+ ~this();
+}
+
+S fun(ref S arg);
+
+void test()
+{
+ S st;
+ fun(st);
+}
// or normal method defintions
bool isNull() => this is null;
+
+ this() {}
+ this(int x) { _x = x; }
+ this(float y) => this(cast(int) y);
}
class B : A{
// Issue 24088 - https://issues.dlang.org/show_bug.cgi?id=24088
S!int f() => S!int();
}
+
+struct T
+{
+ void inc() {}
+ this(this) => inc();
+
+ void free() {}
+ ~this() => free();
+}
// https://issues.dlang.org/show_bug.cgi?id=19227
/* TEST_OUTPUT:
---
-compilable/test19227.d(18): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
-Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
-Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/test19227.d(16): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
---
*/
struct NoCpCtor { }
class C19902 { }
+struct MoveCtor { this(MoveCtor) { } }
+
static assert(__traits(hasCopyConstructor, S));
static assert(__traits(hasCopyConstructor, OuterS.S));
static assert(__traits(hasCopyConstructor, OuterS));
static assert(!__traits(hasPostblit, U!S));
static assert(__traits(hasPostblit, SPostblit));
static assert(!__traits(hasCopyConstructor, SPostblit));
+static assert(__traits(hasMoveConstructor, MoveCtor));
+static assert(!__traits(hasMoveConstructor, NoCpCtor));
static assert(!__traits(hasCopyConstructor, NoCpCtor));
static assert(!__traits(hasCopyConstructor, C19902));
+++ /dev/null
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail19871.d(10): Error: `struct Struct` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail19871.d(19): rvalue constructor defined here
-fail_compilation/fail19871.d(13): copy constructor defined here
----
-*/
-
-struct Struct
-{
- @disable this();
- this(ref Struct other)
- {
- const Struct s = void;
- this(s);
- }
-
- this(Struct) {}
-}
+++ /dev/null
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail19931.d(10): Error: `struct S` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail19931.d(12): rvalue constructor defined here
-fail_compilation/fail19931.d(13): copy constructor defined here
----
-*/
-
-struct S
-{
- this(S s) {}
- this(ref S s) {}
- this(this) {}
-}
+++ /dev/null
-// https://issues.dlang.org/show_bug.cgi?id=23036
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail23036.d(12): Error: `struct S` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail23036.d(15): rvalue constructor defined here
-fail_compilation/fail23036.d(14): copy constructor defined here
----
-*/
-
-struct S
-{
- this(ref S) {}
- this(S, int a = 2) {}
-}
-
-void main()
-{
- S a;
- S b = a;
-}
/*
TEST_OUTPUT:
---
-fail_compilation/ice14642.d(47): Error: undefined identifier `errorValue`
-fail_compilation/ice14642.d(23): Error: template instance `ice14642.X.NA!()` error instantiating
+fail_compilation/ice14642.d(48): Error: undefined identifier `errorValue`
+fail_compilation/ice14642.d(52): error on member `ice14642.Z.v`
+fail_compilation/ice14642.d(24): Error: template instance `ice14642.X.NA!()` error instantiating
---
*/
EXTRA_FILES: imports/ice9865b.d
TEST_OUTPUT:
---
-fail_compilation/ice9865.d(9): Error: alias `ice9865.Baz` recursive alias declaration
+fail_compilation/ice9865.d(10): Error: alias `ice9865.Baz` recursive alias declaration
+fail_compilation/ice9865.d(11): error on member `ice9865.Foo.f`
---
*/
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align`
+fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align(default)`
fail_compilation/parseStc2.d(64): Error: redundant alignment attribute `align(1)`
fail_compilation/parseStc2.d(65): Error: redundant alignment attribute `align(1)`
-fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align`
+fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align(default)`
fail_compilation/parseStc2.d(67): Error: redundant alignment attribute `align(2)`
---
*/
--- /dev/null
+/*
+TEST_OUTPUT:
+---
+fail_compilation/short_fn.d(13): Error: can only return void expression, `this` call or `super` call from constructor
+fail_compilation/short_fn.d(14): Error: can only return void expression, `this` call or `super` call from constructor
+---
+*/
+
+struct Number
+{
+ int x;
+
+ this(int x) => this.x = x;
+ this(byte x) => Number();
+}
/* TEST_OUTPUT:
---
-fail_compilation/test19473.d(14): Error: union `test19473.P` no size because of forward reference
+fail_compilation/test19473.d(15): Error: union `test19473.P` no size because of forward reference
+fail_compilation/test19473.d(31): error on member `test19473.P.p`
---
*/
/* TEST_OUTPUT:
---
-fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference
-fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid
-fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
+fail_compilation/test20719.d(14): Error: struct `test20719.SumType` no size because of forward reference
+fail_compilation/test20719.d(17): error on member `test20719.SumType.storage`
+fail_compilation/test20719.d(33): Error: variable `test20719.isCopyable!(SumType).__lambda_L33_C22.foo` - size of type `SumType` is invalid
+fail_compilation/test20719.d(19): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
---
*/
struct SumType
alias Qual = T;
}
-void test()
+void test1()
{
int x = 3;
int y = 4;
assert(XY[1] == 4);
}
-void main()
+/**********************************************/
+
+struct T
+{
+ int k,i = 2;
+}
+
+struct S
+{
+ int x;
+ T t;
+ alias ti = t.i;
+}
+
+void test2()
+{
+ T t = T(1, 2);
+ S s;
+ assert(s.ti == 2);
+}
+
+/**********************************************/
+
+int main()
{
- test();
+ test1();
+ test2();
+
+ return 0;
}
--- /dev/null
+module imports.test23722_2b;
+
+struct T(alias fun) { }
+
+struct S(int i) {
+ auto t = T!(x => i)();
+}
+
+string g() {
+ S!0 s0;
+ S!1 s1;
+ return s1.t.init.mangleof;
+}
while(--i > 0);
}
+////////////////////////////////////////////////////////////////////////
+// https://github.com/dlang/dmd/issues/20574
+
+int test20574x(int i, int y)
+{
+ return i ? y : y;
+}
+
+void test20574()
+{
+ assert(test20574x(1, 2) == 2);
+ assert(test20574x(0, 2) == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+struct S8
+{
+ int x,y,z;
+}
+
+int test8x(S8 s)
+{
+ s = s;
+ return s.y;
+}
+
+void test8()
+{
+ S8 s;
+ s.y = 2;
+ assert(test8x(s) == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+struct S9
+{
+ int a,b;
+ ~this() { }
+}
+
+
+S9 test9x(ref S9 arg)
+{
+ return arg;
+}
+
+void test9()
+{
+ S9 s;
+ s.b = 3;
+ S9 t = test9x(s);
+ assert(t.b == 3);
+}
+
////////////////////////////////////////////////////////////////////////
int main()
test21816();
test21835();
testDoWhileContinue();
+ test20574();
+ test8();
+ test9();
printf("Success\n");
return 0;
assert(&r == s1ptr);
}
+/***************************************************/
+// https://github.com/dlang/dmd/issues/20567
+
+struct S2
+{
+ int x;
+ this(ref S2 s) { x = s.x; }
+}
+
+S2 returnRval(ref S2 arg1, ref S2 arg2, int i)
+{
+ return i ? arg1 : arg2;
+}
+
+void test2()
+{
+ S2 s1, s2;
+ s1.x = 3;
+ s2.x = 4;
+ S2 s = returnRval(s1, s2, 0);
+ assert(s.x == 4);
+ s = returnRval(s1, s2, 1);
+ assert(s.x == 3);
+}
+
/***************************************************/
void main()
{
test1();
+ test2();
}
--- /dev/null
+/* PERMUTE_ARGS: -preview=rvaluerefparam
+/* testing __rvalue */
+
+import core.stdc.stdio;
+
+/********************************/
+
+int foo(int) { printf("foo(int)\n"); return 1; }
+int foo(ref int) { printf("foo(ref int)\n"); return 2; }
+
+void test1()
+{
+ int s;
+ assert(foo(s) == 2);
+ assert(foo(__rvalue(s)) == 1);
+}
+
+/********************************/
+
+struct S
+{
+ nothrow:
+ ~this() { printf("~this() %p\n", &this); }
+ this(ref S) { printf("this(ref S)\n"); }
+ void opAssign(S) { printf("opAssign(S)\n"); }
+}
+
+void test2()
+{
+ S s;
+ S t;
+
+ t = __rvalue(s);
+}
+
+/********************************/
+
+struct S3
+{
+ int a, b, c;
+
+ this(S3) {}
+ this(ref S3) {}
+}
+
+void test3()
+{
+ S3 s;
+ S3 x = s; // this line causes the compiler to crash
+}
+
+/********************************/
+
+struct S4
+{
+ void* p;
+
+ this(ref S4) { }
+
+ this(S4 s)
+ {
+ assert(&s is &x); // confirm the rvalue reference
+ }
+}
+
+__gshared S4 x;
+
+void test4()
+{
+ S4 t = __rvalue(x);
+}
+
+/********************************/
+
+struct S5
+{
+ this(S5 s) { printf("this(S5 s)\n"); }
+ this(ref inout S5 s) inout { printf("this(ref inout S5 s) inout\n"); }
+}
+
+void test5()
+{
+ S5 t;
+ S5 t1 = t;
+ S5 t2 = __rvalue(t);
+}
+
+/********************************/
+
+int moveCtor, copyCtor, moveAss, copyAss;
+
+struct S6
+{
+ this(S6 s) { ++moveCtor; }
+ this(ref S6 s) { ++copyCtor; }
+ void opAssign(S6 s) { ++moveAss; }
+ void opAssign(ref S6 s) { ++copyAss; }
+}
+
+void test6()
+{
+ S6 x;
+ S6 s = x;
+// printf("S6 s = x; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+ assert(copyCtor == 1);
+ S6 s2 = __rvalue(x);
+// printf("S6 s2 = __rvalue(x); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+ assert(moveCtor == 1);
+ s2 = s;
+// printf("s2 = s; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+ assert(copyAss == 1);
+ s2 = __rvalue(s);
+// printf("s2 = __rvalue(s); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+ assert(moveAss == 1);
+ assert(copyCtor == 1 && moveCtor == 1 && copyAss == 1 && moveAss == 1);
+}
+
+/********************************/
+// https://github.com/dlang/dmd/pull/17050#issuecomment-2543370370
+
+struct MutableString(size_t E) {}
+
+struct StringTest
+{
+ alias toString this;
+ const(char)[] toString() const pure
+ => null;
+
+ this(StringTest rhs) {}
+ this(ref inout StringTest rhs) inout pure {}
+
+ this(typeof(null)) inout pure {}
+ this(size_t Embed)(MutableString!Embed str) inout {}
+
+ ~this() {}
+}
+
+
+void test7()
+{
+ StringTest s = StringTest(null);
+}
+
+/********************************/
+// https://github.com/dlang/dmd/issues/20562
+
+struct S8
+{
+ int a,b;
+ this(S8) { printf("this(S)\n"); b = 4; }
+ this(ref S8) { printf("this(ref S)\n"); assert(0); }
+}
+
+
+S8 returnRval(ref S8 arg)
+{
+static if (0)
+{
+ /* __copytmp2 = 0 ;
+ _D7rvalue51S6__ctorMFNcKSQxQrZQg call (arg param #__copytmp2);
+ * __HID1 streq 1 __copytmp2;
+ __HID1;
+ */
+ return arg;
+}
+else static if (1)
+{
+ /* * __HID1 streq 1 * arg;
+ __HID1;
+ */
+ return __rvalue(arg); // should move-construct the NRVO value
+}
+else
+{
+ /* * t = 0 ;
+ t;
+ _TMP0 = t;
+ _D7rvalue51S6__ctorMFNcSQwQqZQg call (arg param _TMP0);
+ t;
+ */
+ S8 t = __rvalue(arg);
+ return t;
+}
+}
+
+
+void test8()
+{
+ S8 s;
+ S8 t = returnRval(s);
+ printf("t.b: %d\n", t.b);
+ assert(t.b == 4);
+}
+
+/********************************/
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+
+ return 0;
+}
--- /dev/null
+// https://issues.dlang.org/show_bug.cgi?id=23036
+
+struct S
+{
+ this(ref S) {}
+ this(S, int a = 2) {}
+}
+
+void main()
+{
+ S a;
+ S b = a;
+}
--- /dev/null
+// COMPILE_SEPARATELY:
+// EXTRA_SOURCES: imports/test23722_2b.d
+// https://issues.dlang.org/show_bug.cgi?id=23722
+// Lambdas are mangled incorrectly when using multiple compilation units, resulting in incorrect code
+import imports.test23722_2b;
+
+void main() {
+ S!1 s1;
+ assert(s1.t.init.mangleof == g);
+}
-82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
+c57da0cf5945cfb45eed06f1fd820435cda3ee3a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
return val;
}
+ /// Execute target dependent trap instruction, if supported.
+ /// Otherwise, abort execution.
pragma(inline, true)
void trap()
{
}
}
-/// Provide static branch hints
+/// Provide static branch and value hints for the LDC/GDC compilers.
+/// DMD ignores these hints.
pragma(inline, true) bool likely(bool b) { return !!expect(b, true); }
-///
+/// ditto
pragma(inline, true) bool unlikely(bool b) { return !!expect(b, false); }
* GC.stats().allocatedInCurrentThread, but faster.
*/
ulong allocatedInCurrentThread() nothrow;
+
+ // ARRAY FUNCTIONS
+ /**
+ * Get the current used capacity of an array block. Note that this is only
+ * needed if you are about to change the array used size and need to deal
+ * with the memory that is about to go away. For appending or shrinking
+ * arrays that have no destructors, you probably don't need this function.
+ * Params:
+ * ptr = The pointer to check. This can be an interior pointer, but if it
+ * is beyond the end of the used space, the return value may not be
+ * valid.
+ * atomic = If true, the value is fetched atomically (for shared arrays)
+ * Returns: Current array slice, or null if the pointer does not point to a
+ * valid appendable GC block.
+ */
+ void[] getArrayUsed(void *ptr, bool atomic = false) nothrow;
+
+ /**
+ * Expand the array used size. Used for appending and expanding the length
+ * of the array slice. If the operation can be performed without
+ * reallocating, the function succeeds. Newly expanded data is not
+ * initialized.
+ *
+ * slices that do not point at expandable GC blocks cannot be affected, and
+ * this function will always return false.
+ * Params:
+ * slice = the slice to attempt expanding in place.
+ * newUsed = the size that should be stored as used.
+ * atomic = if true, the array may be shared between threads, and this
+ * operation should be done atomically.
+ * Returns: true if successful.
+ */
+ bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe;
+
+ /**
+ * Expand the array capacity. Used for reserving space that can be used for
+ * appending. If the operation can be performed without reallocating, the
+ * function succeeds. The used size is not changed.
+ *
+ * slices that do not point at expandable GC blocks cannot be affected, and
+ * this function will always return zero.
+ * Params:
+ * slice = the slice to attempt reserving capacity for.
+ * request = the requested size to expand to. Includes the existing data.
+ * Passing a value less than the current array size will result in no
+ * changes, but will return the current capacity.
+ * atomic = if true, the array may be shared between threads, and this
+ * operation should be done atomically.
+ * Returns: resulting capacity size, 0 if the operation could not be performed.
+ */
+ size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe;
+
+ /**
+ * Shrink used space of a slice. Unlike the other array functions, the
+ * array slice passed in is the target slice, and the existing used space
+ * is passed separately. This is to discourage code that ends up with a
+ * slice to dangling valid data.
+ * If slice.ptr[0 .. existingUsed] does not point to the end of a valid GC
+ * appendable slice, then the operation fails.
+ * Params:
+ * slice = The proposed valid slice data.
+ * existingUsed = The amount of data in the block (starting at slice.ptr)
+ * that is currently valid in the array. If this amount does not match
+ * the current used size, the operation fails.
+ * atomic = If true, the slice may be shared between threads, and the
+ * operation should be atomic.
+ * Returns: true if successful.
+ */
+ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow;
}
bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0)
{
import core.lifetime : TypeInfoSize;
- import core.internal.gc.blockmeta : __setArrayAllocLengthImpl;
- return __setArrayAllocLengthImpl(info, newLength, isShared, typeid(T), oldLength, TypeInfoSize!T);
+ import core.internal.gc.blockmeta : __setArrayAllocLengthImpl, __setBlockFinalizerInfo;
+ static if (TypeInfoSize!T)
+ __setBlockFinalizerInfo(info, typeid(T));
+ return __setArrayAllocLengthImpl(info, newLength, isShared, oldLength, TypeInfoSize!T);
}
int __nextBlkIdx;
}
-@property BlkInfo *__blkcache() nothrow
+@property BlkInfo *__blkcache() nothrow @nogc
{
if (!__blkcache_storage)
{
so any use of the returned BlkInfo should copy it and then check the
base ptr of the copy before actually using it.
*/
-BlkInfo *__getBlkInfo(void *interior) nothrow
+BlkInfo *__getBlkInfo(void *interior) nothrow @nogc
{
BlkInfo *ptr = __blkcache;
if (ptr is null)
return null; // not in cache.
}
-void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
+void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow @nogc
{
auto cache = __blkcache;
if (cache is null)
}
}
}
+
+debug(PRINTF)
+{
+ extern(C) void printArrayCache()
+ {
+ auto ptr = __blkcache;
+ printf("CACHE: \n");
+ foreach (i; 0 .. N_CACHE_BLOCKS)
+ {
+ printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
+ }
+ }
+}
*/
bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow
{
- size_t typeInfoSize = structTypeInfoSize(tinext);
- return __setArrayAllocLengthImpl(info, newlength, isshared, tinext, oldlength, typeInfoSize);
+ size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+ if (typeInfoSize)
+ __setBlockFinalizerInfo(info, tinext);
+
+ return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize);
}
// the impl function, used both above and in core.internal.array.utils
-bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength, size_t typeInfoSize) pure nothrow
+bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow
{
import core.atomic;
// setting the initial length, no cas needed
*length = cast(ubyte)newlength;
}
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast() tinext;
- }
}
else if (info.size < PAGESIZE)
{
// setting the initial length, no cas needed
*length = cast(ushort)newlength;
}
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
- *typeInfo = cast() tinext;
- }
}
else
{
// setting the initial length, no cas needed
*length = newlength;
}
- if (typeInfoSize)
- {
- auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
- *typeInfo = cast()tinext;
- }
}
return true; // resize succeeded
}
/**
- get the allocation size of the array for the given block (without padding or type info)
+ The block finalizer info is set separately from the array length, as that is
+ only needed on the initial setup of the block. No shared is needed, since
+ this should only happen when the block is new.
+ */
+void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow
+{
+ if ((info.attr & BlkAttr.APPENDABLE) && info.size >= PAGESIZE)
+ {
+ // array used size goes at the beginning. We can stuff the typeinfo
+ // right after it, as we need to use 16 bytes anyway.
+ auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
+ *typeInfo = cast() ti;
+ }
+ else
+ {
+ // all other cases the typeinfo gets put at the end of the block
+ auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
+ *typeInfo = cast() ti;
+ }
+}
+
+/**
+ get the used size of the array for the given block
*/
-size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
+size_t __arrayAllocLength(ref BlkInfo info) pure nothrow
+ in(info.attr & BlkAttr.APPENDABLE)
{
+ auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
if (info.size <= 256)
- return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
+ return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
if (info.size < PAGESIZE)
- return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD);
+ return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
return *cast(size_t *)(info.base);
}
+/**
+ Atomically get the used size of the array for the given block
+ */
+size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow
+ in(info.attr & BlkAttr.APPENDABLE)
+{
+ import core.atomic;
+ auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+ if (info.size <= 256)
+ return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD));
+
+ if (info.size < PAGESIZE)
+ return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD));
+
+ return atomicLoad(*cast(shared(size_t)*)(info.base));
+}
+
+/**
+ Get the maximum bytes that can be stored in the given block.
+ */
+size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow
+ in(info.attr & BlkAttr.APPENDABLE)
+{
+ // Capacity is a calculation based solely on the block info.
+ if (info.size >= PAGESIZE)
+ return info.size - LARGEPAD;
+
+ auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+ auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD;
+ return info.size - typeInfoSize - padsize;
+}
+
/**
get the padding required to allocate size bytes. Note that the padding is
NOT included in the passed in size. Therefore, do NOT call this function
stats.freeSize += freeListSize;
stats.allocatedInCurrentThread = bytesAllocated;
}
+
+ // ARRAY FUNCTIONS
+ void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+ {
+ import core.internal.gc.blockmeta;
+ import core.internal.gc.blkcache;
+ import core.internal.array.utils;
+
+ // lookup the block info, using the cache if possible.
+ auto bic = atomic ? null : __getBlkInfo(ptr);
+ auto info = bic ? *bic : query(ptr);
+
+ if (!(info.attr & BlkAttr.APPENDABLE))
+ // not appendable
+ return null;
+
+ assert(info.base); // sanity check.
+ if (!bic && !atomic)
+ // cache the lookup for next time
+ __insertBlkInfoCache(info, null);
+
+ auto usedSize = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info);
+ return __arrayStart(info)[0 .. usedSize];
+ }
+
+ /* NOTE about @trusted in these functions:
+ * These functions do a lot of pointer manipulation, and has writeable
+ * access to BlkInfo which is used to interface with other parts of the GC,
+ * including the block metadata and block cache. Marking these functions as
+ * @safe would mean that any modification of BlkInfo fields should be
+ * considered @safe, which is not the case. For example, it would be
+ * perfectly legal to change the BlkInfo size to some huge number, and then
+ * store it in the block cache to blow up later. The utility functions
+ * count on the BlkInfo representing the correct information inside the GC.
+ *
+ * In order to mark these @safe, we would need a BlkInfo that has
+ * restrictive access (i.e. @system only) to the information inside the
+ * BlkInfo. Until then any use of these structures needs to be @trusted,
+ * and therefore the entire functions are @trusted. The API is still @safe
+ * because the information is stored and looked up by the GC, not the
+ * caller.
+ */
+ bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @trusted
+ {
+ import core.internal.gc.blockmeta;
+ import core.internal.gc.blkcache;
+ import core.internal.array.utils;
+
+ if (newUsed < slice.length)
+ // cannot "expand" by shrinking.
+ return false;
+
+ // lookup the block info, using the cache if possible
+ auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+ auto info = bic ? *bic : query(slice.ptr);
+
+ if (!(info.attr & BlkAttr.APPENDABLE))
+ // not appendable
+ return false;
+
+ assert(info.base); // sanity check.
+
+ immutable offset = slice.ptr - __arrayStart(info);
+ newUsed += offset;
+ auto existingUsed = slice.length + offset;
+
+ size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+ if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize))
+ {
+ // could expand without extending
+ if (!bic && !atomic)
+ // cache the lookup for next time
+ __insertBlkInfoCache(info, null);
+ return true;
+ }
+
+ // if we got here, just setting the used size did not work.
+ if (info.size < PAGESIZE)
+ // nothing else we can do
+ return false;
+
+ // try extending the block into subsequent pages.
+ 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 false;
+
+ info.size = extendedSize;
+ if (bic)
+ *bic = info;
+ else if (!atomic)
+ __insertBlkInfoCache(info, null);
+
+ // this should always work.
+ return __setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize);
+ }
+
+ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+ {
+ import core.internal.gc.blockmeta;
+ import core.internal.gc.blkcache;
+ import core.internal.array.utils;
+
+ if (existingUsed < slice.length)
+ // cannot "shrink" by growing.
+ return false;
+
+ // lookup the block info, using the cache if possible.
+ auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+ auto info = bic ? *bic : query(slice.ptr);
+
+ if (!(info.attr & BlkAttr.APPENDABLE))
+ // not appendable
+ return false;
+
+ assert(info.base); // sanity check
+
+ immutable offset = slice.ptr - __arrayStart(info);
+ existingUsed += offset;
+ auto newUsed = slice.length + offset;
+
+ size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+
+ if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize))
+ {
+ if (!bic && !atomic)
+ __insertBlkInfoCache(info, null);
+ return true;
+ }
+
+ return false;
+ }
+
+ size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @trusted
+ {
+ import core.internal.gc.blockmeta;
+ import core.internal.gc.blkcache;
+ import core.internal.array.utils;
+
+ // lookup the block info, using the cache if possible.
+ auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+ auto info = bic ? *bic : query(slice.ptr);
+
+ if (!(info.attr & BlkAttr.APPENDABLE))
+ // not appendable
+ return 0;
+
+ assert(info.base); // sanity check
+
+ immutable offset = slice.ptr - __arrayStart(info);
+ request += offset;
+ auto existingUsed = slice.length + offset;
+
+ // make sure this slice ends at the used space
+ auto blockUsed = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info);
+ if (existingUsed != blockUsed)
+ // not an expandable slice.
+ return 0;
+
+ // see if the capacity can contain the existing data
+ auto existingCapacity = __arrayAllocCapacity(info);
+ if (existingCapacity < request)
+ {
+ if (info.size < PAGESIZE)
+ // no possibility to extend
+ return 0;
+
+ immutable requiredExtension = request - existingCapacity;
+ auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null);
+ if (extendedSize == 0)
+ // could not extend, can't satisfy the request
+ return 0;
+
+ info.size = extendedSize;
+
+ // update the block info cache if it was used
+ if (bic)
+ *bic = info;
+ else if (!atomic)
+ __insertBlkInfoCache(info, null);
+
+ existingCapacity = __arrayAllocCapacity(info);
+ }
+
+ return existingCapacity - offset;
+ }
}
{
return typeof(return).init;
}
+
+ void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+ {
+ return null;
+ }
+
+ bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+ {
+ return false;
+ }
+
+ size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+ {
+ return 0;
+ }
+
+ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+ {
+ return false;
+ }
}
{
return stats().allocatedInCurrentThread;
}
+
+ void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+ {
+ return null;
+ }
+
+ bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+ {
+ return false;
+ }
+
+ size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+ {
+ return 0;
+ }
+
+ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+ {
+ return false;
+ }
}
return instance.allocatedInCurrentThread();
}
+ void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow
+ {
+ return instance.getArrayUsed( ptr, atomic );
+ }
+
+ bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow
+ {
+ return instance.expandArrayUsed( slice, newUsed, atomic );
+ }
+
+ size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow
+ {
+ return instance.reserveArrayCapacity( slice, request, atomic );
+ }
+
+ bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow
+ {
+ return instance.shrinkArrayUsed( slice, existingUsed, atomic );
+ }
+
GC gc_getProxy() nothrow
{
return instance;
template hasIndirections(T)
{
- static if (is(T == struct) || is(T == union))
+ static if (is(T == enum))
+ enum hasIndirections = hasIndirections!(OriginalType!T);
+ else static if (is(T == struct) || is(T == union))
enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof));
else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface))
enum hasIndirections = true;
else static if (is(T == E[N], E, size_t N))
- enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E);
+ enum hasIndirections = T.sizeof && (is(E == void) || hasIndirections!(BaseElemOf!E));
else static if (isFunctionPointer!T)
enum hasIndirections = false;
else
enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T;
}
+@safe unittest
+{
+ static assert(!hasIndirections!int);
+ static assert(!hasIndirections!(const int));
+
+ static assert( hasIndirections!(int*));
+ static assert( hasIndirections!(const int*));
+
+ static assert( hasIndirections!(int[]));
+ static assert(!hasIndirections!(int[42]));
+ static assert(!hasIndirections!(int[0]));
+
+ static assert( hasIndirections!(int*));
+ static assert( hasIndirections!(int*[]));
+ static assert( hasIndirections!(int*[42]));
+ static assert(!hasIndirections!(int*[0]));
+
+ static assert( hasIndirections!string);
+ static assert( hasIndirections!(string[]));
+ static assert( hasIndirections!(string[42]));
+ static assert(!hasIndirections!(string[0]));
+
+ static assert( hasIndirections!(void[]));
+ static assert( hasIndirections!(void[17]));
+ static assert(!hasIndirections!(void[0]));
+
+ static assert( hasIndirections!(string[int]));
+ static assert( hasIndirections!(string[int]*));
+ static assert( hasIndirections!(string[int][]));
+ static assert( hasIndirections!(string[int][12]));
+ static assert(!hasIndirections!(string[int][0]));
+
+ static assert(!hasIndirections!(int function(string)));
+ static assert( hasIndirections!(int delegate(string)));
+ static assert(!hasIndirections!(const(int function(string))));
+ static assert( hasIndirections!(const(int delegate(string))));
+ static assert(!hasIndirections!(immutable(int function(string))));
+ static assert( hasIndirections!(immutable(int delegate(string))));
+
+ static class C {}
+ static assert( hasIndirections!C);
+
+ static interface I {}
+ static assert( hasIndirections!I);
+
+ {
+ enum E : int { a }
+ static assert(!hasIndirections!E);
+ }
+ {
+ enum E : int* { a }
+ static assert( hasIndirections!E);
+ }
+ {
+ enum E : string { a = "" }
+ static assert( hasIndirections!E);
+ }
+ {
+ enum E : int[] { a = null }
+ static assert( hasIndirections!E);
+ }
+ {
+ enum E : int[3] { a = [1, 2, 3] }
+ static assert(!hasIndirections!E);
+ }
+ {
+ enum E : int*[3] { a = [null, null, null] }
+ static assert( hasIndirections!E);
+ }
+ {
+ enum E : int*[0] { a = int*[0].init }
+ static assert(!hasIndirections!E);
+ }
+ {
+ enum E : C { a = null }
+ static assert( hasIndirections!E);
+ }
+ {
+ enum E : I { a = null }
+ static assert( hasIndirections!E);
+ }
+
+ {
+ static struct S {}
+ static assert(!hasIndirections!S);
+
+ enum E : S { a = S.init }
+ static assert(!hasIndirections!S);
+ }
+ {
+ static struct S { int i; }
+ static assert(!hasIndirections!S);
+
+ enum E : S { a = S.init }
+ static assert(!hasIndirections!S);
+ }
+ {
+ static struct S { C c; }
+ static assert( hasIndirections!S);
+
+ enum E : S { a = S.init }
+ static assert( hasIndirections!S);
+ }
+ {
+ static struct S { int[] arr; }
+ static assert( hasIndirections!S);
+
+ enum E : S { a = S.init }
+ static assert( hasIndirections!S);
+ }
+ {
+ int local;
+ struct S { void foo() { ++local; } }
+ static assert( hasIndirections!S);
+
+ enum E : S { a = S.init }
+ static assert( hasIndirections!S);
+ }
+
+ {
+ static union U {}
+ static assert(!hasIndirections!U);
+ }
+ {
+ static union U { int i; }
+ static assert(!hasIndirections!U);
+ }
+ {
+ static union U { C c; }
+ static assert( hasIndirections!U);
+ }
+ {
+ static union U { int[] arr; }
+ static assert( hasIndirections!U);
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12000
+@safe unittest
+{
+ static struct S(T)
+ {
+ static assert(hasIndirections!T);
+ }
+
+ static class A(T)
+ {
+ S!A a;
+ }
+
+ A!int dummy;
+}
+
template hasUnsharedIndirections(T)
{
static if (is(T == immutable))
enum F_UNLCK = 2;
enum F_WRLCK = 3;
+ enum O_NOFOLLOW = 0x0100;
enum O_CREAT = 0x0200;
enum O_EXCL = 0x0800;
enum O_NOCTTY = 0;
import core.sys.windows.accctrl, core.sys.windows.basetyps, core.sys.windows.w32api, core.sys.windows.winnt;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
VOID BuildExplicitAccessWithNameA(PEXPLICIT_ACCESS_A, LPSTR, DWORD,
ACCESS_MODE, DWORD);
VOID BuildExplicitAccessWithNameW(PEXPLICIT_ACCESS_W, LPWSTR, DWORD,
// FIXME: linkage attribute?
extern (C) /+DECLSPEC_IMPORT+/ extern const IID IID_ISecurityInformation;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HPROPSHEETPAGE CreateSecurityPage(LPSECURITYINFO psi);
BOOL EditSecurity(HWND hwndOwner, LPSECURITYINFO psi);
}
return Animate_Play(hwnd, frame, frame, 1);
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HBITMAP CreateMappedBitmap(HINSTANCE, INT_PTR, UINT, LPCOLORMAP, int);
HWND CreateStatusWindowA(LONG, LPCSTR, HWND, UINT);
HWND CreateStatusWindowW(LONG, LPCWSTR, HWND, UINT);
cast(LPARAM) lpSysTime);
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
void DrawInsert(HWND, HWND, int);
void DrawStatusTextA(HDC, LPRECT, LPCSTR, UINT);
void DrawStatusTextW(HDC, LPRECT, LPCWSTR, UINT);
}
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HDSA DSA_Create(INT, INT);
BOOL DSA_Destroy(HDSA);
VOID DSA_DestroyCallback(HDSA, PFNDSAENUMCALLBACK, PVOID);
cast(LPARAM) st);
}
-extern (Windows) BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT);
+extern (Windows) nothrow @nogc BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT);
BOOL TabCtrl_GetItem(HWND w, int i, LPTCITEM p) {
return cast(BOOL) SendMessage(w, TCM_GETITEM, i, cast(LPARAM) p);
return cast(BOOL) SendMessage(w, LVM_SETITEMCOUNT, i, cast(LPARAM) f);
}
- extern (Windows) {
+ extern (Windows) nothrow @nogc {
WINBOOL ImageList_SetImageCount(HIMAGELIST, UINT);
WINBOOL ImageList_Copy(HIMAGELIST, int, HIMAGELIST, int, UINT);
WINBOOL ImageList_DrawIndirect(IMAGELISTDRAWPARAMS*);
alias INT function(DWORD, DWORD, HFONT, LPWSTR) LPFNCCSIZETOTEXTW;
alias UINT function(LPCCINFOA) LPFNCCINFOA;
alias UINT function(LPCCINFOW) LPFNCCINFOW;
+nothrow @nogc:
UINT CustomControlInfoA(LPCCINFOA acci);
UINT CustomControlInfoW(LPCCINFOW acci);
}
@property bool fAckReq(bool f) { _bf = cast(ushort) ((_bf & ~0x8000) | (f << 15)); return f; }
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL DdeSetQualityOfService(HWND, const(SECURITY_QUALITY_OF_SERVICE)*,
PSECURITY_QUALITY_OF_SERVICE);
BOOL ImpersonateDdeClientWindow(HWND, HWND);
}
alias MONMSGSTRUCT* PMONMSGSTRUCT;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD);
PBYTE DdeAccessData(HDDEDATA, PDWORD);
HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD);
}
alias DHCPCAPI_PARAMS_ARRAY* PDHCPCAPI_PARAMS_ARRAY, LPDHCPCAPI_PARAMS_ARRAY;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
void DhcpCApiCleanup();
DWORD DhcpCApiInitialize(LPDWORD);
DWORD DhcpDeRegisterParamChange(DWORD, LPVOID, LPVOID);
frrvOkHeadless // = 7
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL AddERExcludedApplicationA(LPCSTR);
BOOL AddERExcludedApplicationW(LPCWSTR);
EFaultRepRetVal ReportFault(LPEXCEPTION_POINTERS, DWORD);
}
alias HSE_SEND_HEADER_EX_INFO* LPHSE_SEND_HEADER_EX_INF;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL GetExtensionVersion(HSE_VERSION_INFO*);
DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK*);
BOOL TerminateExtension(DWORD);
alias BOOL function(DIGEST_HANDLE refdata, PBYTE pData, DWORD dwLength)
DIGEST_FUNCTION;
+nothrow @nogc:
PIMAGE_NT_HEADERS CheckSumMappedFile(LPVOID, DWORD, LPDWORD, LPDWORD);
DWORD MapFileAndCheckSumA(LPSTR, LPDWORD, LPDWORD);
DWORD MapFileAndCheckSumW(PWSTR, LPDWORD, LPDWORD);
alias IUniformResourceLocator PIUniformResourceLocator,
PCIUniformResourceLocator;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL InetIsOffline(DWORD);
HRESULT MIMEAssociationDialogA(HWND, DWORD, PCSTR, PCSTR, PSTR, UINT);
HRESULT MIMEAssociationDialogW(HWND, DWORD, PCWSTR, PCWSTR, PWSTR, UINT);
import core.sys.windows.ipexport, core.sys.windows.iprtrmib, core.sys.windows.iptypes;
import core.sys.windows.winbase, core.sys.windows.windef;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
DWORD AddIPAddress(IPAddr, IPMask, DWORD, PULONG, PULONG);
DWORD CreateIpForwardEntry(PMIB_IPFORWARDROW);
DWORD CreateIpNetEntry(PMIB_IPNETROW);
}
alias NETLOGON_INFO_3* PNETLOGON_INFO_3;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
deprecated {
/* These are obsolete */
NET_API_STATUS NetAccessAdd(LPCWSTR,DWORD,PBYTE,PDWORD);
}
alias USER_OTHER_INFO* PUSER_OTHER_INFO, LPUSER_OTHER_INFO;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
NET_API_STATUS NetAlertRaise(LPCWSTR,PVOID,DWORD);
NET_API_STATUS NetAlertRaiseEx(LPCWSTR,PVOID,DWORD,LPCWSTR);
}
import core.sys.windows.lmcons, core.sys.windows.windef;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
NET_API_STATUS NetApiBufferAllocate(DWORD, PVOID*);
NET_API_STATUS NetApiBufferFree(PVOID);
NET_API_STATUS NetApiBufferReallocate(PVOID, DWORD, PVOID*);
}
alias MSG_INFO_1* PMSG_INFO_1, LPMSG_INFO_1;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
NET_API_STATUS NetMessageBufferSend(LPCWSTR, LPCWSTR, LPCWSTR, PBYTE,
DWORD);
NET_API_STATUS NetMessageNameAdd(LPCWSTR, LPCWSTR);
}
alias TIME_OF_DAY_INFO* PTIME_OF_DAY_INFO, LPTIME_OF_DAY_INFO;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
NET_API_STATUS NetRemoteTOD(LPCWSTR, PBYTE*);
NET_API_STATUS NetRemoteComputerSupports(LPCWSTR, DWORD, PDWORD);
NET_API_STATUS RxRemoteApi(DWORD, LPCWSTR, LPDESC, LPDESC, LPDESC,
alias PVOID LPSNMP_MGR_SESSION;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL SnmpMgrClose(LPSNMP_MGR_SESSION);
BOOL SnmpMgrCtl(LPSNMP_MGR_SESSION, DWORD, LPVOID, DWORD, LPVOID, DWORD,
LPDWORD);
ShutdownPowerOff
}
-extern (Windows) uint NtShutdownSystem(SHUTDOWN_ACTION Action);
+extern (Windows) nothrow @nogc uint NtShutdownSystem(SHUTDOWN_ACTION Action);
}
alias TRUSTED_DOMAIN_FULL_INFORMATION* PTRUSTED_DOMAIN_FULL_INFORMATION;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
NTSTATUS LsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING,
ULONG);
NTSTATUS LsaCallAuthenticationPackage(HANDLE, ULONG, PVOID, ULONG,
}
alias OLESERVERDOC* LPOLESERVERDOC;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
OLESTATUS OleDelete(LPOLEOBJECT);
OLESTATUS OleRelease(LPOLEOBJECT);
OLESTATUS OleSaveToStream(LPOLEOBJECT, LPOLESTREAM);
}
alias OLESTREAMVTBL* LPOLESTREAMVTBL;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HRESULT CreateDataAdviseHolder(LPDATAADVISEHOLDER*);
DWORD OleBuildVersion();
HRESULT ReadClassStg(LPSTORAGE, CLSID*);
alias IAccessible LPACCESSIBLE;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HRESULT AccessibleChildren(IAccessible, LONG, LONG, VARIANT*, LONG*);
HRESULT AccessibleObjectFromEvent(HWND, DWORD, DWORD, IAccessible, VARIANT*);
HRESULT AccessibleObjectFromPoint(POINT, IAccessible*, VARIANT*);
alias ICreateTypeLib2 LPCREATETYPELIB2;
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BSTR SysAllocString(const(OLECHAR)*);
int SysReAllocString(BSTR*, const(OLECHAR)*);
BSTR SysAllocStringLen(const(OLECHAR)*, uint);
}
alias OLEUIINSERTOBJECTA* POLEUIINSERTOBJECTA, LPOLEUIINSERTOBJECTA;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
UINT OleUIInsertObjectW(LPOLEUIINSERTOBJECTW);
UINT OleUIInsertObjectA(LPOLEUIINSERTOBJECTA);
}
}
alias OLEUIOBJECTPROPSA* POLEUIOBJECTPROPSA, LPOLEUIOBJECTPROPSA;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL OleUIAddVerbMenuW(LPOLEOBJECT, LPCWSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*);
BOOL OleUIAddVerbMenuA(LPOLEOBJECT, LPCSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*);
UINT OleUIBusyW(LPOLEUIBUSYW);
}
alias PSHNOTIFY* LPPSHNOTIFY;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
HPROPSHEETPAGE CreatePropertySheetPageA(LPCPROPSHEETPAGEA);
HPROPSHEETPAGE CreatePropertySheetPageW(LPCPROPSHEETPAGEW);
BOOL DestroyPropertySheetPage(HPROPSHEETPAGE);
// Grouped by application, not in alphabetical order.
-extern (Windows) {
+extern (Windows) nothrow @nogc {
/* Process Information
* http://windowssdk.msdn.microsoft.com/library/ms684870.aspx */
BOOL EnumProcesses(DWORD*, DWORD, DWORD*); /* NT/2000/XP/Server2003/Vista/Longhorn */
alias MIDL_user_allocate midl_user_allocate;
alias MIDL_user_free midl_user_free;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
int I_RpcMapWin32Status(RPC_STATUS);
}
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL WinHttpAddRequestHeaders(HINTERNET hRequest, LPCWSTR pwszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
BOOL WinHttpCheckPlatform();
}
alias NETCONNECTINFOSTRUCT* LPNETCONNECTINFOSTRUCT;
-extern (Windows) {
+extern (Windows) nothrow @nogc {
DWORD WNetAddConnection2A(LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD);
DWORD WNetAddConnection2W(LPNETRESOURCEW, LPCWSTR, LPCWSTR, DWORD);
DWORD WNetAddConnection3A(HWND, LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD);
alias SERVICE_FAILURE_ACTIONSW* LPSERVICE_FAILURE_ACTIONSW;
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
BOOL ChangeServiceConfigA(SC_HANDLE, DWORD, DWORD, DWORD, LPCSTR,
LPCSTR, LPDWORD, LPCSTR, LPCSTR, LPCSTR, LPCSTR);
BOOL ChangeServiceConfigW(SC_HANDLE, DWORD, DWORD, DWORD, LPCWSTR,
int GetMenuStringW(HMENU, UINT, LPWSTR, int, UINT);
BOOL GetMessageA(LPMSG, HWND, UINT, UINT);
BOOL GetMessageW(LPMSG, HWND, UINT, UINT);
-LONG GetMessageExtraInfo();
+LPARAM GetMessageExtraInfo();
DWORD GetMessagePos();
LONG GetMessageTime();
DWORD dwFileDateLS;
}
-extern (Windows) {
+extern (Windows) nothrow @nogc {
DWORD VerFindFileA(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, PUINT, LPSTR,
PUINT);
DWORD VerFindFileW(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, PUINT, LPWSTR,
alias BlkInfo = GC.BlkInfo;
alias BlkAttr = GC.BlkAttr;
+// for now, all GC array functions are not exposed via core.memory.
+extern(C) {
+ void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow;
+ bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow;
+ size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow;
+ bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow;
+}
+
private
{
alias bool function(Object) CollectHandler;
{
import core.checkedint;
- size_t typeInfoSize = structTypeInfoSize(tinext);
- size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize);
-
- bool overflow;
- auto padded_size = addu(arrsize, padsize, overflow);
-
- if (overflow)
- return BlkInfo();
-
- uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE;
- if (typeInfoSize)
- attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
-
- auto bi = GC.qalloc(padded_size, attr, tinext);
- __arrayClearPad(bi, arrsize, padsize);
- return bi;
+ return __arrayAlloc(arrsize, null, ti, tinext);
}
-private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
+private BlkInfo __arrayAlloc(size_t arrsize, void* copyAttrsFrom, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
{
import core.checkedint;
- if (!info.base)
- return __arrayAlloc(arrsize, ti, tinext);
-
immutable padsize = __arrayPad(arrsize, tinext);
bool overflow;
auto padded_size = addu(arrsize, padsize, overflow);
return BlkInfo();
}
- auto bi = GC.qalloc(padded_size, info.attr, tinext);
+ uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0);
+ if (copyAttrsFrom)
+ {
+ // try to copy attrs from the given block
+ auto info = GC.query(copyAttrsFrom);
+ if (info.base)
+ attr = info.attr;
+ }
+ // always make sure the appendable attr is set.
+ attr |= BlkAttr.APPENDABLE;
+
+ auto bi = GC.qalloc(padded_size, attr, tinext);
__arrayClearPad(bi, arrsize, padsize);
return bi;
}
*/
extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow
{
- // note, we do not care about shared. We are setting the length no matter
- // what, so no lock is required.
debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length);
auto tinext = unqualify(ti.next);
auto size = tinext.tsize; // array element size
- auto cursize = arr.length * size;
+ auto reqsize = arr.length * size;
auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- auto bic = isshared ? null : __getBlkInfo(arr.ptr);
- auto info = bic ? *bic : GC.query(arr.ptr);
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
- {
- auto newsize = (arr.ptr - __arrayStart(info)) + cursize;
- debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize);
+ 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;
- // destroy structs that become unused memory when array size is shrinked
- if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
+ // 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)
{
- auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
- if (sti.xdtor)
+ try
{
- auto oldsize = __arrayAllocLength(info, tinext);
- if (oldsize > cursize)
- {
- try
- {
- finalize_array(arr.ptr + cursize, oldsize - cursize, sti);
- }
- catch (Exception e)
- {
- import core.exception : onFinalizeError;
- onFinalizeError(sti, e);
- }
- }
+ finalize_array(arr.ptr + reqsize, cursize - reqsize, sti);
+ }
+ catch (Exception e)
+ {
+ import core.exception : onFinalizeError;
+ onFinalizeError(sti, e);
}
}
- // Note: Since we "assume" the append is safe, it means it is not shared.
- // Since it is not shared, we also know it won't throw (no lock).
- if (!__setArrayAllocLength(info, newsize, false, tinext))
- {
- import core.exception : onInvalidMemoryOperationError;
- onInvalidMemoryOperationError();
- }
-
- // cache the block if not already done.
- if (!isshared && !bic)
- __insertBlkInfoCache(info, null);
}
+
+ gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared);
}
package bool hasPostblit(in TypeInfo ti) nothrow pure
import core.stdc.string;
import core.exception : onOutOfMemoryError;
- // step 1, get the block
auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- auto bic = isshared ? null : __getBlkInfo((*p).ptr);
- auto info = bic ? *bic : GC.query((*p).ptr);
auto tinext = unqualify(ti.next);
auto size = tinext.tsize;
version (D_InlineAsm_X86)
assert(0);
Lcontinue:
- // step 2, get the actual "allocated" size. If the allocated size does not
- // match what we expect, then we will need to reallocate anyways.
-
- // TODO: this probably isn't correct for shared arrays
- size_t curallocsize = void;
- size_t curcapacity = void;
- size_t offset = void;
- size_t arraypad = void;
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
- {
- if (info.size <= 256)
- {
- arraypad = SMALLPAD + structTypeInfoSize(tinext);
- curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad));
- }
- else if (info.size < PAGESIZE)
- {
- arraypad = MEDPAD + structTypeInfoSize(tinext);
- curallocsize = *(cast(ushort *)(info.base + info.size - arraypad));
- }
- else
- {
- curallocsize = *(cast(size_t *)(info.base));
- arraypad = LARGEPAD;
- }
-
-
- offset = (*p).ptr - __arrayStart(info);
- if (offset + (*p).length * size != curallocsize)
- {
- curcapacity = 0;
- }
- else
- {
- // figure out the current capacity of the block from the point
- // of view of the array.
- curcapacity = info.size - offset - arraypad;
- }
- }
- else
- {
- curallocsize = curcapacity = offset = 0;
- }
- debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset);
-
- if (curcapacity >= reqsize)
- {
- // no problems, the current allocated size is large enough.
- return curcapacity / size;
- }
-
- // step 3, try to extend the array in place.
- if (info.size >= PAGESIZE && curcapacity != 0)
- {
- auto extendsize = reqsize + offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, extendsize, extendsize);
- if (u)
- {
- // extend worked, save the new current allocated size
- if (bic)
- bic.size = u; // update cache
- curcapacity = u - offset - LARGEPAD;
- return curcapacity / size;
- }
- }
-
- // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size.
+ // step 1, see if we can ensure the capacity is valid in-place
auto datasize = (*p).length * size;
- // copy attributes from original block, or from the typeinfo if the
- // original block doesn't exist.
- info = __arrayAlloc(reqsize, info, ti, tinext);
+ auto curCapacity = gc_reserveArrayCapacity((*p).ptr[0 .. datasize], reqsize, isshared);
+ if (curCapacity != 0)
+ // in-place worked!
+ return curCapacity / size;
+
+ if (reqsize <= datasize)
+ // requested size is less than array size, the current array satisfies
+ // the request. But this is not an appendable GC array, so return 0.
+ return 0;
+
+ // step 2, if reserving in-place doesn't work, allocate a new array with at
+ // least the requested allocated size.
+ auto info = __arrayAlloc(reqsize, (*p).ptr, ti, tinext);
if (info.base is null)
goto Loverflow;
// copy the data over.
// set up the correct length
__setArrayAllocLength(info, datasize, isshared, tinext);
if (!isshared)
- __insertBlkInfoCache(info, bic);
+ __insertBlkInfoCache(info, null);
*p = (cast(void*)tgt)[0 .. (*p).length];
- // determine the padding. This has to be done manually because __arrayPad
- // assumes you are not counting the pad size, and info.size does include
- // the pad.
- if (info.size <= 256)
- arraypad = SMALLPAD + structTypeInfoSize(tinext);
- else if (info.size < PAGESIZE)
- arraypad = MEDPAD + structTypeInfoSize(tinext);
- else
- arraypad = LARGEPAD;
-
- curcapacity = info.size - arraypad;
- return curcapacity / size;
+ curCapacity = __arrayAllocCapacity(info);
+ return curCapacity / size;
}
/**
{
// the GC might not have cleared the padding area in the block
*cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null;
- *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti;
+ __setBlockFinalizerInfo(blkInf, cast() ti);
}
return p;
}
-debug(PRINTF)
-{
- extern(C) void printArrayCache()
- {
- auto ptr = __blkcache;
- printf("CACHE: \n");
- foreach (i; 0 .. N_CACHE_BLOCKS)
- {
- printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
- }
- }
-}
-
/**
*
*/
{
debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p);
- TypeInfo_Struct si = void;
+ // construct a BlkInfo to match the array API
+ auto info = BlkInfo(
+ base: p,
+ size: size,
+ attr: BlkAttr.APPENDABLE | BlkAttr.STRUCTFINAL
+ );
+ auto usedsize = __arrayAllocLength(info);
+ auto arrptr = __arrayStart(info);
+
debug (VALGRIND)
{
auto block = p[0..size];
disableAddrReportingInRange(block);
}
- if (size <= 256)
- {
- si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
- size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD);
- }
- else if (size < PAGESIZE)
- {
- si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
- size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD);
- }
- else
- {
- si = *cast(TypeInfo_Struct*)(p + size_t.sizeof);
- size = *cast(size_t*)p;
- p += LARGEPREFIX;
- }
+
+ TypeInfo_Struct si = size < PAGESIZE ?
+ *cast(TypeInfo_Struct*)(p + size - size_t.sizeof) : // small
+ *cast(TypeInfo_Struct*)(p + size_t.sizeof); // large
+
debug (VALGRIND) enableAddrReportingInRange(block);
try
{
- finalize_array(p, size, si);
+ finalize_array(arrptr, usedsize, si);
}
catch (Exception e)
{
if (newlength <= (*p).length)
{
*p = (*p)[0 .. newlength];
- void* newdata = (*p).ptr;
- return newdata[0 .. newlength];
+ return *p;
}
auto tinext = unqualify(ti.next);
size_t sizeelem = tinext.tsize;
if (!(*p).ptr)
{
+ assert((*p).length == 0);
// pointer was null, need to allocate
auto info = __arrayAlloc(newsize, ti, tinext);
if (info.base is null)
}
const size_t size = (*p).length * sizeelem;
- auto bic = isshared ? null : __getBlkInfo((*p).ptr);
- auto info = bic ? *bic : GC.query((*p).ptr);
/* Attempt to extend past the end of the existing array.
* If not possible, allocate new space for entire array and copy.
*/
- bool allocateAndCopy = false;
void* newdata = (*p).ptr;
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
- {
- // calculate the extent of the array given the base.
- const size_t offset = (*p).ptr - __arrayStart(info);
- if (info.size >= PAGESIZE)
- {
- // size of array is at the front of the block
- if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // check to see if it failed because there is not
- // enough space
- if (*(cast(size_t*)info.base) == size + offset)
- {
- // not enough space, try extending
- auto extendsize = newsize + offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, extendsize, extendsize);
- if (u)
- {
- // extend worked, now try setting the length
- // again.
- info.size = u;
- if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- memset(newdata + size, 0, newsize - size);
- *p = newdata[0 .. newlength];
- return *p;
- }
- }
- }
-
- // couldn't do it, reallocate
- allocateAndCopy = true;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // could not resize in place
- allocateAndCopy = true;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else
- allocateAndCopy = true;
-
- if (allocateAndCopy)
+ if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared))
{
- if (info.base)
- {
- if (bic)
- {
- // a chance that flags have changed since this was cached, we should fetch the most recent flags
- info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
- }
- info = __arrayAlloc(newsize, info, ti, tinext);
- }
- else
- {
- info = __arrayAlloc(newsize, ti, tinext);
- }
-
+ auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext);
if (info.base is null)
{
onOutOfMemoryError();
assert(0);
}
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- newdata = cast(byte *)__arrayStart(info);
+ newdata = __arrayStart(info);
newdata[0 .. size] = (*p).ptr[0 .. size];
- /* Do postblit processing, as we are making a copy and the
- * original array may have references.
- * Note that this may throw.
- */
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+
+ // Do postblit processing, as we are making a copy.
__doPostblit(newdata, size, tinext);
+
+ // this hasn't been added to the cache yet.
+ if (!isshared)
+ __insertBlkInfoCache(info, null);
}
// Zero the unused portion of the newly allocated space
debug(PRINTF)
{
- //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
+ //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
if (p)
printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
}
if (newlength <= (*p).length)
{
*p = (*p)[0 .. newlength];
- void* newdata = (*p).ptr;
- return newdata[0 .. newlength];
+ return *p;
}
auto tinext = unqualify(ti.next);
size_t sizeelem = tinext.tsize;
if (!(*p).ptr)
{
+ assert((*p).length == 0);
// pointer was null, need to allocate
auto info = __arrayAlloc(newsize, ti, tinext);
if (info.base is null)
}
const size_t size = (*p).length * sizeelem;
- auto bic = isshared ? null : __getBlkInfo((*p).ptr);
- auto info = bic ? *bic : GC.query((*p).ptr);
/* Attempt to extend past the end of the existing array.
* If not possible, allocate new space for entire array and copy.
*/
- bool allocateAndCopy = false;
void* newdata = (*p).ptr;
-
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
+ if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared))
{
- // calculate the extent of the array given the base.
- const size_t offset = (*p).ptr - __arrayStart(info);
- if (info.size >= PAGESIZE)
- {
- // size of array is at the front of the block
- if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // check to see if it failed because there is not
- // enough space
- if (*(cast(size_t*)info.base) == size + offset)
- {
- // not enough space, try extending
- auto extendsize = newsize + offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, extendsize, extendsize);
- if (u)
- {
- // extend worked, now try setting the length
- // again.
- info.size = u;
- if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- doInitialize(newdata + size, newdata + newsize, tinext.initializer);
- *p = newdata[0 .. newlength];
- return *p;
- }
- }
- }
-
- // couldn't do it, reallocate
- allocateAndCopy = true;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // could not resize in place
- allocateAndCopy = true;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else
- allocateAndCopy = true;
-
- if (allocateAndCopy)
- {
- if (info.base)
- {
- if (bic)
- {
- // a chance that flags have changed since this was cached, we should fetch the most recent flags
- info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
- }
- info = __arrayAlloc(newsize, info, ti, tinext);
- }
- else
- {
- info = __arrayAlloc(newsize, ti, tinext);
- }
-
+ auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext);
if (info.base is null)
{
onOutOfMemoryError();
assert(0);
}
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- newdata = cast(byte *)__arrayStart(info);
+ newdata = __arrayStart(info);
newdata[0 .. size] = (*p).ptr[0 .. size];
- /* Do postblit processing, as we are making a copy and the
- * original array may have references.
- * Note that this may throw.
- */
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+
+ // Do postblit processing, as we are making a copy.
__doPostblit(newdata, size, tinext);
+
+ // this hasn't been added to the cache yet.
+ if (!isshared)
+ __insertBlkInfoCache(info, null);
}
// Initialize the unused portion of the newly allocated space
byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
{
import core.stdc.string;
+ import core.exception : onOutOfMemoryError;
// This is a cut&paste job from _d_arrayappendT(). Should be refactored.
// 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 bic = isshared ? null : __getBlkInfo(px.ptr);
- auto info = bic ? *bic : GC.query(px.ptr);
auto length = px.length;
auto newlength = length + n;
auto newsize = newlength * sizeelem;
auto size = length * sizeelem;
- size_t newcap = void; // for scratch space
- // calculate the extent of the array given the base.
- size_t offset = cast(void*)px.ptr - __arrayStart(info);
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
+ if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared))
{
- if (info.size >= PAGESIZE)
- {
- // size of array is at the front of the block
- if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // check to see if it failed because there is not
- // enough space
- newcap = newCapacity(newlength, sizeelem);
- if (*(cast(size_t*)info.base) == size + offset)
- {
- // not enough space, try extending
- auto extendoffset = offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset);
- if (u)
- {
- // extend worked, now try setting the length
- // again.
- info.size = u;
- if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- goto L1;
- }
- }
- }
-
- // couldn't do it, reallocate
- goto L2;
- }
- else if (!isshared && !bic)
- {
- __insertBlkInfoCache(info, null);
- }
- }
- else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // could not resize in place
- newcap = newCapacity(newlength, sizeelem);
- goto L2;
- }
- else if (!isshared && !bic)
- {
- __insertBlkInfoCache(info, null);
- }
- }
- else
- {
- // not appendable or is null
- newcap = newCapacity(newlength, sizeelem);
- if (info.base)
- {
- L2:
- if (bic)
- {
- // a chance that flags have changed since this was cached, we should fetch the most recent flags
- info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
- }
- info = __arrayAlloc(newcap, info, ti, tinext);
- }
- else
+ // could not set the size, we must reallocate.
+ auto newcap = newCapacity(newlength, sizeelem);
+ auto info = __arrayAlloc(newcap, px.ptr, ti, tinext);
+ if (info.base is null)
{
- info = __arrayAlloc(newcap, ti, tinext);
+ onOutOfMemoryError();
+ assert(0);
}
+
__setArrayAllocLength(info, newsize, isshared, tinext);
if (!isshared)
- __insertBlkInfoCache(info, bic);
+ __insertBlkInfoCache(info, null);
+
auto newdata = cast(byte*)__arrayStart(info);
- memcpy(newdata, px.ptr, length * sizeelem);
- // do postblit processing
- __doPostblit(newdata, length * sizeelem, tinext);
- (cast(void**)&px)[1] = newdata;
+ memcpy(newdata, px.ptr, size);
+
+
+ // For small blocks that are always fully scanned, if we allocated more
+ // capacity than was requested, we are responsible for zeroing that
+ // memory.
+ // large blocks are only scanned up to the used size.
+ if (!(info.attr & BlkAttr.NO_SCAN) && newcap > newcap && info.size < PAGESIZE)
+ memset(newdata + newsize, 0, newcap - newsize);
+
+ // do potsblit processing.
+ __doPostblit(newdata, size, tinext);
+
+ px = newdata[0 .. newlength];
+ return px;
}
- L1:
- *cast(size_t*)&px = newlength;
+ // we were able to expand in place, just update the length
+ px = px.ptr[0 .. newlength];
return px;
}
-dbc09d8230f0e273af8a78546e5431a7783478b5
+ad8ee55872a25c93beacfb0ae1cca80dc8115916
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
/// jdrewsen - Would like to get socket error constant from std.socket by it is private atm.
version (Windows)
{
- import core.sys.windows.windows, core.sys.windows.winsock2;
+ import core.sys.windows.winsock2;
enum CURL_SOCKET_BAD = SOCKET_ERROR;
}
version (Posix) enum CURL_SOCKET_BAD = -1;
`haystack` before reaching an element for which
`startsWith!pred(haystack, needles)` is `true`. If
`startsWith!pred(haystack, needles)` is not `true` for any element in
- `haystack`, then `-1` is returned. If only `pred` is provided,
- `pred(haystack)` is tested for each element.
+ `haystack`, then `-1` is returned. If more than one needle is provided,
+ `countUntil` will wrap the result in a tuple similar to
+ `Tuple!(ptrdiff_t, "steps", ptrdiff_t needle)`
See_Also: $(REF indexOf, std,string)
+/
-ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
+auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
if (isForwardRange!R
&& Rs.length > 0
&& isForwardRange!(Rs[0]) == isInputRange!(Rs[0])
&& allSatisfy!(canTestStartsWith!(pred, R), Rs))
{
- typeof(return) result;
-
static if (needles.length == 1)
{
static if (hasLength!R) //Note: Narrow strings don't have length.
auto len = haystack.length;
auto r2 = find!pred(haystack, needles[0]);
if (!r2.empty)
- return cast(typeof(return)) (len - r2.length);
+ return ptrdiff_t(len - r2.length);
}
else
{
import std.range : dropOne;
if (needles[0].empty)
- return 0;
+ return ptrdiff_t(0);
+
+ ptrdiff_t result;
//Default case, slower route doing startsWith iteration
for ( ; !haystack.empty ; ++result )
//haystack we pop in all paths, so we do that, and then save.
haystack.popFront();
if (startsWith!pred(haystack.save, needles[0].save.dropOne()))
- return result;
+ return result;
}
else
- haystack.popFront();
+ {
+ haystack.popFront();
+ }
}
}
+ return ptrdiff_t(-1);
}
else
{
+ static struct Result
+ {
+ ptrdiff_t steps, needle; // both -1 when nothing was found
+ alias steps this; // compatible with previous ptrdiff_t return type
+ ptrdiff_t opIndex(size_t idx) // faking a tuple
+ {
+ assert(idx < 2, "Type has only two members: pos and needle");
+ return idx == 0 ? steps : needle;
+ }
+ }
+ Result result;
+
foreach (i, Ri; Rs)
{
static if (isForwardRange!Ri)
{
if (needles[i].empty)
- return 0;
+ {
+ result.needle = i;
+ return result;
+ }
}
}
Tuple!Rs t;
t[i] = needles[i];
}
}
- for (; !haystack.empty ; ++result, haystack.popFront())
+ for (; !haystack.empty ; ++result.steps, haystack.popFront())
{
foreach (i, Ri; Rs)
{
t[i] = needles[i].save;
}
}
- if (startsWith!pred(haystack.save, t.expand))
+ if (auto needle = startsWith!pred(haystack.save, t.expand))
{
+ result.needle = needle - 1;
return result;
}
}
- }
- // Because of https://issues.dlang.org/show_bug.cgi?id=8804
- // Avoids both "unreachable code" or "no return statement"
- static if (isInfinite!R) assert(false, R.stringof ~ " must not be an"
- ~ " infinite range");
- else return -1;
+ // no match was found
+ result.needle = -1;
+ result.steps = -1;
+ return result;
+ }
}
/// ditto
assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+
+ // supports multiple needles
+ auto res = "...hello".countUntil("ha", "he");
+ assert(res.steps == 3);
+ assert(res.needle == 1);
+
+ // returns -1 if no needle was found
+ res = "hello".countUntil("ha", "hu");
+ assert(res.steps == -1);
+ assert(res.needle == -1);
}
@safe unittest
assert(countUntil("hello world", "world", 'l') == 2);
}
+@safe unittest
+{
+ auto res = "...hello".countUntil("hella", "hello");
+ assert(res == 3);
+ assert(res.steps == 3);
+ assert(res.needle == 1);
+ // test tuple emulation
+ assert(res[0] == 3);
+ assert(res[1] == 1);
+
+ // the first matching needle is chosen
+ res = "...hello".countUntil("hell", "hello");
+ assert(res == 3);
+ assert(res.needle == 0);
+
+ // no match
+ auto noMatch = "hello world".countUntil("ha", "hu");
+ assert(noMatch == -1);
+ assert(noMatch.steps == -1);
+ assert(noMatch.needle == -1);
+ // test tuple emulation
+ assert(noMatch[0] == -1);
+ assert(noMatch[1] == -1);
+}
+
+// test infinite ranges
+@safe unittest
+{
+ import std.algorithm.iteration : joiner;
+ import std.range : iota, repeat;
+ import std.stdio;
+ assert(10.iota.repeat.joiner.countUntil(9) == 9);
+ assert(10.iota.repeat.joiner.countUntil(1, 2) == 1);
+ assert(10.iota.repeat.joiner.countUntil!(a => a >= 9) == 9);
+}
+
/// ditto
ptrdiff_t countUntil(alias pred, R)(R haystack)
if (isInputRange!R &&
static assert(test == "three"d);
}
+///
+@safe pure nothrow unittest
+{
+ import std.meta : AliasSeq;
+
+ // can be used as an alternative implementation of overlap that returns
+ // `true` or `false` instead of a slice of the overlap
+ bool isSliceOf(T)(const scope T[] part, const scope T[] whole)
+ {
+ return part.overlap(whole) is part;
+ }
+
+ auto x = [1, 2, 3, 4, 5];
+
+ assert(isSliceOf(x[3..$], x));
+ assert(isSliceOf(x[], x));
+ assert(!isSliceOf(x, x[3..$]));
+ assert(!isSliceOf([7, 8], x));
+ assert(isSliceOf(null, x));
+
+ // null is a slice of itself
+ assert(isSliceOf(null, null));
+
+ foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
+ {
+ T a = [1, 2, 3, 4, 5];
+ T b = a;
+ T c = a[1 .. $];
+ T d = a[0 .. 1];
+ T e = null;
+
+ assert(isSliceOf(a, a));
+ assert(isSliceOf(b, a));
+ assert(isSliceOf(a, b));
+
+ assert(isSliceOf(c, a));
+ assert(isSliceOf(c, b));
+ assert(!isSliceOf(a, c));
+ assert(!isSliceOf(b, c));
+
+ assert(isSliceOf(d, a));
+ assert(isSliceOf(d, b));
+ assert(!isSliceOf(a, d));
+ assert(!isSliceOf(b, d));
+
+ assert(isSliceOf(e, a));
+ assert(isSliceOf(e, b));
+ assert(isSliceOf(e, c));
+ assert(isSliceOf(e, d));
+
+ //verifies R-value compatibilty
+ assert(!isSliceOf(a[$ .. $], a));
+ assert(isSliceOf(a[0 .. 0], a));
+ assert(isSliceOf(a, a[0.. $]));
+ assert(isSliceOf(a[0 .. $], a));
+ }
+}
+
@safe pure nothrow unittest
{
static void test(L, R)(L l, R r)
avoid thrashing (excessive creation/destruction of allocators under certain use
patterns), it keeps unused allocators for a while.
+The shared version of `AllocatorList` is `SharedAllocatorList`, which has
+identical semantics to its single-threaded version. Both `BookkeepingAllocator`
+and `Allocator` provided by `factoryFunction` must be shared, in order to
+ensure corectness.
+
Params:
factoryFunction = A function or template function (including function literals).
New allocators are created by calling `factoryFunction(n)` with strictly
assert(b1.length == 1024 * 10);
}
+/// Ditto
+shared struct SharedAllocatorList(Factory, BookkeepingAllocator = GCAllocator)
+{
+ import std.typecons : Ternary;
+ import std.traits : hasMember;
+ import core.internal.spinlock : SpinLock;
+
+private:
+ // Forward all calls to 'impl' and protect them by the lock below
+ AllocatorList!(Factory, BookkeepingAllocator) impl;
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+
+ // This could be potentially removed in the future,
+ // should a successor to <https://github.com/dlang/druntime/pull/2156>
+ // or a solution to <https://github.com/dlang/dmd/issues/17128> get merged.
+ static ref T assumeUnshared(T)(ref shared T val) @trusted @nogc pure nothrow
+ {
+ return *cast(T*) &val;
+ }
+
+ // Debug function used for testing
+ version (unittest)
+ auto allocators()
+ {
+ return impl.allocators;
+ }
+
+// Everything is inherited from the 'AllocatorList' implementation
+public:
+
+ /*
+ Note: This does not work well with rvalues because it copies them once more.
+ Probably not a problem here because all parameters are cheap.
+ <https://github.com/dlang/phobos/pull/6465/files#r189629862>
+ */
+
+ /**
+ The alignment offered.
+ */
+ enum alignment = impl.alignment;
+
+ /**
+ Allocate a block of size `s`. First tries to allocate from the existing
+ list of already-created allocators. If neither can satisfy the request,
+ creates a new allocator by calling `make(s)` and delegates the request
+ to it. However, if the allocation fresh off a newly created allocator
+ fails, subsequent calls to `allocate` will not cause more calls to $(D
+ make).
+ */
+ static if (hasMember!(typeof(impl), "allocate"))
+ void[] allocate(size_t s)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).allocate(s);
+ }
+
+ /**
+ Allocate a block of size `s` with alignment `a`. First tries to allocate
+ from the existing list of already-created allocators. If neither can
+ satisfy the request, creates a new allocator by calling `make(s + a - 1)`
+ and delegates the request to it. However, if the allocation fresh off a
+ newly created allocator fails, subsequent calls to `alignedAllocate`
+ will not cause more calls to `make`.
+ */
+ static if (hasMember!(typeof(impl), "alignedAllocate"))
+ void[] alignedAllocate(size_t s, uint a)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).alignedAllocate(s, a);
+ }
+
+ /**
+ Defined if `Allocator.deallocate` and `Allocator.owns` are defined.
+ */
+ static if (hasMember!(typeof(impl), "deallocate"))
+ bool deallocate(void[] b)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).deallocate(b);
+ }
+
+ /**
+ Defined only if `Allocator` defines `owns`. Tries each allocator in
+ turn, in most-recently-used order. If the owner is found, it is moved to
+ the front of the list as a side effect under the assumption it will be used
+ soon.
+
+ Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`,
+ `Ternary.no` if all component allocators returned `Ternary.no`, and
+ `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one
+ returned `Ternary.unknown`.
+ */
+ static if (hasMember!(typeof(impl), "owns"))
+ Ternary owns(void[] b)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).owns(b);
+ }
+
+ /**
+ Defined only if `Allocator.expand` is defined. Finds the owner of `b`
+ and calls `expand` for it. The owner is not brought to the head of the
+ list.
+ */
+ static if (hasMember!(typeof(impl), "expand"))
+ bool expand(ref void[] b, size_t delta)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).expand(b, delta);
+ }
+
+ /**
+ Defined only if `Allocator.reallocate` is defined. Finds the owner of
+ `b` and calls `reallocate` for it. If that fails, calls the global
+ `reallocate`, which allocates a new block and moves memory.
+ */
+ static if (hasMember!(typeof(impl), "reallocate"))
+ bool reallocate(ref void[] b, size_t s)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).reallocate(b, s);
+ }
+
+ /**
+ Defined only if `Allocator.owns` and `Allocator.deallocateAll` are
+ defined.
+ */
+ static if (hasMember!(typeof(impl), "deallocateAll"))
+ bool deallocateAll()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).deallocateAll();
+ }
+
+ /**
+ Returns `Ternary.yes` if no allocators are currently active,
+ `Ternary.no` otherwise. This methods never returns `Ternary.unknown`.
+ */
+ static if (hasMember!(typeof(impl), "empty"))
+ Ternary empty()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).empty();
+ }
+}
+
+/// Ditto
+template SharedAllocatorList(alias factoryFunction,
+ BookkeepingAllocator = GCAllocator)
+{
+ alias A = typeof(factoryFunction(1));
+ static assert(
+ // is a template function (including literals)
+ is(typeof({A function(size_t) @system x = factoryFunction!size_t;}))
+ ||
+ // or a function (including literals)
+ is(typeof({A function(size_t) @system x = factoryFunction;}))
+ ,
+ "Only function names and function literals that take size_t"
+ ~ " and return an allocator are accepted, not "
+ ~ typeof(factoryFunction).stringof
+ );
+ static struct Factory
+ {
+ A opCall(size_t n) { return factoryFunction(n); }
+ }
+ alias SharedAllocatorList = .SharedAllocatorList!(Factory, BookkeepingAllocator);
+}
+
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ const b1 = a.allocate(1024 * 8192);
+ assert(b1 !is null); // still works due to overdimensioning
+ const b2 = a.allocate(1024 * 10);
+ assert(b2.length == 1024 * 10);
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
- NullAllocator) a;
- const b1 = a.allocate(1024 * 8192);
- assert(b1 !is null); // still works due to overdimensioning
- const b2 = a.allocate(1024 * 10);
- assert(b2.length == 1024 * 10);
- a.deallocateAll();
+ NullAllocator) reg1;
+
+ SharedAllocatorList!((n) => SharedRegion!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
+ NullAllocator) reg2;
+
+ testAlloc(reg1);
+ testAlloc(reg2);
}
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.alignedAllocate(1024 * 8192, 1024);
- assert(b1 !is null); // still works due to overdimensioning
- assert(b1.length == 1024 * 8192);
- assert(b1.ptr.alignedAt(1024));
- assert(a.allocators.length == 1);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
- b1 = a.alignedAllocate(0, 1024);
- assert(b1.length == 0);
- assert(a.allocators.length == 1);
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.alignedAllocate(1024 * 8192, 1024);
+ assert(b1 !is null); // still works due to overdimensioning
+ assert(b1.length == 1024 * 8192);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
- b1 = a.allocate(1024 * 10);
- assert(b1.length == 1024 * 10);
+ b1 = a.alignedAllocate(0, 1024);
+ assert(b1.length == 0);
+ assert(a.allocators.length == 1);
- assert(a.reallocate(b1, 1024));
- assert(b1.length == 1024);
+ b1 = a.allocate(1024 * 10);
+ assert(b1.length == 1024 * 10);
- a.deallocateAll();
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
{
import core.exception : AssertError;
import std.exception : assertThrown;
-
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.alignedAllocate(0, 1);
- assert(b1 is null);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.alignedAllocate(0, 1);
+ assert(b1 is null);
+
+ b1 = a.alignedAllocate(1, 0);
+ assert(b1 is null);
- b1 = a.alignedAllocate(1, 0);
- assert(b1 is null);
+ b1 = a.alignedAllocate(0, 0);
+ assert(b1 is null);
- b1 = a.alignedAllocate(0, 0);
- assert(b1 is null);
+ assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
- assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
- a.deallocateAll();
+ // FIXME: This special-casing might note be necessary.
+ // At the moment though, this call would take potentially forever
+ // for the `SharedAllocatorList` from below.
+ static if (!is(Allocator == shared))
+ {
+ a.deallocateAll();
+ }
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b0 = a.alignedAllocate(1, 1024);
- assert(b0.length == 1);
- assert(b0.ptr.alignedAt(1024));
- assert(a.allocators.length == 1);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
- auto b1 = a.alignedAllocate(1024 * 4096, 1024);
- assert(b1.length == 1024 * 4096);
- assert(b1.ptr.alignedAt(1024));
- assert(a.allocators.length == 2);
-
- auto b2 = a.alignedAllocate(1024, 128);
- assert(b2.length == 1024);
- assert(b2.ptr.alignedAt(128));
- assert(a.allocators.length == 2);
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b0 = a.alignedAllocate(1, 1024);
+ assert(b0.length == 1);
+ assert(b0.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
+
+ auto b1 = a.alignedAllocate(1024 * 4096, 1024);
+ assert(b1.length == 1024 * 4096);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 2);
+
+ auto b2 = a.alignedAllocate(1024, 128);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(128));
+ assert(a.allocators.length == 2);
+
+ auto b3 = a.allocate(1024);
+ assert(b3.length == 1024);
+ assert(a.allocators.length == 2);
+
+ auto b4 = a.allocate(1024 * 4096);
+ assert(b4.length == 1024 * 4096);
+ assert(a.allocators.length == 3);
+
+ static if (!is(Allocator == shared))
+ {
+ assert(a.root.empty == Ternary.no);
+ assert(a.deallocate(b4));
+ assert(a.root.empty == Ternary.yes);
- auto b3 = a.allocate(1024);
- assert(b3.length == 1024);
- assert(a.allocators.length == 2);
+ assert(a.deallocate(b1));
+ }
- auto b4 = a.allocate(1024 * 4096);
- assert(b4.length == 1024 * 4096);
- assert(a.allocators.length == 3);
+ a.deallocateAll();
+ }
- assert(a.root.empty == Ternary.no);
- assert(a.deallocate(b4));
- assert(a.root.empty == Ternary.yes);
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
- assert(a.deallocate(b1));
- a.deallocateAll();
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.allocate(1024 * 8192);
- assert(b1 !is null); // still works due to overdimensioning
- b1 = a.allocate(1024 * 10);
- assert(b1.length == 1024 * 10);
- assert(a.reallocate(b1, 1024));
- assert(b1.length == 1024);
- a.deallocateAll();
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.allocate(1024 * 8192);
+ assert(b1 !is null); // still works due to overdimensioning
+ b1 = a.allocate(1024 * 10);
+ assert(b1.length == 1024 * 10);
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
enum bs = GCAllocator.alignment;
- AllocatorList!((n) => Region!GCAllocator(256 * bs)) a;
- auto b1 = a.allocate(192 * bs);
- assert(b1.length == 192 * bs);
- assert(a.allocators.length == 1);
- auto b2 = a.allocate(64 * bs);
- assert(b2.length == 64 * bs);
- assert(a.allocators.length == 1);
- auto b3 = a.allocate(192 * bs);
- assert(b3.length == 192 * bs);
- assert(a.allocators.length == 2);
- // Ensure deallocate inherits from parent allocators
- () nothrow @nogc { a.deallocate(b1); }();
- b1 = a.allocate(64 * bs);
- assert(b1.length == 64 * bs);
- assert(a.allocators.length == 2);
- a.deallocateAll();
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.allocate(192 * bs);
+ assert(b1.length == 192 * bs);
+ assert(a.allocators.length == 1);
+ auto b2 = a.allocate(64 * bs);
+ assert(b2.length == 64 * bs);
+ assert(a.allocators.length == 1);
+ auto b3 = a.allocate(192 * bs);
+ assert(b3.length == 192 * bs);
+ assert(a.allocators.length == 2);
+ // Ensure deallocate inherits from parent allocators
+ () nothrow @nogc { a.deallocate(b1); }();
+ b1 = a.allocate(64 * bs);
+ assert(b1.length == 64 * bs);
+ assert(a.allocators.length == 2);
+ a.deallocateAll();
+ }
+
+ AllocatorList!((n) => Region!GCAllocator(256 * bs)) a1;
+ SharedAllocatorList!((n) => SharedRegion!GCAllocator(256 * bs)) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
@system unittest
{
- import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator :
+ AscendingPageAllocator, SharedAscendingPageAllocator;
import std.experimental.allocator.mallocator : Mallocator;
import std.algorithm.comparison : max;
import std.typecons : Ternary;
+ enum pageSize = 4096;
+ enum numPages = 2;
+
static void testrw(void[] b)
{
ubyte* buf = cast(ubyte*) b.ptr;
}
}
- enum numPages = 2;
- AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ void[] b1 = a.allocate(1);
+ assert(b1.length == 1);
+ b1 = a.allocate(2);
+ assert(b1.length == 2);
+ testrw(b1);
- void[] b1 = a.allocate(1);
- assert(b1.length == 1);
- b1 = a.allocate(2);
- assert(b1.length == 2);
- testrw(b1);
+ void[] b2 = a.allocate((numPages + 1) * pageSize);
+ assert(b2.length == (numPages + 1) * pageSize);
+ testrw(b2);
- void[] b2 = a.allocate((numPages + 1) * pageSize);
- assert(b2.length == (numPages + 1) * pageSize);
- testrw(b2);
+ void[] b3 = a.allocate(3);
+ assert(b3.length == 3);
+ testrw(b3);
- void[] b3 = a.allocate(3);
- assert(b3.length == 3);
- testrw(b3);
+ void[] b4 = a.allocate(0);
+ assert(b4.length == 0);
- void[] b4 = a.allocate(0);
- assert(b4.length == 0);
+ assert(a.allocators.length == 3);
+ assert(a.owns(b1) == Ternary.yes);
+ assert(a.owns(b2) == Ternary.yes);
+ assert(a.owns(b3) == Ternary.yes);
- assert(a.allocators.length == 3);
- assert(a.owns(b1) == Ternary.yes);
- assert(a.owns(b2) == Ternary.yes);
- assert(a.owns(b3) == Ternary.yes);
+ assert(a.expand(b1, pageSize - b1.length));
+ assert(b1.length == pageSize);
+ assert(!a.expand(b1, 1));
+ assert(!a.expand(b2, 1));
- assert(a.expand(b1, pageSize - b1.length));
- assert(b1.length == pageSize);
- assert(!a.expand(b1, 1));
- assert(!a.expand(b2, 1));
+ testrw(b1);
+ testrw(b2);
+ testrw(b3);
- testrw(b1);
- testrw(b2);
- testrw(b3);
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
- assert(a.deallocate(b1));
- assert(a.deallocate(b2));
+ const alignment = cast(uint) (70 * pageSize);
+ b3 = a.alignedAllocate(70 * pageSize, alignment);
+ assert(b3.length == 70 * pageSize);
+ assert(b3.ptr.alignedAt(alignment));
+ testrw(b3);
+ assert(a.allocators.length == 4);
+ assert(a.deallocate(b3));
- const alignment = cast(uint) (70 * pageSize);
- b3 = a.alignedAllocate(70 * pageSize, alignment);
- assert(b3.length == 70 * pageSize);
- assert(b3.ptr.alignedAt(alignment));
- testrw(b3);
- assert(a.allocators.length == 4);
- assert(a.deallocate(b3));
+ assert(a.deallocateAll());
+ }
- assert(a.deallocateAll());
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a1;
+ SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a2;
}
@system unittest
assert(a.deallocateAll());
}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.algorithm.comparison : max;
+ import std.typecons : Ternary;
+
+ enum pageSize = 4096;
+
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ for (int i = 0; i < b.length; i += pageSize)
+ {
+ buf[i] = cast(ubyte) (i % 256);
+ assert(buf[i] == cast(ubyte) (i % 256));
+ }
+ }
+
+ enum numPages = 5;
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ auto b = a.alignedAllocate(1, pageSize * 2);
+ assert(b.length == 1);
+ assert(a.expand(b, 4095));
+ assert(b.ptr.alignedAt(2 * 4096));
+ assert(b.length == 4096);
+
+ b = a.allocate(4096);
+ assert(b.length == 4096);
+ assert(a.allocators.length == 1);
+
+ assert(a.allocate(4096 * 5).length == 4096 * 5);
+ assert(a.allocators.length == 2);
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : SharedRegion;
+ import core.thread : ThreadGroup;
+ import std.algorithm.comparison : max;
+
+ enum numThreads = 10;
+ SharedAllocatorList!((n) => SharedRegion!(GCAllocator)(new ubyte[max(n, 1024)])) a;
+
+ void fun()
+ {
+ void[] b1 = a.allocate(1024);
+ assert(b1.length == 1024);
+
+ void[] b2 = a.alignedAllocate(1024, 1024);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(1024));
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
+ import core.thread : ThreadGroup;
+ import std.algorithm.comparison : max;
+
+ enum numThreads = 100;
+ enum pageSize = 4096;
+ enum numPages = 10;
+ SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, pageSize * numPages)), Mallocator) a;
+
+ void fun()
+ {
+ void[] b1 = a.allocate(512);
+ assert(b1.length == 512);
+ assert(a.expand(b1, 512));
+ assert(b1.length == 1024);
+
+ void[] b2 = a.alignedAllocate(1024, 4096);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(1024));
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ assert(a.deallocateAll());
+}
$(H4 $(LNAME2 format-indicator, Format Indicator))
The $(I format indicator) can either be a single character or an
-expression surrounded by $(B %\() and $(B %\)). It specifies the
+expression surrounded by $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). It specifies the
basic manner in which a value will be formatted and is the minimum
requirement to format a value.
The $(I compound indicator) can be used to describe compound types
like arrays or structs in more detail. A compound type is enclosed
-within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is
+within $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). The enclosed sub-format string is
applied to individual elements. The trailing portion of the
sub-format string following the specifier for the element is
interpreted as the delimiter, and is therefore omitted following the
assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
}
+private template getOverloads(alias fun)
+{
+ import std.meta : AliasSeq;
+ static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true)))
+ alias getOverloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true);
+ else
+ alias getOverloads = AliasSeq!fun;
+}
+
/**
* $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as
* to avoid repeated computation. The memoization structure is a hash table keyed by a
*/
template memoize(alias fun)
{
- import std.traits : ReturnType;
- // https://issues.dlang.org/show_bug.cgi?id=13580
- // alias Args = Parameters!fun;
+ import std.traits : Parameters;
+ import std.meta : anySatisfy;
+
+ // Specific overloads:
+ alias overloads = getOverloads!fun;
+ static foreach (fn; overloads)
+ static if (is(Parameters!fn))
+ alias memoize = impl!(Parameters!fn);
+
+ enum isTemplate(alias a) = __traits(isTemplate, a);
+ static if (anySatisfy!(isTemplate, overloads))
+ {
+ // Generic implementation
+ alias memoize = impl;
+ }
- ReturnType!fun memoize(Parameters!fun args)
+ auto impl(Args...)(Args args) if (is(typeof(fun(args))))
{
- alias Args = Parameters!fun;
- import std.typecons : Tuple;
+ import std.typecons : Tuple, tuple;
import std.traits : Unqual;
- static Unqual!(ReturnType!fun)[Tuple!Args] memo;
- auto t = Tuple!Args(args);
- if (auto p = t in memo)
- return *p;
- auto r = fun(args);
- memo[t] = r;
- return r;
+ static if (args.length > 0)
+ {
+ static Unqual!(typeof(fun(args)))[Tuple!(typeof(args))] memo;
+
+ auto t = Tuple!Args(args);
+ if (auto p = t in memo)
+ return *p;
+ auto r = fun(args);
+ memo[t] = r;
+ return r;
+ }
+ else
+ {
+ static typeof(fun(args)) result;
+ result = fun(args);
+ return result;
+ }
}
}
/// ditto
template memoize(alias fun, uint maxSize)
{
- import std.traits : ReturnType;
- // https://issues.dlang.org/show_bug.cgi?id=13580
- // alias Args = Parameters!fun;
- ReturnType!fun memoize(Parameters!fun args)
+ import std.traits : Parameters;
+ import std.meta : anySatisfy;
+
+ // Specific overloads:
+ alias overloads = getOverloads!fun;
+ static foreach (fn; overloads)
+ static if (is(Parameters!fn))
+ alias memoize = impl!(Parameters!fn);
+
+ enum isTemplate(alias a) = __traits(isTemplate, a);
+ static if (anySatisfy!(isTemplate, overloads))
{
- import std.meta : staticMap;
- import std.traits : hasIndirections, Unqual;
- import std.typecons : tuple;
- static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; }
- static Value[] memo;
- static size_t[] initialized;
+ // Generic implementation
+ alias memoize = impl;
+ }
- if (!memo.length)
+ auto impl(Args...)(Args args) if (is(typeof(fun(args))))
+ {
+ static if (args.length > 0)
{
- import core.memory : GC;
+ import std.meta : staticMap;
+ import std.traits : hasIndirections, Unqual;
+ import std.typecons : tuple;
+ alias returnType = typeof(fun(args));
+ static struct Value { staticMap!(Unqual, Args) args; Unqual!returnType res; }
+ static Value[] memo;
+ static size_t[] initialized;
- // Ensure no allocation overflows
- static assert(maxSize < size_t.max / Value.sizeof);
- static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
+ if (!memo.length)
+ {
+ import core.memory : GC;
- enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
- memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
- enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
- initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
- }
+ // Ensure no allocation overflows
+ static assert(maxSize < size_t.max / Value.sizeof);
+ static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
- import core.bitop : bt, bts;
- import core.lifetime : emplace;
+ enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
+ memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
+ enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
+ initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
+ }
- size_t hash;
- foreach (ref arg; args)
- hash = hashOf(arg, hash);
- // cuckoo hashing
- immutable idx1 = hash % maxSize;
- if (!bt(initialized.ptr, idx1))
- {
- emplace(&memo[idx1], args, fun(args));
- // only set to initialized after setting args and value
- // https://issues.dlang.org/show_bug.cgi?id=14025
- bts(initialized.ptr, idx1);
+ import core.bitop : bt, bts;
+ import core.lifetime : emplace;
+
+ size_t hash;
+ foreach (ref arg; args)
+ hash = hashOf(arg, hash);
+ // cuckoo hashing
+ immutable idx1 = hash % maxSize;
+ if (!bt(initialized.ptr, idx1))
+ {
+ emplace(&memo[idx1], args, fun(args));
+ // only set to initialized after setting args and value
+ // https://issues.dlang.org/show_bug.cgi?id=14025
+ bts(initialized.ptr, idx1);
+ return memo[idx1].res;
+ }
+ else if (memo[idx1].args == args)
+ return memo[idx1].res;
+ // FNV prime
+ immutable idx2 = (hash * 16_777_619) % maxSize;
+ if (!bt(initialized.ptr, idx2))
+ {
+ emplace(&memo[idx2], memo[idx1]);
+ bts(initialized.ptr, idx2);
+ }
+ else if (memo[idx2].args == args)
+ return memo[idx2].res;
+ else if (idx1 != idx2)
+ memo[idx2] = memo[idx1];
+
+ memo[idx1] = Value(args, fun(args));
return memo[idx1].res;
}
- else if (memo[idx1].args == args)
- return memo[idx1].res;
- // FNV prime
- immutable idx2 = (hash * 16_777_619) % maxSize;
- if (!bt(initialized.ptr, idx2))
+ else
{
- emplace(&memo[idx2], memo[idx1]);
- bts(initialized.ptr, idx2);
+ static typeof(fun(args)) result;
+ result = fun(args);
+ return result;
}
- else if (memo[idx2].args == args)
- return memo[idx2].res;
- else if (idx1 != idx2)
- memo[idx2] = memo[idx1];
-
- memo[idx1] = Value(args, fun(args));
- return memo[idx1].res;
}
}
assert(fact(10) == 3628800);
}
+// Issue 20099
+@system unittest // not @safe due to memoize
+{
+ int i = 3;
+ alias a = memoize!((n) => i + n);
+ alias b = memoize!((n) => i + n, 3);
+
+ assert(a(3) == 6);
+ assert(b(3) == 6);
+}
+
+@system unittest // not @safe due to memoize
+{
+ static Object objNum(int a) { return new Object(); }
+ assert(memoize!objNum(0) is memoize!objNum(0U));
+ assert(memoize!(objNum, 3)(0) is memoize!(objNum, 3)(0U));
+}
+
+@system unittest // not @safe due to memoize
+{
+ struct S
+ {
+ static int fun() { return 0; }
+ static int fun(int i) { return 1; }
+ }
+ assert(memoize!(S.fun)() == 0);
+ assert(memoize!(S.fun)(3) == 1);
+ assert(memoize!(S.fun, 3)() == 0);
+ assert(memoize!(S.fun, 3)(3) == 1);
+}
+
@system unittest // not @safe due to memoize
{
import core.math : sqrt;
}}
}
+// memoize should continue to work with functions that cannot be evaluated at compile time
+@system unittest
+{
+ __gshared string[string] glob;
+
+ static bool foo()
+ {
+ return (":-)" in glob) is null;
+ }
+
+ assert(memoize!foo);
+}
+
private struct DelegateFaker(F)
{
import std.typecons : FuncInfo, MemberFunctionGenerator;
{
return fp;
}
+ else static if (is(F Func == Func*) && is(Func == function))
+ {
+ return function(ref F fp) @trusted
+ {
+ return buildDelegate(fp);
+ }(fp);
+ }
else static if (is(typeof(&F.opCall) == delegate)
|| (is(typeof(&F.opCall) V : V*) && is(V == function)))
{
return toDelegate(&fp.opCall);
}
+ else static if (is(typeof(&fp.opCall!())))
+ {
+ return toDelegate(&fp.opCall!());
+ }
else
{
+ static assert(false, "Unsupported type of callable, please open an issue.");
+ }
+}
+
+///
+@safe unittest
+{
+ static int inc(ref uint num) {
+ num++;
+ return 8675309;
+ }
+
+ uint myNum = 0;
+ auto incMyNumDel = toDelegate(&inc);
+ auto returnVal = incMyNumDel(myNum);
+ assert(myNum == 1);
+}
+
+private template buildDelegate(F)
+{
+ auto buildDelegate(auto ref F fp) {
alias DelType = typeof(&(new DelegateFaker!(F)).doIt);
static struct DelegateFields {
}
}
-///
-@system unittest
+@safe unittest
{
static int inc(ref uint num) {
num++;
return 8675309;
}
- uint myNum = 0;
- auto incMyNumDel = toDelegate(&inc);
- auto returnVal = incMyNumDel(myNum);
- assert(myNum == 1);
+ uint myNum = 0x1337;
+ struct S1 { int opCall() { inc(myNum); return myNum; } }
+ static assert(!is(typeof(&s1.opCall) == delegate));
+ S1 s1;
+ auto getvals1 = toDelegate(s1);
+ assert(getvals1() == 0x1338);
}
-@system unittest // not @safe due to toDelegate
+@system unittest
{
static int inc(ref uint num) {
num++;
}
}
+
+@safe unittest
+{
+ static struct S1 { static void opCall()() { } }
+ static struct S2 { static T opCall(T = int)(T x) {return x; } }
+
+ S1 i1;
+ const dg1 = toDelegate(i1);
+ dg1();
+
+ S2 i2;
+ assert(toDelegate(i2)(0xBED) == 0xBED);
+}
+
+@safe unittest
+{
+ static void fun() @system pure nothrow @nogc
+ {
+ return;
+ }
+
+ auto fn = &fun;
+ static assert( is(typeof(fn) == void function() @system pure nothrow @nogc));
+ static assert(!is(typeof(fn) == void function() @safe pure nothrow @nogc));
+
+ auto dg = fn.toDelegate();
+ static assert( is(typeof(dg) == void delegate() @system pure nothrow @nogc));
+ static assert(!is(typeof(dg) == void delegate() @safe pure nothrow @nogc));
+}
+
/**
* Passes the fields of a struct as arguments to a function.
*
{
// we don't need to actually run the code, only make sure
// it compiles
- static _() {
+ static void _() {
auto l = new shared FileLogger("");
}
}
import std.logger;
void main() {
- log("Hello World");
+ info("Hello World");
}
-------------
This will print a message to the `stderr` device. The message will contain
$(LI `fatal`)
)
The default `Logger` will by default log to `stderr` and has a default
-`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
+`LogLevel` of `LogLevel.info`. The default Logger can be accessed by
using the property called `sharedLog`. This property is a reference to the
current default `Logger`. This reference can be used to assign a new
default `Logger`.
import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned;
+
/*********************************
* Determines if $(D_PARAM x) is NaN.
* Params:
*/
bool isIdentical(real x, real y) @trusted pure nothrow @nogc
{
- import std.math.traits : floatTraits, RealFormat;
-
+ if (__ctfe)
+ {
+ if (x !is y) return false;
+ if (x == x) return true; // If not NaN `is` implies identical representation.
+ static if (double.mant_dig != real.mant_dig)
+ {
+ // Works because we are in CTFE and there is no way in CTFE to set more
+ // bits of NaN payload than can fit in a double, and since 2.087
+ // changed real.init to be non-signaling I *think* there is no way in
+ // CTFE for a real to be a signaling NaN unless real and double have
+ // the same representation so real's bits can be manipulated directly.
+ double d1 = x, d2 = y;
+ }
+ else
+ {
+ // Alias to avoid converting signaling to quiet.
+ alias d1 = x;
+ alias d2 = y;
+ }
+ return *cast(long*) &d1 == *cast(long*) &d2;
+ }
// We're doing a bitwise comparison so the endianness is irrelevant.
long* pxs = cast(long *)&x;
long* pys = cast(long *)&y;
assert(0, "isIdentical not implemented");
}
}
-
///
@safe @nogc pure nothrow unittest
{
+ // We're forcing the CTFE to run by assigning the result of the function to an enum
+ enum test1 = isIdentical(1.0,1.0);
+ enum test2 = isIdentical(real.nan,real.nan);
+ enum test3 = isIdentical(real.infinity, real.infinity);
+ enum test4 = isIdentical(real.infinity, real.infinity);
+ enum test5 = isIdentical(0.0, 0.0);
+
+ assert(test1);
+ assert(test2);
+ assert(test3);
+ assert(test4);
+ assert(test5);
+
+ enum test6 = !isIdentical(0.0, -0.0);
+ enum test7 = !isIdentical(real.nan, -real.nan);
+ enum test8 = !isIdentical(real.infinity, -real.infinity);
+
+ assert(test6);
+ assert(test7);
+ assert(test8);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ assert( !isIdentical(1.2,1.3));
assert( isIdentical(0.0, 0.0));
assert( isIdentical(1.0, 1.0));
assert( isIdentical(real.infinity, real.infinity));
assert( isIdentical(-real.infinity, -real.infinity));
+ assert( isIdentical(real.nan, real.nan));
assert(!isIdentical(0.0, -0.0));
assert(!isIdentical(real.nan, -real.nan));
assert(!isIdentical(real.infinity, -real.infinity));
}
-
/*********************************
* Return 1 if sign bit of e is set, 0 if not.
*/
@safe unittest
{
import std.array;
- import std.utf : byDchar;
assert(rtrimDirSeparators("//abc//").array == "//abc");
assert(rtrimDirSeparators("//abc//"d).array == "//abc"d);
@safe unittest
{
import std.array;
- import std.utf : byDchar;
assert(trimDirSeparators("//abc//").array == "abc");
assert(trimDirSeparators("//abc//"d).array == "abc"d);
else version (Posix)
{
import core.sys.posix.signal : kill;
+ if (pid.osHandle == Pid.invalid)
+ throw new ProcessException("Pid is invalid");
+ if (pid.osHandle == Pid.terminated)
+ throw new ProcessException("Pid is already terminated");
if (kill(pid.osHandle, codeOrSignal) == -1)
throw ProcessException.newFromErrno();
}
do { s = tryWait(pid); } while (!s.terminated);
version (Windows) assert(s.status == 123);
else version (Posix) assert(s.status == -SIGKILL);
- assertThrown!ProcessException(kill(pid));
+ assertThrown!ProcessException(kill(pid)); // Already terminated
}
@system unittest // wait() and kill() detached process
private struct ByLineCopy(Char, Terminator)
{
private:
- import std.typecons : RefCounted, RefCountedAutoInitialize;
+ import std.typecons : SafeRefCounted, RefCountedAutoInitialize;
/* Ref-counting stops the source range's ByLineCopyImpl
* from getting out of sync after the range is copied, e.g.
* when accessing range.front, then using std.range.take,
* then accessing range.front again. */
- alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
+ alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
RefCountedAutoInitialize.no);
Impl impl;
f = File to read lines from.
terminator = Line separator (`'\n'` by default).
*/
- this(File f, dchar terminator = '\n')
+ this(File f, dchar terminator = '\n') @safe
{
this.f = f;
this.terminator = terminator;
}
}
+@safe unittest
+{
+ /*
+ As pointed out in <https://github.com/dlang/phobos/issues/10605>,
+ it's a pity that `byLine()` & co. aren't @safe to use yet.
+
+ This is a first attempt at working towards that goal.
+ For now, this test doesn't do much; as there isn't much to do safely yet.
+ */
+ auto deleteMe = testFilename();
+ scope(exit) { imported!"std.file".remove(deleteMe); }
+
+ // Setup
+ {
+ auto f = File(deleteMe, "w");
+ scope(exit) { f.close(); }
+ foreach (i; 1 .. 11)
+ f.writeln(i);
+ }
+
+ // Actual tests
+ {
+ auto f = File(deleteMe, "r");
+ scope(exit) { f.close(); }
+
+ auto myLines = lines(f);
+ foreach (string line; myLines)
+ continue;
+
+ auto myByLineCopy = f.byLineCopy; // but cannot safely iterate yet
+ /*
+ still `@system`:
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.empty`
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.popFront`
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.front`
+ */
+ //foreach (line; myByLineCopy)
+ // continue;
+ }
+}
+
@system unittest
{
static import std.file;
return stats().allocatedInCurrentThread;
}
+ void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+ {
+ return null;
+ }
+
+ bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+ {
+ return false;
+ }
+
+ size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+ {
+ return 0;
+ }
+
+ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+ {
+ return false;
+ }
+
private:
// doesn't care for alignment
static void* sentinelAdd(void* p, size_t value)