mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 21:21:48 +03:00
Add named arguments to struct literals (#14776)
* Add named argument parsing * Refactor StructInitializer semantic to make it reusable * Move out `resolveStructLiteralNamedArgs` * Resolve named args in struct literal * Add tests * Check for errors returned by `resolveStructLiteralNamedArgs` * Expand names along with tuples * Update error messages in tests * Convert tabs to spaces in named arg test * Fix style of `resolveStructLiteralNamedArgs` * Clarify 'fixme' comment in expandTuples * Add example to `expandTuples` documentation * Update compiler/src/dmd/initsem.d Co-authored-by: Razvan Nitu <razvan.nitu1305@gmail.com> * Add overlapping initialization supplemental error to Struct Initializer * Improve "too many initializers" error message Co-authored-by: Razvan Nitu <razvan.nitu1305@gmail.com>
This commit is contained in:
parent
a15cf38f92
commit
42609ae98e
15 changed files with 329 additions and 141 deletions
|
@ -249,18 +249,63 @@ bool isDotOpDispatch(Expression e)
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* Expand tuples.
|
* Expand tuples in-place.
|
||||||
* Input:
|
*
|
||||||
* exps aray of Expressions
|
* Example:
|
||||||
* Output:
|
* When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40))`, the input is:
|
||||||
* exps rewritten in place
|
* exps = [10, (20, 30), 40]
|
||||||
|
* names = [null, "pair", "single"]);
|
||||||
|
* The arrays will be modified to:
|
||||||
|
* exps = [10, 20, 30, 40]
|
||||||
|
* names = [null, "pair", null, "single"]);
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* exps = array of Expressions
|
||||||
|
* names = optional array of names corresponding to Expressions
|
||||||
*/
|
*/
|
||||||
extern (C++) void expandTuples(Expressions* exps)
|
extern (C++) void expandTuples(Expressions* exps, Identifiers* names = null)
|
||||||
{
|
{
|
||||||
//printf("expandTuples()\n");
|
//printf("expandTuples()\n");
|
||||||
if (exps is null)
|
if (exps is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (names)
|
||||||
|
{
|
||||||
|
if (exps.length != names.length)
|
||||||
|
{
|
||||||
|
// Fixme: this branch should be removed when CallExp rewrites
|
||||||
|
// (UFCS/operator overloading) are fixed to take named arguments into account
|
||||||
|
names.setDim(exps.length);
|
||||||
|
foreach (i; 0..exps.length)
|
||||||
|
(*names)[i] = null;
|
||||||
|
}
|
||||||
|
if (exps.length != names.length)
|
||||||
|
{
|
||||||
|
printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
|
||||||
|
printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
|
||||||
|
if (exps.length > 0)
|
||||||
|
printf("%s\n", (*exps)[0].loc.toChars());
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
|
||||||
|
void expandNames(size_t index, size_t length)
|
||||||
|
{
|
||||||
|
if (names)
|
||||||
|
{
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
names.remove(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (i; 1 .. length)
|
||||||
|
{
|
||||||
|
names.insert(index + i, cast(Identifier) null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < exps.length; i++)
|
for (size_t i = 0; i < exps.length; i++)
|
||||||
{
|
{
|
||||||
Expression arg = (*exps)[i];
|
Expression arg = (*exps)[i];
|
||||||
|
@ -275,6 +320,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
||||||
if (!tt.arguments || tt.arguments.length == 0)
|
if (!tt.arguments || tt.arguments.length == 0)
|
||||||
{
|
{
|
||||||
exps.remove(i);
|
exps.remove(i);
|
||||||
|
expandNames(i, 0);
|
||||||
if (i == exps.length)
|
if (i == exps.length)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -285,6 +331,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
||||||
foreach (j, a; *tt.arguments)
|
foreach (j, a; *tt.arguments)
|
||||||
(*texps)[j] = new TypeExp(e.loc, a.type);
|
(*texps)[j] = new TypeExp(e.loc, a.type);
|
||||||
exps.insert(i, texps);
|
exps.insert(i, texps);
|
||||||
|
expandNames(i, texps.length);
|
||||||
}
|
}
|
||||||
i--;
|
i--;
|
||||||
continue;
|
continue;
|
||||||
|
@ -297,6 +344,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
||||||
TupleExp te = cast(TupleExp)arg;
|
TupleExp te = cast(TupleExp)arg;
|
||||||
exps.remove(i); // remove arg
|
exps.remove(i); // remove arg
|
||||||
exps.insert(i, te.exps); // replace with tuple contents
|
exps.insert(i, te.exps); // replace with tuple contents
|
||||||
|
expandNames(i, te.exps.length);
|
||||||
if (i == exps.length)
|
if (i == exps.length)
|
||||||
return; // empty tuple, no more arguments
|
return; // empty tuple, no more arguments
|
||||||
(*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
|
(*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
|
||||||
|
@ -5079,16 +5127,18 @@ extern (C++) final class DotTypeExp : UnaExp
|
||||||
extern (C++) final class CallExp : UnaExp
|
extern (C++) final class CallExp : UnaExp
|
||||||
{
|
{
|
||||||
Expressions* arguments; // function arguments
|
Expressions* arguments; // function arguments
|
||||||
|
Identifiers* names; // named argument identifiers
|
||||||
FuncDeclaration f; // symbol to call
|
FuncDeclaration f; // symbol to call
|
||||||
bool directcall; // true if a virtual call is devirtualized
|
bool directcall; // true if a virtual call is devirtualized
|
||||||
bool inDebugStatement; /// true if this was in a debug statement
|
bool inDebugStatement; /// true if this was in a debug statement
|
||||||
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
|
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
|
||||||
VarDeclaration vthis2; // container for multi-context
|
VarDeclaration vthis2; // container for multi-context
|
||||||
|
|
||||||
extern (D) this(const ref Loc loc, Expression e, Expressions* exps)
|
extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null)
|
||||||
{
|
{
|
||||||
super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
|
super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
|
||||||
this.arguments = exps;
|
this.arguments = exps;
|
||||||
|
this.names = names;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern (D) this(const ref Loc loc, Expression e)
|
extern (D) this(const ref Loc loc, Expression e)
|
||||||
|
@ -5155,7 +5205,7 @@ extern (C++) final class CallExp : UnaExp
|
||||||
|
|
||||||
override CallExp syntaxCopy()
|
override CallExp syntaxCopy()
|
||||||
{
|
{
|
||||||
return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
|
return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments), names);
|
||||||
}
|
}
|
||||||
|
|
||||||
override bool isLvalue()
|
override bool isLvalue()
|
||||||
|
|
|
@ -45,7 +45,7 @@ typedef union tree_node Symbol;
|
||||||
struct Symbol; // back end symbol
|
struct Symbol; // back end symbol
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void expandTuples(Expressions *exps);
|
void expandTuples(Expressions *exps, Identifiers *names = nullptr);
|
||||||
bool isTrivialExp(Expression *e);
|
bool isTrivialExp(Expression *e);
|
||||||
bool hasSideEffect(Expression *e, bool assumeImpureCalls = false);
|
bool hasSideEffect(Expression *e, bool assumeImpureCalls = false);
|
||||||
|
|
||||||
|
@ -826,6 +826,7 @@ class CallExp final : public UnaExp
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Expressions *arguments; // function arguments
|
Expressions *arguments; // function arguments
|
||||||
|
Identifiers *names;
|
||||||
FuncDeclaration *f; // symbol to call
|
FuncDeclaration *f; // symbol to call
|
||||||
bool directcall; // true if a virtual call is devirtualized
|
bool directcall; // true if a virtual call is devirtualized
|
||||||
bool inDebugStatement; // true if this was in a debug statement
|
bool inDebugStatement; // true if this was in a debug statement
|
||||||
|
|
|
@ -651,6 +651,9 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
|
||||||
if (!ce.arguments)
|
if (!ce.arguments)
|
||||||
ce.arguments = new Expressions();
|
ce.arguments = new Expressions();
|
||||||
ce.arguments.shift(eleft);
|
ce.arguments.shift(eleft);
|
||||||
|
if (!ce.names)
|
||||||
|
ce.names = new Identifiers();
|
||||||
|
ce.names.shift(null);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1629,12 +1632,12 @@ private Expression rewriteOpAssign(BinExp exp)
|
||||||
* Returns:
|
* Returns:
|
||||||
* true a semantic error occurred
|
* true a semantic error occurred
|
||||||
*/
|
*/
|
||||||
private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true)
|
private bool preFunctionParameters(Scope* sc, Identifiers* names, Expressions* exps, const bool reportErrors = true)
|
||||||
{
|
{
|
||||||
bool err = false;
|
bool err = false;
|
||||||
if (exps)
|
if (exps)
|
||||||
{
|
{
|
||||||
expandTuples(exps);
|
expandTuples(exps, names);
|
||||||
|
|
||||||
for (size_t i = 0; i < exps.length; i++)
|
for (size_t i = 0; i < exps.length; i++)
|
||||||
{
|
{
|
||||||
|
@ -3549,7 +3552,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
||||||
{
|
{
|
||||||
return setError();
|
return setError();
|
||||||
}
|
}
|
||||||
if (preFunctionParameters(sc, exp.arguments))
|
if (preFunctionParameters(sc, /*names*/ null, exp.arguments))
|
||||||
{
|
{
|
||||||
return setError();
|
return setError();
|
||||||
}
|
}
|
||||||
|
@ -4287,7 +4290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
||||||
if (FuncExp fe = exp.e1.isFuncExp())
|
if (FuncExp fe = exp.e1.isFuncExp())
|
||||||
{
|
{
|
||||||
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
||||||
preFunctionParameters(sc, exp.arguments))
|
preFunctionParameters(sc, exp.names, exp.arguments))
|
||||||
return setError();
|
return setError();
|
||||||
|
|
||||||
// Run e1 semantic even if arguments have any errors
|
// Run e1 semantic even if arguments have any errors
|
||||||
|
@ -4526,7 +4529,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
||||||
preFunctionParameters(sc, exp.arguments))
|
preFunctionParameters(sc, exp.names, exp.arguments))
|
||||||
return setError();
|
return setError();
|
||||||
|
|
||||||
// Check for call operator overload
|
// Check for call operator overload
|
||||||
|
@ -4624,7 +4627,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
||||||
/* It's a struct literal
|
/* It's a struct literal
|
||||||
*/
|
*/
|
||||||
Lx:
|
Lx:
|
||||||
Expression e = new StructLiteralExp(exp.loc, sd, exp.arguments, exp.e1.type);
|
Expressions* resolvedArgs = exp.arguments;
|
||||||
|
if (exp.names)
|
||||||
|
{
|
||||||
|
resolvedArgs = resolveStructLiteralNamedArgs(sd, exp.e1.type, sc, exp.loc,
|
||||||
|
(*exp.names)[],
|
||||||
|
(size_t i, Type t) => (*exp.arguments)[i],
|
||||||
|
i => (*exp.arguments)[i].loc
|
||||||
|
);
|
||||||
|
if (!resolvedArgs)
|
||||||
|
{
|
||||||
|
result = ErrorExp.get();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression e = new StructLiteralExp(exp.loc, sd, resolvedArgs, exp.e1.type);
|
||||||
e = e.expressionSemantic(sc);
|
e = e.expressionSemantic(sc);
|
||||||
result = e;
|
result = e;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6874,7 +6874,7 @@ public:
|
||||||
void accept(Visitor* v) override;
|
void accept(Visitor* v) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void expandTuples(Array<Expression* >* exps);
|
extern void expandTuples(Array<Expression* >* exps, Array<Identifier* >* names = nullptr);
|
||||||
|
|
||||||
struct UnionExp final
|
struct UnionExp final
|
||||||
{
|
{
|
||||||
|
@ -7422,6 +7422,7 @@ class CallExp final : public UnaExp
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Array<Expression* >* arguments;
|
Array<Expression* >* arguments;
|
||||||
|
Array<Identifier* >* names;
|
||||||
FuncDeclaration* f;
|
FuncDeclaration* f;
|
||||||
bool directcall;
|
bool directcall;
|
||||||
bool inDebugStatement;
|
bool inDebugStatement;
|
||||||
|
|
|
@ -135,111 +135,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
||||||
sd.size(i.loc);
|
sd.size(i.loc);
|
||||||
if (sd.sizeok != Sizeok.done)
|
if (sd.sizeok != Sizeok.done)
|
||||||
return err();
|
return err();
|
||||||
const nfields = sd.nonHiddenFields();
|
|
||||||
//expandTuples for non-identity arguments?
|
|
||||||
auto elements = new Expressions(nfields);
|
|
||||||
auto elems = (*elements)[];
|
|
||||||
foreach (ref elem; elems)
|
|
||||||
elem = null;
|
|
||||||
|
|
||||||
// Run semantic for explicitly given initializers
|
|
||||||
// TODO: this part is slightly different from StructLiteralExp::semantic.
|
|
||||||
bool errors = false;
|
|
||||||
size_t fieldi = 0;
|
|
||||||
foreach (j, id; i.field[])
|
|
||||||
{
|
|
||||||
if (id)
|
|
||||||
{
|
|
||||||
/* Determine `fieldi` that `id` matches
|
|
||||||
*/
|
|
||||||
Dsymbol s = sd.search(i.loc, id);
|
|
||||||
if (!s)
|
|
||||||
{
|
|
||||||
s = sd.search_correct(id);
|
|
||||||
const initLoc = i.value[j].loc;
|
|
||||||
if (s)
|
|
||||||
error(initLoc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars());
|
|
||||||
else
|
|
||||||
error(initLoc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars());
|
|
||||||
return err();
|
|
||||||
}
|
|
||||||
s.checkDeprecated(i.loc, sc);
|
|
||||||
s = s.toAlias();
|
|
||||||
|
|
||||||
// Find out which field index `s` is
|
|
||||||
for (fieldi = 0; 1; fieldi++)
|
|
||||||
{
|
|
||||||
if (fieldi >= nfields)
|
|
||||||
{
|
|
||||||
error(i.loc, "`%s.%s` is not a per-instance initializable field", sd.toChars(), s.toChars());
|
|
||||||
return err();
|
|
||||||
}
|
|
||||||
if (s == sd.fields[fieldi])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j >= nfields)
|
|
||||||
{
|
|
||||||
error(i.value[j].loc, "too many initializers for `%s`", sd.toChars());
|
|
||||||
return err();
|
|
||||||
}
|
|
||||||
|
|
||||||
VarDeclaration vd = sd.fields[fieldi];
|
|
||||||
if (elems[fieldi])
|
|
||||||
{
|
|
||||||
error(i.value[j].loc, "duplicate initializer for field `%s`", vd.toChars());
|
|
||||||
errors = true;
|
|
||||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
|
||||||
++fieldi;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for @safe violations
|
|
||||||
if (vd.type.hasPointers)
|
|
||||||
{
|
|
||||||
if ((!t.alignment.isDefault() && t.alignment.get() < target.ptrsize ||
|
|
||||||
(vd.offset & (target.ptrsize - 1))))
|
|
||||||
{
|
|
||||||
if (sc.setUnsafe(false, i.value[j].loc,
|
|
||||||
"field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, vd))
|
|
||||||
{
|
|
||||||
errors = true;
|
|
||||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
|
||||||
++fieldi;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for overlapping initializations (can happen with unions)
|
|
||||||
foreach (k, v2; sd.fields[0 .. nfields])
|
|
||||||
{
|
|
||||||
if (vd.isOverlappedWith(v2) && elems[k])
|
|
||||||
{
|
|
||||||
error(elems[k].loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
|
||||||
errors = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert initializer to Expression `ex`
|
|
||||||
assert(sc);
|
|
||||||
auto tm = vd.type.addMod(t.mod);
|
|
||||||
auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
|
|
||||||
auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
|
|
||||||
if (ex.op == EXP.error)
|
|
||||||
{
|
|
||||||
errors = true;
|
|
||||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
|
||||||
++fieldi;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Expression getExp(size_t j, Type fieldType)
|
||||||
|
{
|
||||||
|
// Convert initializer to Expression `ex`
|
||||||
|
auto tm = fieldType.addMod(t.mod);
|
||||||
|
auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
|
||||||
|
auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
|
||||||
|
if (ex.op != EXP.error)
|
||||||
i.value[j] = iz;
|
i.value[j] = iz;
|
||||||
elems[fieldi] = doCopyOrMove(sc, ex);
|
return ex;
|
||||||
++fieldi;
|
}
|
||||||
}
|
auto elements = resolveStructLiteralNamedArgs(sd, t, sc, i.loc, i.field[], &getExp, (size_t j) => i.value[j].loc);
|
||||||
if (errors)
|
if (!elements)
|
||||||
return err();
|
return err();
|
||||||
|
|
||||||
// Make a StructLiteralExp out of elements[]
|
// Make a StructLiteralExp out of elements[]
|
||||||
|
@ -1514,3 +1422,141 @@ private bool hasNonConstPointers(Expression e)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given the names and values of a `StructInitializer` or `CallExp`,
|
||||||
|
resolve it to a list of expressions to construct a `StructLiteralExp`.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
sd = struct
|
||||||
|
t = type of struct (potentially including qualifiers such as `const` or `immutable`)
|
||||||
|
sc = scope of the expression initializing the struct
|
||||||
|
iloc = location of expression initializing the struct
|
||||||
|
names = identifiers passed in argument list, `null` entries for positional arguments
|
||||||
|
getExp = function that, given an index into `names` and destination type, returns the initializing expression
|
||||||
|
getLoc = function that, given an index into `names`, returns a location for error messages
|
||||||
|
|
||||||
|
Returns: list of expressions ordered to the struct's fields, or `null` on error
|
||||||
|
*/
|
||||||
|
Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* sc,
|
||||||
|
Loc iloc, Identifier[] names, scope Expression delegate(size_t i, Type fieldType) getExp,
|
||||||
|
scope Loc delegate(size_t i) getLoc
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//expandTuples for non-identity arguments?
|
||||||
|
const nfields = sd.nonHiddenFields();
|
||||||
|
auto elements = new Expressions(nfields);
|
||||||
|
auto elems = (*elements)[];
|
||||||
|
foreach (ref elem; elems)
|
||||||
|
elem = null;
|
||||||
|
|
||||||
|
// Run semantic for explicitly given initializers
|
||||||
|
// TODO: this part is slightly different from StructLiteralExp::semantic.
|
||||||
|
bool errors = false;
|
||||||
|
size_t fieldi = 0;
|
||||||
|
foreach (j, id; names)
|
||||||
|
{
|
||||||
|
const argLoc = getLoc(j);
|
||||||
|
if (id)
|
||||||
|
{
|
||||||
|
// Determine `fieldi` that `id` matches
|
||||||
|
Dsymbol s = sd.search(iloc, id);
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
s = sd.search_correct(id);
|
||||||
|
if (s)
|
||||||
|
error(argLoc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars());
|
||||||
|
else
|
||||||
|
error(argLoc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
s.checkDeprecated(iloc, sc);
|
||||||
|
s = s.toAlias();
|
||||||
|
|
||||||
|
// Find out which field index `s` is
|
||||||
|
for (fieldi = 0; 1; fieldi++)
|
||||||
|
{
|
||||||
|
if (fieldi >= nfields)
|
||||||
|
{
|
||||||
|
error(iloc, "`%s.%s` is not a per-instance initializable field", sd.toChars(), s.toChars());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (s == sd.fields[fieldi])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nfields == 0)
|
||||||
|
{
|
||||||
|
error(argLoc, "initializer provided for struct `%s` with no fields", sd.toChars());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (j >= nfields)
|
||||||
|
{
|
||||||
|
error(argLoc, "too many initializers for `%s` with %d field%s", sd.toChars(),
|
||||||
|
cast(int) nfields, nfields != 1 ? "s".ptr : "".ptr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VarDeclaration vd = sd.fields[fieldi];
|
||||||
|
if (elems[fieldi])
|
||||||
|
{
|
||||||
|
error(argLoc, "duplicate initializer for field `%s`", vd.toChars());
|
||||||
|
errors = true;
|
||||||
|
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||||
|
++fieldi;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for @safe violations
|
||||||
|
if (vd.type.hasPointers)
|
||||||
|
{
|
||||||
|
if ((!t.alignment.isDefault() && t.alignment.get() < target.ptrsize ||
|
||||||
|
(vd.offset & (target.ptrsize - 1))))
|
||||||
|
{
|
||||||
|
if (sc.setUnsafe(false, argLoc,
|
||||||
|
"field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, vd))
|
||||||
|
{
|
||||||
|
errors = true;
|
||||||
|
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||||
|
++fieldi;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlapping initializations (can happen with unions)
|
||||||
|
foreach (k, v2; sd.fields[0 .. nfields])
|
||||||
|
{
|
||||||
|
if (vd.isOverlappedWith(v2) && elems[k])
|
||||||
|
{
|
||||||
|
error(elems[k].loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
||||||
|
enum errorMsg = "`struct` initializers that contain anonymous unions" ~
|
||||||
|
" must initialize only the first member of a `union`. All subsequent" ~
|
||||||
|
" non-overlapping fields are default initialized";
|
||||||
|
if (!sd.isUnionDeclaration())
|
||||||
|
.errorSupplemental(elems[k].loc, errorMsg);
|
||||||
|
errors = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sc);
|
||||||
|
|
||||||
|
auto ex = getExp(j, vd.type);
|
||||||
|
|
||||||
|
if (ex.op == EXP.error)
|
||||||
|
{
|
||||||
|
errors = true;
|
||||||
|
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||||
|
++fieldi;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
elems[fieldi] = doCopyOrMove(sc, ex);
|
||||||
|
++fieldi;
|
||||||
|
}
|
||||||
|
if (errors)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
|
@ -1285,7 +1285,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
|
||||||
if (token.value == TOK.leftParenthesis)
|
if (token.value == TOK.leftParenthesis)
|
||||||
{
|
{
|
||||||
const loc = token.loc;
|
const loc = token.loc;
|
||||||
exp = new AST.CallExp(loc, exp, parseArguments());
|
AST.Expressions* args = new AST.Expressions();
|
||||||
|
AST.Identifiers* names = new AST.Identifiers();
|
||||||
|
parseNamedArguments(args, names);
|
||||||
|
exp = new AST.CallExp(loc, exp, args, names);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udas is null)
|
if (udas is null)
|
||||||
|
@ -8912,7 +8915,10 @@ LagainStc:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOK.leftParenthesis:
|
case TOK.leftParenthesis:
|
||||||
e = new AST.CallExp(loc, e, parseArguments());
|
AST.Expressions* args = new AST.Expressions();
|
||||||
|
AST.Identifiers* names = new AST.Identifiers();
|
||||||
|
parseNamedArguments(args, names);
|
||||||
|
e = new AST.CallExp(loc, e, args, names);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case TOK.leftBracket:
|
case TOK.leftBracket:
|
||||||
|
@ -9347,26 +9353,52 @@ LagainStc:
|
||||||
private AST.Expressions* parseArguments()
|
private AST.Expressions* parseArguments()
|
||||||
{
|
{
|
||||||
// function call
|
// function call
|
||||||
AST.Expressions* arguments;
|
AST.Expressions* arguments = new AST.Expressions();
|
||||||
|
parseNamedArguments(arguments, null);
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* Collect argument list.
|
||||||
|
* Assume current token is ',', '$(LPAREN)' or '['.
|
||||||
|
*/
|
||||||
|
private void parseNamedArguments(AST.Expressions* arguments, AST.Identifiers* names)
|
||||||
|
{
|
||||||
|
assert(arguments);
|
||||||
|
|
||||||
arguments = new AST.Expressions();
|
|
||||||
const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
|
const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
|
||||||
|
|
||||||
nextToken();
|
nextToken();
|
||||||
|
|
||||||
while (token.value != endtok && token.value != TOK.endOfFile)
|
while (token.value != endtok && token.value != TOK.endOfFile)
|
||||||
{
|
{
|
||||||
|
if (peekNext() == TOK.colon)
|
||||||
|
{
|
||||||
|
// Named argument `name: exp`
|
||||||
|
auto loc = token.loc;
|
||||||
|
auto ident = token.ident;
|
||||||
|
check(TOK.identifier);
|
||||||
|
check(TOK.colon);
|
||||||
|
if (names)
|
||||||
|
names.push(ident);
|
||||||
|
else
|
||||||
|
error(loc, "named arguments not allowed here");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (names)
|
||||||
|
names.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
auto arg = parseAssignExp();
|
auto arg = parseAssignExp();
|
||||||
arguments.push(arg);
|
arguments.push(arg);
|
||||||
|
|
||||||
if (token.value != TOK.comma)
|
if (token.value != TOK.comma)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nextToken(); //comma
|
nextToken(); //comma
|
||||||
}
|
}
|
||||||
|
|
||||||
check(endtok);
|
check(endtok);
|
||||||
|
|
||||||
return arguments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************
|
/*******************************************
|
||||||
|
|
24
compiler/test/compilable/named_argumens_struct.d
Normal file
24
compiler/test/compilable/named_argumens_struct.d
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
struct S
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
immutable S s = S(x: 2, 3, name: "boo");
|
||||||
|
|
||||||
|
static assert(s.x == 2);
|
||||||
|
static assert(s.y == 3);
|
||||||
|
static assert(s.name == "boo");
|
||||||
|
|
||||||
|
union U
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
int i;
|
||||||
|
}
|
||||||
|
|
||||||
|
immutable U u = U(i: 2);
|
||||||
|
|
||||||
|
static assert(u.i == 2);
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/diag11132.d(22): Error: overlapping initialization for field `a` and `b`
|
fail_compilation/diag11132.d(23): Error: overlapping initialization for field `a` and `b`
|
||||||
|
fail_compilation/diag11132.d(23): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/fail155.d(20): Error: overlapping initialization for `y`
|
fail_compilation/fail155.d(20): Error: overlapping initialization for field `x` and `y`
|
||||||
fail_compilation/fail155.d(20): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
fail_compilation/fail155.d(20): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/fail156.d(35): Error: overlapping initialization for `y`
|
fail_compilation/fail156.d(35): Error: overlapping initialization for field `x` and `y`
|
||||||
fail_compilation/fail156.d(35): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
fail_compilation/fail156.d(35): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
||||||
fail_compilation/fail156.d(42): Error: overlapping initialization for `y`
|
fail_compilation/fail156.d(42): Error: overlapping initialization for field `x` and `y`
|
||||||
fail_compilation/fail156.d(42): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
fail_compilation/fail156.d(42): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/fail158.d(17): Error: more initializers than fields (2) of `S`
|
fail_compilation/fail158.d(17): Error: too many initializers for `S` with 2 fields
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/fail22570.d(19): Error: more initializers than fields (1) of `S`
|
fail_compilation/fail22570.d(19): Error: too many initializers for `S` with 1 field
|
||||||
fail_compilation/fail22570.d(20): Error: more initializers than fields (1) of `S`
|
fail_compilation/fail22570.d(20): Error: too many initializers for `S` with 1 field
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
TEST_OUTPUT:
|
TEST_OUTPUT:
|
||||||
---
|
---
|
||||||
fail_compilation/fail299.d(14): Error: more initializers than fields (0) of `Foo`
|
fail_compilation/fail299.d(14): Error: initializer provided for struct `Foo` with no fields
|
||||||
---
|
---
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
15
compiler/test/fail_compilation/named_arguments_parse.d
Normal file
15
compiler/test/fail_compilation/named_arguments_parse.d
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
TEST_OUTPUT:
|
||||||
|
---
|
||||||
|
fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here
|
||||||
|
fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here
|
||||||
|
fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here
|
||||||
|
---
|
||||||
|
*/
|
||||||
|
|
||||||
|
@(attribute: 3)
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
mixin(thecode: "{}");
|
||||||
|
pragma(msg, themsg: "hello");
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ TEST_OUTPUT:
|
||||||
fail_compilation/test20998.d(76): Error: undefined identifier `invalid`
|
fail_compilation/test20998.d(76): Error: undefined identifier `invalid`
|
||||||
X x = { invalid, 2, "asd" };
|
X x = { invalid, 2, "asd" };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(76): Error: too many initializers for `X`
|
fail_compilation/test20998.d(76): Error: too many initializers for `X` with 2 fields
|
||||||
X x = { invalid, 2, "asd" };
|
X x = { invalid, 2, "asd" };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(83): Error: cannot implicitly convert expression `"a"` of type `string` to `int`
|
fail_compilation/test20998.d(83): Error: cannot implicitly convert expression `"a"` of type `string` to `int`
|
||||||
|
@ -15,7 +15,7 @@ X2 x2 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
fail_compilation/test20998.d(83): Error: duplicate initializer for field `ptr`
|
fail_compilation/test20998.d(83): Error: duplicate initializer for field `ptr`
|
||||||
X2 x2 = { ptr: null, "a", ptr: 2, 444 };
|
X2 x2 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(83): Error: too many initializers for `X2`
|
fail_compilation/test20998.d(83): Error: too many initializers for `X2` with 3 fields
|
||||||
X2 x2 = { ptr: null, "a", ptr: 2, 444 };
|
X2 x2 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(90): Error: overlapping initialization for field `ptr` and `x`
|
fail_compilation/test20998.d(90): Error: overlapping initialization for field `ptr` and `x`
|
||||||
|
@ -27,7 +27,7 @@ X3 x3 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
fail_compilation/test20998.d(90): Error: duplicate initializer for field `ptr`
|
fail_compilation/test20998.d(90): Error: duplicate initializer for field `ptr`
|
||||||
X3 x3 = { ptr: null, "a", ptr: 2, 444 };
|
X3 x3 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(90): Error: too many initializers for `X3`
|
fail_compilation/test20998.d(90): Error: too many initializers for `X3` with 3 fields
|
||||||
X3 x3 = { ptr: null, "a", ptr: 2, 444 };
|
X3 x3 = { ptr: null, "a", ptr: 2, 444 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(98): Error: field `X4.ptr` cannot assign to misaligned pointers in `@safe` code
|
fail_compilation/test20998.d(98): Error: field `X4.ptr` cannot assign to misaligned pointers in `@safe` code
|
||||||
|
@ -36,7 +36,7 @@ fail_compilation/test20998.d(98): Error: field `X4.ptr` cannot assign to misalig
|
||||||
fail_compilation/test20998.d(98): Error: cannot implicitly convert expression `"a"` of type `string` to `int`
|
fail_compilation/test20998.d(98): Error: cannot implicitly convert expression `"a"` of type `string` to `int`
|
||||||
X4 x4 = { ptr: null, "a", 444, ptr: 2, true };
|
X4 x4 = { ptr: null, "a", 444, ptr: 2, true };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(98): Error: too many initializers for `X4`
|
fail_compilation/test20998.d(98): Error: too many initializers for `X4` with 2 fields
|
||||||
X4 x4 = { ptr: null, "a", 444, ptr: 2, true };
|
X4 x4 = { ptr: null, "a", 444, ptr: 2, true };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(102): called from here: `test()`
|
fail_compilation/test20998.d(102): called from here: `test()`
|
||||||
|
@ -51,16 +51,16 @@ X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 };
|
||||||
fail_compilation/test20998.d(104): Error: duplicate initializer for field `ptr`
|
fail_compilation/test20998.d(104): Error: duplicate initializer for field `ptr`
|
||||||
X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 };
|
X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(104): Error: too many initializers for `X2`
|
fail_compilation/test20998.d(104): Error: too many initializers for `X2` with 3 fields
|
||||||
X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 };
|
X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(107): Error: too many initializers for `X2`
|
fail_compilation/test20998.d(107): Error: too many initializers for `X2` with 3 fields
|
||||||
X2 c6 = { null, 2, true, null };
|
X2 c6 = { null, 2, true, null };
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(116): Error: cannot implicitly convert expression `1` of type `int` to `immutable(char*)`
|
fail_compilation/test20998.d(116): Error: cannot implicitly convert expression `1` of type `int` to `immutable(char*)`
|
||||||
immutable Struct iStruct = {1, &ch};
|
immutable Struct iStruct = {1, &ch};
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(116): Error: too many initializers for `Struct`
|
fail_compilation/test20998.d(116): Error: too many initializers for `Struct` with 1 field
|
||||||
immutable Struct iStruct = {1, &ch};
|
immutable Struct iStruct = {1, &ch};
|
||||||
^
|
^
|
||||||
fail_compilation/test20998.d(120): called from here: `test2()`
|
fail_compilation/test20998.d(120): called from here: `test2()`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue