d: Merge upstream dmd, druntime c57da0cf59, phobos ad8ee5587

D front-end changes:

	- Import latest fixes from dmd v2.110.0-beta.1.
	- The `align' attribute now allows to specify `default'
	  explicitly.
	- Add primary expression of the form `__rvalue(expression)'
	  which causes `expression' to be treated as an rvalue, even if
	  it is an lvalue.
	- Shortened method syntax can now be used in constructors.

D runtime changes:

	- Import latest fixes from druntime v2.110.0-beta.1.

Phobos changes:

	- Import latest fixes from phobos v2.110.0-beta.1.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd c57da0cf59.
	* d-codegen.cc (can_elide_copy_p): New.
	(d_build_call): Use it.
	* d-lang.cc (d_post_options): Update for new front-end interface.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime c57da0cf59.
	* src/MERGE: Merge upstream phobos ad8ee5587.
	* testsuite/libphobos.init_fini/custom_gc.d: Adjust test.

gcc/testsuite/ChangeLog:

	* gdc.dg/copy1.d: New test.
This commit is contained in:
Iain Buclaw 2025-01-08 21:02:56 +01:00
parent a236f70617
commit 0dd21bce3a
124 changed files with 2872 additions and 1304 deletions

View file

@ -631,6 +631,37 @@ force_target_expr (tree exp)
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
@ -2280,8 +2311,10 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
- 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)),

View file

@ -925,7 +925,8 @@ d_post_options (const char ** fn)
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);

View file

@ -1,4 +1,4 @@
82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
c57da0cf5945cfb45eed06f1fd820435cda3ee3a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.

View file

@ -302,7 +302,7 @@ enum ThreeState : ubyte
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
}

View file

@ -104,16 +104,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol
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";
@ -653,20 +643,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration
}
}
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);
@ -762,12 +738,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
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";

View file

@ -28,7 +28,6 @@ class AttribDeclaration : public Dsymbol
{
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;
@ -148,7 +147,6 @@ public:
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); }
};
@ -176,7 +174,6 @@ public:
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); }
};

View file

@ -1610,13 +1610,15 @@ private Statement generateCopyCtorBody(StructDeclaration sd)
* 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;
@ -1648,14 +1650,17 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
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");
@ -1710,6 +1715,7 @@ LcheckFields:
* 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),
@ -1717,10 +1723,10 @@ LcheckFields:
* 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());

View file

@ -806,6 +806,7 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e
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_)
@ -820,7 +821,17 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio
{
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
{

View file

@ -30,6 +30,7 @@ import dmd.func;
import dmd.funcsem : overloadApply, getLevelAndCheck;
import dmd.globals;
import dmd.gluelayer;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.init;
@ -95,6 +96,7 @@ extern (C++) abstract class Declaration : Dsymbol
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;
@ -441,8 +443,7 @@ extern (C++) final class AliasDeclaration : Declaration
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);
}
@ -450,7 +451,7 @@ extern (C++) final class AliasDeclaration : Declaration
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);
@ -611,8 +612,9 @@ extern (C++) final class AliasDeclaration : Declaration
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;

View file

@ -782,6 +782,7 @@ class CtorDeclaration final : public FuncDeclaration
{
public:
d_bool isCpCtor;
d_bool isMoveCtor;
CtorDeclaration *syntaxCopy(Dsymbol *) override;
const char *kind() const override;
const char *toChars() const override;

View file

@ -24,6 +24,7 @@ import dmd.dclass;
import dmd.declaration;
import dmd.dmodule;
import dmd.doc;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
@ -146,6 +147,7 @@ extern (C++) struct Scope
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;

View file

@ -101,6 +101,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
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
@ -171,11 +172,12 @@ extern (C++) class StructDeclaration : AggregateDeclaration
{
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)
@ -320,11 +322,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration
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;

View file

@ -925,22 +925,8 @@ extern (C++) class Dsymbol : ASTNode
*/
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
@ -958,7 +944,7 @@ extern (C++) class Dsymbol : ASTNode
/* 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;
/**********************************

View file

@ -433,4 +433,5 @@ namespace dmd
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);
}

View file

@ -53,6 +53,7 @@ import dmd.init;
import dmd.initsem;
import dmd.intrange;
import dmd.hdrgen;
import dmd.lexer;
import dmd.location;
import dmd.mtype;
import dmd.mustuse;
@ -64,6 +65,7 @@ import dmd.parse;
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;
@ -256,6 +258,10 @@ Return:
*/
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());
@ -280,6 +286,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem
*/
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))
@ -306,6 +313,7 @@ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
*/
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;)
{
@ -2399,7 +2407,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
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)
@ -2432,6 +2440,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
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();
@ -2482,12 +2508,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
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
}
}
}
@ -2978,7 +3007,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
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);
@ -5211,7 +5243,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara
// 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
@ -7782,7 +7814,6 @@ private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
Expression ec;
ec = new VarExp(vd.loc, vd);
e = new DeleteExp(vd.loc, ec, true);
e.type = Type.tvoid;
break;
}
return e;
@ -7845,3 +7876,57 @@ Lfail:
}
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) {}
}

View file

@ -293,10 +293,16 @@ enum WANTexpand = 1; // expand const/immutable variables if possible
*/
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
{
@ -724,7 +730,7 @@ extern (C++) abstract class Expression : ASTNode
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; }
@ -1307,7 +1313,7 @@ extern (C++) class IdentifierExp : Expression
override final bool isLvalue()
{
return true;
return !this.rvalue;
}
override void accept(Visitor v)
@ -1351,7 +1357,7 @@ extern (C++) final class DsymbolExp : Expression
override bool isLvalue()
{
return true;
return !rvalue;
}
override void accept(Visitor v)
@ -1397,7 +1403,7 @@ extern (C++) class ThisExp : Expression
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)
@ -1782,7 +1788,7 @@ extern (C++) final class StringExp : Expression
/* 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);
}
/********************************
@ -2719,7 +2725,7 @@ extern (C++) final class VarExp : SymbolExp
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;
}
@ -3098,7 +3104,7 @@ extern (C++) class BinAssignExp : BinExp
override final bool isLvalue()
{
return true;
return !rvalue;
}
override void accept(Visitor v)
@ -3212,7 +3218,6 @@ extern (C++) final class ThrowExp : UnaExp
extern (D) this(const ref Loc loc, Expression e)
{
super(loc, EXP.throw_, e);
this.type = Type.tnoreturn;
}
override ThrowExp syntaxCopy()
@ -3303,6 +3308,8 @@ extern (C++) final class DotVarExp : UnaExp
override bool isLvalue()
{
if (rvalue)
return false;
if (e1.op != EXP.structLiteral)
return true;
auto vd = var.isVarDeclaration();
@ -3530,6 +3537,8 @@ extern (C++) final class CallExp : UnaExp
override bool isLvalue()
{
if (rvalue)
return false;
Type tb = e1.type.toBasetype();
if (tb.ty == Tdelegate || tb.ty == Tpointer)
tb = tb.nextOf();
@ -3648,7 +3657,7 @@ extern (C++) final class PtrExp : UnaExp
override bool isLvalue()
{
return true;
return !rvalue;
}
override void accept(Visitor v)
@ -3777,7 +3786,7 @@ extern (C++) final class CastExp : UnaExp
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());
@ -3834,7 +3843,7 @@ extern (C++) final class VectorArrayExp : UnaExp
override bool isLvalue()
{
return e1.isLvalue();
return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
@ -3891,7 +3900,7 @@ extern (C++) final class SliceExp : UnaExp
/* 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()
@ -3956,6 +3965,8 @@ extern (C++) final class ArrayExp : UnaExp
override bool isLvalue()
{
if (rvalue)
return false;
if (type && type.toBasetype().ty == Tvoid)
return false;
return true;
@ -4005,7 +4016,7 @@ extern (C++) final class CommaExp : BinExp
override bool isLvalue()
{
return e2.isLvalue();
return !rvalue && e2.isLvalue();
}
override Optional!bool toBool()
@ -4080,7 +4091,7 @@ extern (C++) final class DelegatePtrExp : UnaExp
override bool isLvalue()
{
return e1.isLvalue();
return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
@ -4103,7 +4114,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
override bool isLvalue()
{
return e1.isLvalue();
return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
@ -4143,6 +4154,8 @@ extern (C++) final class IndexExp : BinExp
override bool isLvalue()
{
if (rvalue)
return false;
auto t1b = e1.type.toBasetype();
if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
(e1.isIndexExp() && t1b != t1b.isTypeDArray()))
@ -4251,7 +4264,7 @@ extern (C++) class AssignExp : BinExp
{
return false;
}
return true;
return !rvalue;
}
override void accept(Visitor v)
@ -4982,7 +4995,7 @@ extern (C++) final class CondExp : BinExp
override bool isLvalue()
{
return e1.isLvalue() && e2.isLvalue();
return !rvalue && e1.isLvalue() && e2.isLvalue();
}
override void accept(Visitor v)

View file

@ -78,6 +78,7 @@ public:
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();
@ -138,7 +139,7 @@ public:
TypeidExp* isTypeidExp();
TraitsExp* isTraitsExp();
HaltExp* isHaltExp();
IsExp* isExp();
IsExp* isIsExp();
MixinExp* isMixinExp();
ImportExp* isImportExp();
AssertExp* isAssertExp();

View file

@ -816,21 +816,58 @@ extern(D) bool arrayExpressionSemantic(
* 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;
}
@ -839,13 +876,15 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
* 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)
@ -860,7 +899,9 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
* 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
@ -888,6 +929,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
*/
Expression valueNoDtor(Expression e)
{
//printf("valueNoDtor() %s\n", toChars(e));
auto ex = lastComma(e);
if (auto ce = ex.isCallExp())
@ -2706,7 +2748,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
continue;
}
e = doCopyOrMove(sc, e);
e = doCopyOrMove(sc, e, null, false);
if (!foundType && t0 && !t0.equals(e.type))
{
@ -2952,7 +2994,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
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;
@ -3677,7 +3719,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
*/
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;
@ -3886,11 +3928,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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);
@ -4109,11 +4148,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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;
@ -4174,11 +4208,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("SuperExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
FuncDeclaration fd = hasThis(sc);
ClassDeclaration cd;
@ -4255,11 +4284,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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;
}
@ -4348,11 +4372,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("StringExp::semantic() %s\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
OutBuffer buffer;
size_t newlen = 0;
@ -4461,11 +4480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("+TupleExp::semantic(%s)\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (exp.e0)
exp.e0 = exp.e0.expressionSemantic(sc);
@ -4503,11 +4517,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("ArrayLiteralExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
/* Perhaps an empty array literal [ ] should be rewritten as null?
*/
@ -4550,11 +4559,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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);
@ -4593,11 +4597,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("StructLiteralExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
e.sd.size(e.loc);
if (e.sd.sizeok != Sizeok.done)
@ -4702,11 +4701,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
ScopeDsymbol sds2 = exp.sds;
TemplateInstance ti = sds2.isTemplateInstance();
@ -4895,11 +4889,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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;
@ -5677,7 +5666,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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;
@ -5693,11 +5682,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf(" treq = %s\n", exp.fd.treq.toChars());
}
if (exp.type)
{
result = exp;
return;
}
Expression e = exp;
@ -5891,11 +5875,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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;
@ -6718,7 +6697,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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
@ -6801,7 +6780,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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)
@ -6954,11 +6933,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(DeclarationExp e)
{
if (e.type)
{
result = e;
return;
}
static if (LOGSEMANTIC)
{
printf("DeclarationExp::semantic() %s\n", e.toChars());
@ -7575,11 +7549,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(BinAssignExp exp)
{
if (exp.type)
{
result = exp;
return;
}
Expression e = exp.op_overload(sc);
if (e)
@ -8157,6 +8126,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
import dmd.statementsem;
te.type = Type.tnoreturn;
if (throwSemantic(te.loc, te.e1, sc))
result = te;
else
@ -8168,7 +8138,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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)
@ -8251,11 +8221,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(DotTemplateExp e)
{
if (e.type)
{
result = e;
return;
}
if (Expression ex = unaSemantic(e, sc))
{
result = ex;
@ -8272,11 +8237,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("DotVarExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
exp.var = exp.var.toAlias().isDeclaration();
@ -8444,11 +8404,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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)
@ -8464,11 +8419,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("DelegateExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
e.e1 = e.e1.expressionSemantic(sc);
@ -8530,11 +8480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("DotTypeExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (auto e = unaSemantic(exp, sc))
{
@ -8552,11 +8497,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("AddrExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = unaSemantic(exp, sc))
{
@ -8853,11 +8793,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("PtrExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
Expression e = exp.op_overload(sc);
if (e)
@ -8916,11 +8851,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("NegExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
Expression e = exp.op_overload(sc);
if (e)
@ -8962,7 +8892,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("UAddExp::semantic('%s')\n", exp.toChars());
}
assert(!exp.type);
Expression e = exp.op_overload(sc);
if (e)
@ -8989,11 +8918,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(ComExp exp)
{
if (exp.type)
{
result = exp;
return;
}
Expression e = exp.op_overload(sc);
if (e)
@ -9031,11 +8955,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(NotExp e)
{
if (e.type)
{
result = e;
return;
}
e.setNoderefOperand();
@ -9140,11 +9059,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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) &&
@ -9429,11 +9343,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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);
@ -9502,11 +9411,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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))
@ -9789,11 +9693,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("ArrayLengthExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
if (Expression ex = unaSemantic(e, sc))
{
@ -9812,7 +9711,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("ArrayExp::semantic('%s')\n", exp.toChars());
}
assert(!exp.type);
if (sc.inCfile)
{
@ -9886,11 +9784,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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)
@ -9936,11 +9829,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("IntervalExp::semantic('%s')\n", e.toChars());
}
if (e.type)
{
result = e;
return;
}
Expression le = e.lwr;
le = le.expressionSemantic(sc);
@ -10015,11 +9903,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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)
@ -10242,11 +10125,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("PostExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (sc.inCfile)
{
@ -10404,9 +10282,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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__)
@ -10415,11 +10293,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
}
if (exp.type)
{
return setResult(exp);
}
Expression e1old = exp.e1;
if (auto e2comma = exp.e2.isCommaExp())
@ -10876,6 +10749,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
/* 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)
@ -10922,6 +10797,38 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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
@ -11815,11 +11722,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(PowAssignExp exp)
{
if (exp.type)
{
result = exp;
return;
}
Expression e = exp.op_overload(sc);
if (e)
@ -11899,11 +11801,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(CatAssignExp exp)
{
if (exp.type)
{
result = exp;
return;
}
//printf("CatAssignExp::semantic() %s\n", exp.toChars());
Expression e = exp.op_overload(sc);
@ -11985,7 +11882,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
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) &&
@ -12195,11 +12092,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("AddExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -12302,11 +12194,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("MinExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -12554,11 +12441,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
// 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))
{
@ -12610,7 +12492,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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().
@ -12649,7 +12531,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
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_)
{
@ -12741,11 +12623,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("MulExp::semantic() %s\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -12841,11 +12718,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(DivExp exp)
{
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -12942,11 +12814,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(ModExp exp)
{
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -13000,11 +12867,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(PowExp exp)
{
if (exp.type)
{
result = exp;
return;
}
//printf("PowExp::semantic() %s\n", toChars());
if (Expression ex = binSemanticProp(exp, sc))
@ -13080,11 +12942,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
private void visitShift(BinExp exp)
{
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -13133,11 +12990,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
private void visitBinaryBitOp(BinExp exp)
{
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -13206,11 +13058,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("LogicalExp::semantic() %s\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
exp.setNoderefOperands();
@ -13292,11 +13139,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("CmpExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
exp.setNoderefOperands();
@ -13461,11 +13303,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(InExp exp)
{
if (exp.type)
{
result = exp;
return;
}
if (Expression ex = binSemanticProp(exp, sc))
{
@ -13531,11 +13368,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(EqualExp exp)
{
//printf("EqualExp::semantic('%s')\n", exp.toChars());
if (exp.type)
{
result = exp;
return;
}
exp.setNoderefOperands();
@ -13754,11 +13586,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(IdentityExp exp)
{
if (exp.type)
{
result = exp;
return;
}
exp.setNoderefOperands();
@ -13822,11 +13649,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("CondExp::semantic('%s')\n", exp.toChars());
}
if (exp.type)
{
result = exp;
return;
}
if (auto die = exp.econd.isDotIdExp())
die.noderef = true;
@ -14188,9 +14010,25 @@ Expression binSemanticProp(BinExp e, Scope* sc)
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;
@ -14198,7 +14036,7 @@ Expression expressionSemantic(Expression e, Scope* sc)
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;
@ -14326,7 +14164,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
*/
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; }
@ -14664,7 +14502,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
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);
@ -15482,6 +15320,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
*/
Expression addDtorHook(Expression e, Scope* sc)
{
//printf("addDtorHook() %s\n", toChars(e));
Expression visit(Expression exp)
{
return exp;
@ -16510,7 +16349,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
if (e.op == EXP.error)
return false;
(*elements)[i] = doCopyOrMove(sc, e);
(*elements)[i] = doCopyOrMove(sc, e, null, false);
}
return true;
}

View file

@ -269,6 +269,10 @@ extern (C++) class FuncDeclaration : Declaration
*/
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;
@ -1366,11 +1370,13 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
*/
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);
}

View file

@ -154,6 +154,28 @@ public:
}
}
/****************************************
* 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.
*/
@ -1507,6 +1529,7 @@ enum FuncResolveFlag : ubyte
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
@ -2066,7 +2089,7 @@ MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* name
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
@ -2939,6 +2962,7 @@ extern (D) void checkMain(FuncDeclaration fd)
*/
extern (D) bool checkNRVO(FuncDeclaration fd)
{
//printf("checkNRVO*() %s\n", fd.ident.toChars());
if (!fd.isNRVO() || fd.returns is null)
return false;

View file

@ -82,6 +82,13 @@ enum CLIIdentifierTable : ubyte
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
@ -126,10 +133,10 @@ extern(C++) struct Verbose
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)

View file

@ -94,6 +94,13 @@ enum class CLIIdentifierTable : unsigned char
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
@ -138,10 +145,10 @@ struct Verbose
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)
@ -192,7 +199,7 @@ struct Param
// 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

View file

@ -305,10 +305,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
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;
@ -322,7 +321,13 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
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)
@ -460,20 +465,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
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())
@ -572,21 +564,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
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)
@ -1718,7 +1696,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
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();
@ -2888,6 +2869,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
buf.writestring(e.value.toChars());
}
if (e.rvalue)
buf.writestring("__rvalue(");
scope (exit)
if (e.rvalue)
buf.writeByte(')');
switch (e.op)
{
default:
@ -2929,7 +2917,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
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());

View file

@ -519,6 +519,7 @@ immutable Msgtable[] msgtable =
{ "getLocation" },
{ "hasPostblit" },
{ "hasCopyConstructor" },
{ "hasMoveConstructor" },
{ "isCopyable" },
{ "toType" },
{ "parameters" },

View file

@ -211,11 +211,14 @@ nothrow:
* 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;
@ -234,14 +237,20 @@ nothrow:
* 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
{
@ -253,7 +262,7 @@ nothrow:
}
else
{
const key = Key(loc, prefix);
const key = Key(loc, prefix, parent);
if (auto pCounter = key in counters)
{
idBuf.writestring("_");

View file

@ -1630,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope*
continue;
}
elems[fieldi] = doCopyOrMove(sc, ex);
elems[fieldi] = doCopyOrMove(sc, ex, null, false);
++fieldi;
}
if (errors)

View file

@ -42,7 +42,8 @@ import dmd.target;
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;
}

View file

@ -1609,7 +1609,7 @@ extern (C++) abstract class TypeNext : Type
/*******************************
* 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
@ -3543,17 +3543,19 @@ extern (C++) final class TypeTuple : Type
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());

View file

@ -469,7 +469,7 @@ enum RET
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
};

View file

@ -93,39 +93,66 @@ struct ObjcSelector
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);
}
@ -565,6 +592,16 @@ extern(C++) private final class Supported : Objc
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)

View file

@ -932,22 +932,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
{
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;
@ -4371,14 +4363,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
}
case TOK.align_:
{
nextToken();
setAlignment = true;
if (token.value == TOK.leftParenthesis)
{
nextToken();
ealign = parseExpression();
check(TOK.rightParenthesis);
}
ealign = parseAlign();
continue;
}
default:
@ -4388,6 +4374,27 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
}
}
/**
* 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:
@ -5252,7 +5259,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
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;
@ -5877,6 +5887,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
case TOK.moduleString:
case TOK.functionString:
case TOK.prettyFunction:
case TOK.rvalue:
Lexp:
{
AST.Expression exp = parseExpression();
@ -8423,6 +8434,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
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...)

View file

@ -51,6 +51,12 @@ extern (C++) final class PrintASTVisitor : Visitor
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);

View file

@ -42,7 +42,8 @@ version (Windows)
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)

View file

@ -143,6 +143,7 @@ struct Scope final
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);
};

View file

@ -940,7 +940,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
* 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);

View file

@ -2460,7 +2460,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
/* 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)

View file

@ -623,7 +623,7 @@ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration
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
@ -1890,23 +1890,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
{
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);
}
@ -1993,7 +1992,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
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);
@ -2198,7 +2197,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
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;
@ -2300,8 +2299,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
// 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;
@ -2404,7 +2403,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
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,

View file

@ -27,6 +27,9 @@ enum TOK : ubyte
{
reserved,
// if this list changes, update
// tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match
// Other
leftParenthesis,
rightParenthesis,
@ -249,6 +252,7 @@ enum TOK : ubyte
wchar_tLiteral,
endOfLine, // \n, \r, \u2028, \u2029
whitespace,
rvalue,
// C only keywords
inline,
@ -425,6 +429,7 @@ enum EXP : ubyte
interval,
loweredAssignExp,
rvalue,
}
enum FirstCKeyword = TOK.inline;
@ -556,6 +561,7 @@ private immutable TOK[] keywords =
TOK.prettyFunction,
TOK.shared_,
TOK.immutable_,
TOK.rvalue,
// C only keywords
TOK.inline,
@ -680,6 +686,7 @@ extern (C++) struct Token
TOK.pragma_: "pragma",
TOK.typeof_: "typeof",
TOK.typeid_: "typeid",
TOK.rvalue: "__rvalue",
TOK.template_: "template",
TOK.void_: "void",
TOK.int8: "byte",

View file

@ -258,6 +258,7 @@ enum class TOK : unsigned char
wchar_tLiteral,
endOfLine, // \n, \r, \u2028, \u2029
whitespace,
rvalue,
// C only keywords
inline_,

View file

@ -544,7 +544,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
}
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);
@ -562,8 +564,14 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
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();
}

View file

@ -681,6 +681,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
* '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
@ -690,9 +691,10 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
* 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;
@ -820,7 +822,7 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis
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();
@ -887,14 +889,15 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis
*
* 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);
@ -980,25 +983,28 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
*
* 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;
@ -1013,18 +1019,47 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
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;
@ -4427,6 +4462,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
*/
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;
@ -5047,8 +5086,41 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
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())
@ -6039,7 +6111,7 @@ Dsymbol toDsymbol(Type type, Scope* sc)
Dsymbol visitIdentifier(TypeIdentifier type)
{
//printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
//printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type));
if (!sc)
return null;
@ -6051,7 +6123,6 @@ Dsymbol toDsymbol(Type type, Scope* sc)
s = t.toDsymbol(sc);
if (e)
s = getDsymbol(e);
return s;
}

View file

@ -0,0 +1,19 @@
// { 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;
}

View file

@ -0,0 +1,28 @@
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);
}

View file

@ -564,6 +564,11 @@ ref int* foo(scope return ref int* a) @safe
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
{

View file

@ -0,0 +1,22 @@
/* 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);
}

View file

@ -13,6 +13,10 @@ class A {
// 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{
@ -36,3 +40,12 @@ void func() {
// 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();
}

View file

@ -1,9 +1,7 @@
// 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
---
*/

View file

@ -137,6 +137,8 @@ struct DisabledPostblit
struct NoCpCtor { }
class C19902 { }
struct MoveCtor { this(MoveCtor) { } }
static assert(__traits(hasCopyConstructor, S));
static assert(__traits(hasCopyConstructor, OuterS.S));
static assert(__traits(hasCopyConstructor, OuterS));
@ -147,6 +149,8 @@ static assert(__traits(hasCopyConstructor, U!S));
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));

View file

@ -1,20 +0,0 @@
/*
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) {}
}

View file

@ -1,15 +0,0 @@
/*
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) {}
}

View file

@ -1,22 +0,0 @@
// 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;
}

View file

@ -1,8 +1,9 @@
/*
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
---
*/

View file

@ -2,7 +2,8 @@
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`
---
*/

View file

@ -53,10 +53,10 @@ public private void f10() {}
/*
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)`
---
*/

View file

@ -0,0 +1,15 @@
/*
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();
}

View file

@ -1,6 +1,7 @@
/* 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`
---
*/

View file

@ -1,8 +1,9 @@
/* 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

View file

@ -15,7 +15,7 @@ template Qual(alias T)
alias Qual = T;
}
void test()
void test1()
{
int x = 3;
int y = 4;
@ -25,7 +25,33 @@ void test()
assert(XY[1] == 4);
}
void main()
/**********************************************/
struct T
{
test();
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()
{
test1();
test2();
return 0;
}

View file

@ -0,0 +1,13 @@
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;
}

View file

@ -2491,6 +2491,62 @@ void testDoWhileContinue()
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()
@ -2592,6 +2648,9 @@ int main()
test21816();
test21835();
testDoWhileContinue();
test20574();
test8();
test9();
printf("Success\n");
return 0;

View file

@ -22,9 +22,35 @@ void test1()
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();
}

View file

@ -0,0 +1,209 @@
/* 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;
}

View file

@ -0,0 +1,13 @@
// 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;
}

View file

@ -0,0 +1,10 @@
// 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);
}

View file

@ -1,4 +1,4 @@
82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
c57da0cf5945cfb45eed06f1fd820435cda3ee3a
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.

View file

@ -78,6 +78,8 @@ else version (DigitalMars)
return val;
}
/// Execute target dependent trap instruction, if supported.
/// Otherwise, abort execution.
pragma(inline, true)
void trap()
{
@ -90,7 +92,8 @@ else version (DigitalMars)
}
}
/// 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); }

View file

@ -190,4 +190,73 @@ interface GC
* 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;
}

View file

@ -264,6 +264,8 @@ void *__arrayStart()(return scope BlkInfo info) nothrow pure
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);
}

View file

@ -38,7 +38,7 @@ else
int __nextBlkIdx;
}
@property BlkInfo *__blkcache() nothrow
@property BlkInfo *__blkcache() nothrow @nogc
{
if (!__blkcache_storage)
{
@ -135,7 +135,7 @@ unittest
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)
@ -175,7 +175,7 @@ BlkInfo *__getBlkInfo(void *interior) nothrow
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)
@ -241,3 +241,16 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
}
}
}
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);
}
}
}

View file

@ -71,12 +71,15 @@ size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
*/
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;
@ -113,11 +116,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
// 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)
{
@ -144,11 +142,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
// 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
{
@ -175,29 +168,80 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
// 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.
*/
size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
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) 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

View file

@ -1377,6 +1377,193 @@ class ConservativeGC : GC
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;
}
}

View file

@ -267,4 +267,24 @@ class ManualGC : GC
{
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;
}
}

View file

@ -241,4 +241,24 @@ class ProtoGC : GC
{
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;
}
}

View file

@ -246,6 +246,26 @@ extern (C)
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;

View file

@ -538,18 +538,173 @@ unittest
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))

View file

@ -475,6 +475,7 @@ else version (Darwin)
enum F_UNLCK = 2;
enum F_WRLCK = 3;
enum O_NOFOLLOW = 0x0100;
enum O_CREAT = 0x0200;
enum O_EXCL = 0x0800;
enum O_NOCTTY = 0;

View file

@ -15,7 +15,7 @@ pragma(lib, "advapi32");
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,

View file

@ -114,7 +114,7 @@ alias ISecurityInformation LPSECURITYINFO;
// 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);
}

View file

@ -5014,7 +5014,7 @@ BOOL Animate_Seek(HWND hwnd, int frame) {
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);
@ -5068,7 +5068,7 @@ BOOL DateTime_SetSystemtime(HWND hwnd, WPARAM flag, LPSYSTEMTIME lpSysTime) {
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);
@ -5142,7 +5142,7 @@ static if (_WIN32_IE >= 0x400) {
}
}
extern (Windows) {
extern (Windows) nothrow @nogc {
HDSA DSA_Create(INT, INT);
BOOL DSA_Destroy(HDSA);
VOID DSA_DestroyCallback(HDSA, PFNDSAENUMCALLBACK, PVOID);
@ -5758,7 +5758,7 @@ BOOL MonthCal_SetRange(HWND w, DWORD f, LPSYSTEMTIME st) {
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);
@ -6069,7 +6069,7 @@ static if (_WIN32_IE >= 0x300) {
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*);

View file

@ -99,6 +99,7 @@ extern (Windows) {
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);
}

View file

@ -148,7 +148,7 @@ deprecated struct DDEUP {
@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);

View file

@ -332,7 +332,7 @@ struct MONMSGSTRUCT {
}
alias MONMSGSTRUCT* PMONMSGSTRUCT;
extern (Windows) {
extern (Windows) nothrow @nogc {
BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD);
PBYTE DdeAccessData(HDDEDATA, PDWORD);
HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD);

View file

@ -45,7 +45,7 @@ struct DHCPCAPI_PARAMS_ARRAY {
}
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);

View file

@ -29,7 +29,7 @@ enum EFaultRepRetVal {
frrvOkHeadless // = 7
}
extern (Windows) {
extern (Windows) nothrow @nogc {
BOOL AddERExcludedApplicationA(LPCSTR);
BOOL AddERExcludedApplicationW(LPCWSTR);
EFaultRepRetVal ReportFault(LPEXCEPTION_POINTERS, DWORD);

View file

@ -109,7 +109,7 @@ struct HSE_SEND_HEADER_EX_INFO {
}
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);

View file

@ -295,6 +295,7 @@ extern (Windows) {
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);

View file

@ -70,7 +70,7 @@ interface IUniformResourceLocator : IUnknown {
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);

View file

@ -13,7 +13,7 @@ version (Windows):
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);

View file

@ -708,7 +708,7 @@ struct NETLOGON_INFO_3{
}
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);

View file

@ -71,7 +71,7 @@ struct USER_OTHER_INFO{
}
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);
}

View file

@ -12,7 +12,7 @@ pragma(lib, "netapi32");
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*);

View file

@ -32,7 +32,7 @@ struct MSG_INFO_1 {
}
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);

View file

@ -52,7 +52,7 @@ struct TIME_OF_DAY_INFO {
}
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,

View file

@ -29,7 +29,7 @@ enum MGMCTL_SETAGENTPORT = 1;
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);

View file

@ -19,4 +19,4 @@ enum SHUTDOWN_ACTION {
ShutdownPowerOff
}
extern (Windows) uint NtShutdownSystem(SHUTDOWN_ACTION Action);
extern (Windows) nothrow @nogc uint NtShutdownSystem(SHUTDOWN_ACTION Action);

View file

@ -726,7 +726,7 @@ struct TRUSTED_DOMAIN_FULL_INFORMATION {
}
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,

View file

@ -283,7 +283,7 @@ struct OLESERVERDOC {
}
alias OLESERVERDOC* LPOLESERVERDOC;
extern (Windows) {
extern (Windows) nothrow @nogc {
OLESTATUS OleDelete(LPOLEOBJECT);
OLESTATUS OleRelease(LPOLEOBJECT);
OLESTATUS OleSaveToStream(LPOLEOBJECT, LPOLESTREAM);

View file

@ -48,7 +48,7 @@ extern (Windows) {
}
alias OLESTREAMVTBL* LPOLESTREAMVTBL;
extern (Windows) {
extern (Windows) nothrow @nogc {
HRESULT CreateDataAdviseHolder(LPDATAADVISEHOLDER*);
DWORD OleBuildVersion();
HRESULT ReadClassStg(LPSTORAGE, CLSID*);

View file

@ -186,7 +186,7 @@ interface IAccessible : IDispatch {
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*);

View file

@ -226,7 +226,7 @@ deprecated { // not actually deprecated, but they aren't converted yet.
alias ICreateTypeLib2 LPCREATETYPELIB2;
}
extern (Windows) {
extern (Windows) nothrow @nogc {
BSTR SysAllocString(const(OLECHAR)*);
int SysReAllocString(BSTR*, const(OLECHAR)*);
BSTR SysAllocStringLen(const(OLECHAR)*, uint);

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