diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index b0272c510f..5ce3bf0a84 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -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 "~ diff --git a/compiler/src/dmd/mtype.d b/compiler/src/dmd/mtype.d index e0bf723e43..5939db5ba8 100644 --- a/compiler/src/dmd/mtype.d +++ b/compiler/src/dmd/mtype.d @@ -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; + } } diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 65ddbf525a..ac60596506 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -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. diff --git a/compiler/test/compilable/named_arguments.d b/compiler/test/compilable/named_arguments.d index 240bfe1ea9..f287ccdea9 100644 --- a/compiler/test/compilable/named_arguments.d +++ b/compiler/test/compilable/named_arguments.d @@ -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"); diff --git a/compiler/test/fail_compilation/diag3438.d b/compiler/test/fail_compilation/diag3438.d index c4cbc721d8..0e4fb8b076 100644 --- a/compiler/test/fail_compilation/diag3438.d +++ b/compiler/test/fail_compilation/diag3438.d @@ -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) { } }