mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +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.
|
||||
* Input:
|
||||
* exps aray of Expressions
|
||||
* Output:
|
||||
* exps rewritten in place
|
||||
* Expand tuples in-place.
|
||||
*
|
||||
* Example:
|
||||
* When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40))`, the input is:
|
||||
* 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");
|
||||
if (exps is null)
|
||||
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++)
|
||||
{
|
||||
Expression arg = (*exps)[i];
|
||||
|
@ -275,6 +320,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
|||
if (!tt.arguments || tt.arguments.length == 0)
|
||||
{
|
||||
exps.remove(i);
|
||||
expandNames(i, 0);
|
||||
if (i == exps.length)
|
||||
return;
|
||||
}
|
||||
|
@ -285,6 +331,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
|||
foreach (j, a; *tt.arguments)
|
||||
(*texps)[j] = new TypeExp(e.loc, a.type);
|
||||
exps.insert(i, texps);
|
||||
expandNames(i, texps.length);
|
||||
}
|
||||
i--;
|
||||
continue;
|
||||
|
@ -297,6 +344,7 @@ extern (C++) void expandTuples(Expressions* exps)
|
|||
TupleExp te = cast(TupleExp)arg;
|
||||
exps.remove(i); // remove arg
|
||||
exps.insert(i, te.exps); // replace with tuple contents
|
||||
expandNames(i, te.exps.length);
|
||||
if (i == exps.length)
|
||||
return; // empty tuple, no more arguments
|
||||
(*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
|
||||
|
@ -5079,16 +5127,18 @@ extern (C++) final class DotTypeExp : UnaExp
|
|||
extern (C++) final class CallExp : UnaExp
|
||||
{
|
||||
Expressions* arguments; // function arguments
|
||||
Identifiers* names; // named argument identifiers
|
||||
FuncDeclaration f; // symbol to call
|
||||
bool directcall; // true if a virtual call is devirtualized
|
||||
bool inDebugStatement; /// true if this was in a debug statement
|
||||
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
|
||||
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);
|
||||
this.arguments = exps;
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
extern (D) this(const ref Loc loc, Expression e)
|
||||
|
@ -5155,7 +5205,7 @@ extern (C++) final class CallExp : UnaExp
|
|||
|
||||
override CallExp syntaxCopy()
|
||||
{
|
||||
return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
|
||||
return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments), names);
|
||||
}
|
||||
|
||||
override bool isLvalue()
|
||||
|
|
|
@ -45,7 +45,7 @@ typedef union tree_node Symbol;
|
|||
struct Symbol; // back end symbol
|
||||
#endif
|
||||
|
||||
void expandTuples(Expressions *exps);
|
||||
void expandTuples(Expressions *exps, Identifiers *names = nullptr);
|
||||
bool isTrivialExp(Expression *e);
|
||||
bool hasSideEffect(Expression *e, bool assumeImpureCalls = false);
|
||||
|
||||
|
@ -826,6 +826,7 @@ class CallExp final : public UnaExp
|
|||
{
|
||||
public:
|
||||
Expressions *arguments; // function arguments
|
||||
Identifiers *names;
|
||||
FuncDeclaration *f; // symbol to call
|
||||
bool directcall; // true if a virtual call is devirtualized
|
||||
bool inDebugStatement; // true if this was in a debug statement
|
||||
|
|
|
@ -651,6 +651,9 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
|
|||
if (!ce.arguments)
|
||||
ce.arguments = new Expressions();
|
||||
ce.arguments.shift(eleft);
|
||||
if (!ce.names)
|
||||
ce.names = new Identifiers();
|
||||
ce.names.shift(null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1629,12 +1632,12 @@ private Expression rewriteOpAssign(BinExp exp)
|
|||
* Returns:
|
||||
* 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;
|
||||
if (exps)
|
||||
{
|
||||
expandTuples(exps);
|
||||
expandTuples(exps, names);
|
||||
|
||||
for (size_t i = 0; i < exps.length; i++)
|
||||
{
|
||||
|
@ -3549,7 +3552,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
{
|
||||
return setError();
|
||||
}
|
||||
if (preFunctionParameters(sc, exp.arguments))
|
||||
if (preFunctionParameters(sc, /*names*/ null, exp.arguments))
|
||||
{
|
||||
return setError();
|
||||
}
|
||||
|
@ -4287,7 +4290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
if (FuncExp fe = exp.e1.isFuncExp())
|
||||
{
|
||||
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
||||
preFunctionParameters(sc, exp.arguments))
|
||||
preFunctionParameters(sc, exp.names, exp.arguments))
|
||||
return setError();
|
||||
|
||||
// Run e1 semantic even if arguments have any errors
|
||||
|
@ -4526,7 +4529,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
return;
|
||||
}
|
||||
if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
|
||||
preFunctionParameters(sc, exp.arguments))
|
||||
preFunctionParameters(sc, exp.names, exp.arguments))
|
||||
return setError();
|
||||
|
||||
// Check for call operator overload
|
||||
|
@ -4624,7 +4627,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
/* It's a struct literal
|
||||
*/
|
||||
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);
|
||||
result = e;
|
||||
return;
|
||||
|
|
|
@ -6874,7 +6874,7 @@ public:
|
|||
void accept(Visitor* v) override;
|
||||
};
|
||||
|
||||
extern void expandTuples(Array<Expression* >* exps);
|
||||
extern void expandTuples(Array<Expression* >* exps, Array<Identifier* >* names = nullptr);
|
||||
|
||||
struct UnionExp final
|
||||
{
|
||||
|
@ -7422,6 +7422,7 @@ class CallExp final : public UnaExp
|
|||
{
|
||||
public:
|
||||
Array<Expression* >* arguments;
|
||||
Array<Identifier* >* names;
|
||||
FuncDeclaration* f;
|
||||
bool directcall;
|
||||
bool inDebugStatement;
|
||||
|
|
|
@ -135,111 +135,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
sd.size(i.loc);
|
||||
if (sd.sizeok != Sizeok.done)
|
||||
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[])
|
||||
Expression getExp(size_t j, Type fieldType)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
errors = true;
|
||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||
++fieldi;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ex.op != EXP.error)
|
||||
i.value[j] = iz;
|
||||
elems[fieldi] = doCopyOrMove(sc, ex);
|
||||
++fieldi;
|
||||
return ex;
|
||||
}
|
||||
if (errors)
|
||||
auto elements = resolveStructLiteralNamedArgs(sd, t, sc, i.loc, i.field[], &getExp, (size_t j) => i.value[j].loc);
|
||||
if (!elements)
|
||||
return err();
|
||||
|
||||
// Make a StructLiteralExp out of elements[]
|
||||
|
@ -1514,3 +1422,141 @@ private bool hasNonConstPointers(Expression e)
|
|||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -8912,7 +8915,10 @@ LagainStc:
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
case TOK.leftBracket:
|
||||
|
@ -9347,26 +9353,52 @@ LagainStc:
|
|||
private AST.Expressions* parseArguments()
|
||||
{
|
||||
// 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;
|
||||
|
||||
nextToken();
|
||||
|
||||
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();
|
||||
arguments.push(arg);
|
||||
|
||||
if (token.value != TOK.comma)
|
||||
break;
|
||||
|
||||
nextToken(); //comma
|
||||
}
|
||||
|
||||
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:
|
||||
---
|
||||
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:
|
||||
---
|
||||
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
|
||||
---
|
||||
*/
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
/*
|
||||
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(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
|
||||
---
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
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:
|
||||
---
|
||||
fail_compilation/fail22570.d(19): Error: more initializers than fields (1) of `S`
|
||||
fail_compilation/fail22570.d(20): 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: too many initializers for `S` with 1 field
|
||||
---
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
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`
|
||||
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" };
|
||||
^
|
||||
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`
|
||||
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 };
|
||||
^
|
||||
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`
|
||||
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 };
|
||||
^
|
||||
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`
|
||||
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 };
|
||||
^
|
||||
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`
|
||||
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 };
|
||||
^
|
||||
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 };
|
||||
^
|
||||
fail_compilation/test20998.d(116): Error: cannot implicitly convert expression `1` of type `int` to `immutable(char*)`
|
||||
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};
|
||||
^
|
||||
fail_compilation/test20998.d(120): called from here: `test2()`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue