Allow default arguments in the middle (#14879)

This commit is contained in:
Dennis 2023-02-15 11:31:59 +01:00 committed by GitHub
parent eb57825c7c
commit 1d19f710b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 38 additions and 14 deletions

View file

@ -4146,9 +4146,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
{
}
else if (dim && tf.parameterList[0].defaultArg)
else if (dim && !tf.parameterList.hasArgsWithoutDefault)
{
// if the first parameter has a default argument, then the rest does as well
if (ctd.storage_class & STC.disable)
{
ctd.error("is marked `@disable`, so it cannot have default "~

View file

@ -4683,7 +4683,7 @@ extern (C++) final class TypeFunction : TypeNext
}
// https://issues.dlang.org/show_bug.cgi?id=22997
if (parameterList.varargs == VarArg.none && nparams > argumentList.length && !parameterList[argumentList.length].defaultArg)
if (parameterList.varargs == VarArg.none && nparams > argumentList.length && !parameterList.hasDefaultArgs)
{
OutBuffer buf;
buf.printf("too few arguments, expected %d, got %d", cast(int)nparams, cast(int)argumentList.length);
@ -6631,6 +6631,28 @@ extern (C++) struct ParameterList
// Ensure no remaining parameters in `other`
return !diff && other[idx] is null;
}
/// Returns: `true` if any parameter has a default argument
extern(D) bool hasDefaultArgs()
{
foreach (oidx, oparam, eidx, eparam; this)
{
if (eparam.defaultArg)
return true;
}
return false;
}
// Returns: `true` if any parameter doesn't have a default argument
extern(D) bool hasArgsWithoutDefault()
{
foreach (oidx, oparam, eidx, eparam; this)
{
if (!eparam.defaultArg)
return true;
}
return false;
}
}

View file

@ -1333,7 +1333,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
// extended index), as we need to run semantic when `oidx` changes.
size_t tupleOrigIdx = size_t.max;
size_t tupleExtIdx = size_t.max;
bool hasDefault;
foreach (oidx, oparam, eidx, eparam; tf.parameterList)
{
// oparam (original param) will always have the default arg
@ -1342,7 +1341,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
// position to get the offset in it later on.
if (oparam.defaultArg)
{
hasDefault = true;
// Get the obvious case out of the way
if (oparam is eparam)
errors |= !defaultArgSemantic(eparam, argsc);
@ -1369,11 +1367,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
eparam.defaultArg = (*te.exps)[eidx - tupleExtIdx];
}
}
else if (hasDefault)
{
.error(loc, "default argument expected for `%s`", oparam.toChars());
errors = true;
}
// We need to know the default argument to resolve `auto ref`,
// hence why this has to take place as the very last step.

View file

@ -12,6 +12,18 @@ static assert(fun(y: "y", "z", x: "x") == "xyzW");
static assert(fun( "x", "y", w: "w") == "xyZw");
static assert(fun(x: "x", "y", z: "z") == "xyzW");
// Default arguments need not all be at the end anymore
string fun2(string x = "x", string y, string z = "z")
{
return x ~ y ~ z;
}
static assert(fun2(y: "y") == "xyz");
// The assumption that first parameter having a default implies all parameters have a default is no longer valid,
// so this struct constructor shouldn't be mistaken for a default constructor.
struct SD { this(int x = 1, int y) { } }
// UFCS
static assert("x".fun("y", w: "w") == "xyZw");

View file

@ -7,7 +7,7 @@ fail_compilation/diag3438.d(20): Error: constructor `diag3438.F5.this` is marked
fail_compilation/diag3438.d(20): Use `@disable this();` if you want to disable default initialization.
fail_compilation/diag3438.d(21): Error: constructor `diag3438.F6.this` is marked `@disable`, so it cannot have default arguments for all parameters.
fail_compilation/diag3438.d(21): Use `@disable this();` if you want to disable default initialization.
fail_compilation/diag3438.d(24): Error: default argument expected for `y`
fail_compilation/diag3438.d(22): Error: constructor `diag3438.F7.this` all parameters have default arguments, but structs cannot have default constructors.
---
*/
@ -19,6 +19,4 @@ struct F3 { this(...) { } } // ok
struct F4 { this(int[] x...) { } } // ok
struct F5 { @disable this(int x = 1); }
struct F6 { @disable this(int x = 1) { } }
// Make sure the deprecation doesn't interfere w/ the check for default arguments
struct S { this(int x = 1, int y) { } }
struct F7 { this(int x = 1, int y = 2) { } }