From 4e20e93a28b1154c3a2a84bd7c6734dff5941e2b Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Sun, 5 Aug 2018 13:13:13 +0800 Subject: [PATCH 01/41] Initial draft of Multiple template constraints --- DIPs/DIP1xxx.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 DIPs/DIP1xxx.md diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md new file mode 100644 index 000000000..fe5a053ee --- /dev/null +++ b/DIPs/DIP1xxx.md @@ -0,0 +1,128 @@ +# Multiple template constraints + +| Field | Value | +|-----------------|-----------------------------------------------------------------| +| DIP: | (number/id -- assigned by DIP Manager) | +| Review Count: | 0 | +| Author: | Nicholas Wilson | +| Implementation: | | +| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | + +## Abstract + +Allow multiple `if` template constraints with an optional message to be printed in the +event that overload resolution fails (similar to `static assert`). The template is +considered a valid overload iff each of the constraints is satified. + +```D +template all(alias pred) +{ + bool all(Range)(Range range) + if (isInputRange!Range) + if (is(typeof(unaryFun!pred(range.front))), + "`" ~ pred.stringof[1..$-1] ~ "` isn't a unary predicate function for range.front")) + { + } +} +`` + +### Reference + +Optional links to reference material such as existing discussions, research papers +or any other supplementary materials. + +## Contents +* [Rationale](#rationale) +* [Description](#description) +* [Breaking Changes and Deprecations](#breaking-changes-and-deprecations) +* [Copyright & License](#copyright--license) +* [Reviews](#reviews) + +## Rationale + +It is well known that compilation error messages due to template contraint overload resolution +are particularly difficult to decipher. This is not helped by the number of overloads and very +precice (and thus complex) constraints placed on the overloads. When overload resolution fails +the compiler will print out all the in scope overloads and their constraints, without indication +of which constraints have failed. + +While it is not possible in the general case to provide useful information as to what constraints +have failed and why, because a constraint may have an arbitrary combination of logic. However the vast +majority of constraints are expressed in Conjuntive Normal Form (CNF). In this case it is definitely +possible to provide better daignostics as to which clauses have failed. However the current grammer +provides no way to translate particularly verbose constraints to a user not intimately familiar with +the constraint e.g. `is(typeof(unaryFun!pred(range.front))` (TODO: more examples). + +This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple `if` constraints, +each with an optional message, such that the compiler is much better placed to provide better diagnostics, +such as: + +* indicating if a clause is satisfied, +* indicating if a clause is the same as another overload (e.g. range functions and `isRange!Range`) + +## Description + +Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. +Each constraint must be satisfied to be a viable overload candiate. That is +```D +template foo(T) +if (constraint1!T) +if (constraint2!T) +if (constraint3!T) { ... } +`` +is semantically equivalent to +```D +template foo(T) +if (constraint1!T && + constraint2!T && + constraint3!T) +`` + +The optional constraint message can be used to provide a more easily uderstood description of why a +constraint has not been met. + +```D +template foo(T) +if (constraint1!T, " Constraint1 not met for " ~ T.stringof) +`` + +###Template Grammar changes + +All references to `Constraint` in the spec now reference `Constraints`. + +`Constraint` is changed from +``` +Constraint: + if ( Expression ) +`` +to +``` +Constraint: + if ( Expression ) + if ( Expression , Expression ) +`` + +and `Constraints` is defined as +``` +Constraints: + Constraint + Constraint Constraints +`` + +## Breaking Changes and Deprecations + +N/A. This DIP is purely additive. However in order to make use of this DIP, library writers will need +to update the constraints from CNF to one clause per `if` constraint which is a backwards incompatible change. +This is not a problem for Phobos, since it is released in sync with the compiler. + + +## Copyright & License + +Copyright (c) 2018 by the D Language Foundation + +Licensed under [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) + +## Reviews + +The DIP Manager will supplement this section with a summary of each review stage +of the DIP process beyond the Draft Review. From ea9479271e203026f6c097bcaa2327aa38cc601e Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Sun, 5 Aug 2018 13:15:48 +0800 Subject: [PATCH 02/41] Fix code blocks --- DIPs/DIP1xxx.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index fe5a053ee..d95fab26c 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -24,7 +24,7 @@ template all(alias pred) { } } -`` +``` ### Reference @@ -69,14 +69,14 @@ template foo(T) if (constraint1!T) if (constraint2!T) if (constraint3!T) { ... } -`` +``` is semantically equivalent to ```D template foo(T) if (constraint1!T && constraint2!T && constraint3!T) -`` +``` The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. @@ -84,7 +84,7 @@ constraint has not been met. ```D template foo(T) if (constraint1!T, " Constraint1 not met for " ~ T.stringof) -`` +``` ###Template Grammar changes @@ -107,7 +107,7 @@ and `Constraints` is defined as Constraints: Constraint Constraint Constraints -`` +``` ## Breaking Changes and Deprecations From ae1ab345846e987d4c1fdb144ada685ad9b1a31d Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Sun, 5 Aug 2018 13:17:45 +0800 Subject: [PATCH 03/41] Two more --- DIPs/DIP1xxx.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index d95fab26c..e8c2b4f28 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -94,13 +94,13 @@ All references to `Constraint` in the spec now reference `Constraints`. ``` Constraint: if ( Expression ) -`` +``` to ``` Constraint: if ( Expression ) if ( Expression , Expression ) -`` +``` and `Constraints` is defined as ``` From daf7279763ed00925b92b768201ec95409a27914 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Mon, 6 Aug 2018 16:57:16 +0800 Subject: [PATCH 04/41] Link to implementation --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index e8c2b4f28..08f50c8dd 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -5,7 +5,7 @@ | DIP: | (number/id -- assigned by DIP Manager) | | Review Count: | 0 | | Author: | Nicholas Wilson | -| Implementation: | | +| Implementation: | https://github.com/thewilsonator/dmd/tree/template-constraint-dip | | Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | ## Abstract From 3a5b70c7f5836299693603ba89bc2eae6d14719a Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:36:11 +0800 Subject: [PATCH 05/41] Add countUntil example --- DIPs/DIP1xxx.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 08f50c8dd..6a613ceff 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -51,7 +51,7 @@ have failed and why, because a constraint may have an arbitrary combination of l majority of constraints are expressed in Conjuntive Normal Form (CNF). In this case it is definitely possible to provide better daignostics as to which clauses have failed. However the current grammer provides no way to translate particularly verbose constraints to a user not intimately familiar with -the constraint e.g. `is(typeof(unaryFun!pred(range.front))` (TODO: more examples). +the constraint. This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple `if` constraints, each with an optional message, such that the compiler is much better placed to provide better diagnostics, @@ -60,6 +60,39 @@ such as: * indicating if a clause is satisfied, * indicating if a clause is the same as another overload (e.g. range functions and `isRange!Range`) +Using the particularly egregious example of the first overload of `std.algorithm.searching.countUntil`, +its current signature of + +```D +ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +if (isForwardRange!R +&& Rs.length > 0 +&& isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) +&& is(typeof(startsWith!pred(haystack, needles[0]))) +&& (Rs.length == 1 +|| is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) +``` + +would be be written as + +```D +ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +if (isForwardRange!R) +if (Rs.length > 0, "need a needle to countUntil with") +if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "each needle in `needles` must be a forward range") //TODO: is this actually what this means? +if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") +if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") +``` +and would print on error using `countUntil("foo", notARange)` (with the current implementation of this DIP) +``` +example.d(42): Error: template `countUntil("foo", notARange)` cannot deduce function from argument types !()(string), candidates are: +/path/to/std/algorithm/searching.d(747): ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) + satisfied: need a needle to countUntil with + not satisfied: each needle in `needles` must be a forward range + not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` + satisfied: multiple needles requires all constraints for all needles to be satisfied +/path/to/std/algorithm/searching.d(835): ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +``` ## Description Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. From 9e9fc93fc90b4152dbd1a0163febd16894f5598e Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:45:39 +0800 Subject: [PATCH 06/41] Add note that this applies for functions, methods, classes and structs templates --- DIPs/DIP1xxx.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 6a613ceff..e4e56f663 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -110,6 +110,7 @@ if (constraint1!T && constraint2!T && constraint3!T) ``` +The same is true for constraints on template functions, methods, classes and structs. The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. From e6573bb8c105d21c3dccb235c68b558f649f7ee5 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:47:00 +0800 Subject: [PATCH 07/41] wording --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index e4e56f663..63c2bb8be 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -110,7 +110,7 @@ if (constraint1!T && constraint2!T && constraint3!T) ``` -The same is true for constraints on template functions, methods, classes and structs. +The also applies to constraints on template functions, methods, classes and structs. The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. From 85685629bddfc7c91af76a427a66d1dca7c63646 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:49:35 +0800 Subject: [PATCH 08/41] Note not implemented yet --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 63c2bb8be..c3610a3c5 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -58,7 +58,7 @@ each with an optional message, such that the compiler is much better placed to p such as: * indicating if a clause is satisfied, -* indicating if a clause is the same as another overload (e.g. range functions and `isRange!Range`) +* indicating if a clause is the same as another overload (e.g. range functions and `isRange!Range`) (not implemented yet) Using the particularly egregious example of the first overload of `std.algorithm.searching.countUntil`, its current signature of From 61d147d19a39c3a2598e786c7cd4cef6db2f99c7 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:53:33 +0800 Subject: [PATCH 09/41] Fix error message --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index c3610a3c5..1e4bd2905 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -85,7 +85,7 @@ if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"mu ``` and would print on error using `countUntil("foo", notARange)` (with the current implementation of this DIP) ``` -example.d(42): Error: template `countUntil("foo", notARange)` cannot deduce function from argument types !()(string), candidates are: +example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: /path/to/std/algorithm/searching.d(747): ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: need a needle to countUntil with not satisfied: each needle in `needles` must be a forward range From 6a15ec68536c55c6d72b72533e01dc1445f4f6b7 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 14:58:05 +0800 Subject: [PATCH 10/41] Again --- DIPs/DIP1xxx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 1e4bd2905..661f11b29 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -86,13 +86,13 @@ if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"mu and would print on error using `countUntil("foo", notARange)` (with the current implementation of this DIP) ``` example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: -/path/to/std/algorithm/searching.d(747): ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +/path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: need a needle to countUntil with not satisfied: each needle in `needles` must be a forward range not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied -/path/to/std/algorithm/searching.d(835): ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) -``` +/path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +`` ## Description Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. From e388f2a1c405b62b88724f9a64a379ac268882e9 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Tue, 7 Aug 2018 22:59:56 +0800 Subject: [PATCH 11/41] add missing output --- DIPs/DIP1xxx.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 661f11b29..526beb119 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -87,6 +87,7 @@ and would print on error using `countUntil("foo", notARange)` (with the current ``` example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) + satisfied: isForwardRange!R satisfied: need a needle to countUntil with not satisfied: each needle in `needles` must be a forward range not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` From 7b8d07c711c31ce291b09e6a4d4d9c4b287f8a30 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 09:22:27 +0800 Subject: [PATCH 12/41] add missing ` --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 526beb119..91b4e8a20 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -93,7 +93,7 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) -`` +``` ## Description Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. From ee6016df0eeef9bd257a61e17daa0c2c4c84ed42 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 10:42:18 +0800 Subject: [PATCH 13/41] Fix messages --- DIPs/DIP1xxx.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 91b4e8a20..91a694fc6 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -79,7 +79,7 @@ would be be written as ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R) if (Rs.length > 0, "need a needle to countUntil with") -if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "each needle in `needles` must be a forward range") //TODO: is this actually what this means? +if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "`needles` that are ranges must be forward ranges") if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") ``` @@ -89,7 +89,7 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: isForwardRange!R satisfied: need a needle to countUntil with - not satisfied: each needle in `needles` must be a forward range + not satisfied: `needles` that are ranges must be forward ranges" not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) From e0b4d0ba16f368ac44a2911126e337876dded13d Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:30:33 +0800 Subject: [PATCH 14/41] remove less descriptive message --- DIPs/DIP1xxx.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 91a694fc6..759c0376d 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -78,7 +78,7 @@ would be be written as ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R) -if (Rs.length > 0, "need a needle to countUntil with") +if (Rs.length > 0) if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "`needles` that are ranges must be forward ranges") if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") @@ -88,7 +88,7 @@ and would print on error using `countUntil("foo", notARange)` (with the current example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: isForwardRange!R - satisfied: need a needle to countUntil with + satisfied: Rs.length > 0 not satisfied: `needles` that are ranges must be forward ranges" not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied From 0e6130c3950e0ec4b7a616370e137103528fbe50 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:31:15 +0800 Subject: [PATCH 15/41] Correct expressions for if with message --- DIPs/DIP1xxx.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 759c0376d..252e00a33 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -134,8 +134,8 @@ to ``` Constraint: if ( Expression ) - if ( Expression , Expression ) -``` + if ( AssignExpression , AssignExpression ) +`` and `Constraints` is defined as ``` From 44a1b16a2742ccc4de18112c32dadf307ff57ef4 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:31:48 +0800 Subject: [PATCH 16/41] Add section about static foreach --- DIPs/DIP1xxx.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 252e00a33..83fb2e9a0 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -93,6 +93,29 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +`` + +###Optional additional proposal: make static foreach work with constraints + +While this deals nicely with the first four constraints of `countUntil`, errors due to the last constraint are still difficult to understand. +One way to get better error mesage would be to enable static foreach: + +```D +ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +if (isForwardRange!R) +if (Rs.length > 0, "need a needle to countUntil with") +static foreach (i,alias N; Rs) + if (isForwardRange!(N) == isInputRange!(N), "`needles` that are ranges must be forward ranges"), + if (is(typeof(startsWith!pred(haystack, needles[i]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle[i])`) +``` +such that the supplemental message for `countUntil("foo", "bar", inputRangeofChar)` could be +``` +/path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) + satisfied: isForwardRange!R + satisfied: Rs.length > 0 + static foreach (i,alias N; Rs) + satisfied, not satisfied: `needles` that are ranges must be forward ranges": + satisfied, satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle[i])` ``` ## Description @@ -137,6 +160,14 @@ Constraint: if ( AssignExpression , AssignExpression ) `` +(or with static foreach +``` +Constraint: +if ( Expression ) +if ( AssignExpression , AssignExpression ) +StaticForeach Constraint +``` +) and `Constraints` is defined as ``` Constraints: From 3f2eca301eac72611cfd5054749c43d23c04ab57 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:32:32 +0800 Subject: [PATCH 17/41] Remove unneeded text in breaking changes --- DIPs/DIP1xxx.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 83fb2e9a0..7d3fa2ed0 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -177,10 +177,7 @@ Constraints: ## Breaking Changes and Deprecations -N/A. This DIP is purely additive. However in order to make use of this DIP, library writers will need -to update the constraints from CNF to one clause per `if` constraint which is a backwards incompatible change. -This is not a problem for Phobos, since it is released in sync with the compiler. - +N/A. This DIP is purely additive. ## Copyright & License From 40d1fa5c261108d7b177cf8dfdaf42ded035f38d Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:42:10 +0800 Subject: [PATCH 18/41] make static foreach match the grammar --- DIPs/DIP1xxx.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 7d3fa2ed0..1dde7004c 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -104,18 +104,22 @@ One way to get better error mesage would be to enable static foreach: ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R) if (Rs.length > 0, "need a needle to countUntil with") -static foreach (i,alias N; Rs) - if (isForwardRange!(N) == isInputRange!(N), "`needles` that are ranges must be forward ranges"), - if (is(typeof(startsWith!pred(haystack, needles[i]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle[i])`) +static foreach (alias N; Rs) + if (isForwardRange!(N) == isInputRange!(N), "needles that are ranges must be forward ranges") +static foreach (n; needles) + if (is(typeof(startsWith!pred(haystack, n))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, "~n.stringof~"`)) ``` such that the supplemental message for `countUntil("foo", "bar", inputRangeofChar)` could be ``` /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: isForwardRange!R satisfied: Rs.length > 0 - static foreach (i,alias N; Rs) - satisfied, not satisfied: `needles` that are ranges must be forward ranges": - satisfied, satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle[i])` + static foreach (alias N; Rs) + satisfied: needles that are ranges must be forward ranges": + not satisfied: needles that are ranges must be forward ranges": + static foreach (n; needles) + satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, "bar")` + satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, inputRangeofChar)` ``` ## Description From 298d779513ab2282cdc43b044934d864f006233e Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:46:26 +0800 Subject: [PATCH 19/41] fix --- DIPs/DIP1xxx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 1dde7004c..e718436b8 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -79,7 +79,7 @@ would be be written as ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R) if (Rs.length > 0) -if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "`needles` that are ranges must be forward ranges") +if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "needles that are ranges must be forward ranges") if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") ``` @@ -93,9 +93,9 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) -`` +``` -###Optional additional proposal: make static foreach work with constraints +###Optional additional proposal: make static foreach work with constraints (not implemented yet) While this deals nicely with the first four constraints of `countUntil`, errors due to the last constraint are still difficult to understand. One way to get better error mesage would be to enable static foreach: From a9b623b84ac1c5ed2e06a8e30ff95b07be2484fb Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:49:26 +0800 Subject: [PATCH 20/41] fix --- DIPs/DIP1xxx.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index e718436b8..26325da2d 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -89,7 +89,7 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) satisfied: isForwardRange!R satisfied: Rs.length > 0 - not satisfied: `needles` that are ranges must be forward ranges" + not satisfied: needles that are ranges must be forward ranges" not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) @@ -162,7 +162,7 @@ to Constraint: if ( Expression ) if ( AssignExpression , AssignExpression ) -`` +``` (or with static foreach ``` From 997e31ec8ef8e1498934257cda460ddbeb3596a9 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Wed, 8 Aug 2018 11:50:15 +0800 Subject: [PATCH 21/41] indent --- DIPs/DIP1xxx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 26325da2d..39ca08956 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -167,9 +167,9 @@ Constraint: (or with static foreach ``` Constraint: -if ( Expression ) -if ( AssignExpression , AssignExpression ) -StaticForeach Constraint + if ( Expression ) + if ( AssignExpression , AssignExpression ) + StaticForeach Constraint ``` ) and `Constraints` is defined as From 5d8991e382072473b001fca2530653e4026cc785 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Thu, 9 Aug 2018 09:27:34 +0800 Subject: [PATCH 22/41] add countuntil's third overload --- DIPs/DIP1xxx.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 39ca08956..7a3f76a27 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -83,7 +83,7 @@ if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "needles that are ranges mus if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") ``` -and would print on error using `countUntil("foo", notARange)` (with the current implementation of this DIP) +and would print on error using `countUntil("foo", notARangeOrChar)` (with the current implementation of this DIP) ``` example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) @@ -93,6 +93,7 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` satisfied: multiple needles requires all constraints for all needles to be satisfied /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +/path/to/std/algorithm/searching.d(913): std.algorithm.searching.countUntil(alias pred, R)(R haystack) if (isInputRange!R && is(typeof(unaryFun!pred(haystack.front)) : bool)) ``` ###Optional additional proposal: make static foreach work with constraints (not implemented yet) From 20b9abc00d82aebd74c3cd20540c1463d5bd654b Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Thu, 9 Aug 2018 11:12:57 +0800 Subject: [PATCH 23/41] Set status to draft --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 7a3f76a27..5d9314d12 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -6,7 +6,7 @@ | Review Count: | 0 | | Author: | Nicholas Wilson | | Implementation: | https://github.com/thewilsonator/dmd/tree/template-constraint-dip | -| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | +| Status: | Draft | ## Abstract From d084cd630911145b566f59daea3500542f9ae693 Mon Sep 17 00:00:00 2001 From: Nicholas Lindsay Wilson Date: Fri, 17 Aug 2018 11:05:36 +0800 Subject: [PATCH 24/41] Make `if` template constraints like `in` contracts. That is `if` is to static foreach and static assert what `in` is to foreach and assert. --- DIPs/DIP1xxx.md | 188 +++++++++++++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 74 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 5d9314d12..f48b2251e 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -1,4 +1,4 @@ -# Multiple template constraints +# Expression and Block Statement Template Constraints | Field | Value | |-----------------|-----------------------------------------------------------------| @@ -10,10 +10,13 @@ ## Abstract -Allow multiple `if` template constraints with an optional message to be printed in the -event that overload resolution fails (similar to `static assert`). The template is -considered a valid overload iff each of the constraints is satified. +Allow multiple expression based `if` template constraints with an optional message to be printed in the +event that overload resolution fails (similar to `static assert`) as well as block statement form that allows the use of `static foreach`. +That is to say, template constraint `if` becomes to `static foreach` and `static assert` as contract precondition `in` +is to `foreach` and `assert`. +The template is considered a valid overload iff each of the constraints is satified. +Expression form: ```D template all(alias pred) { @@ -24,6 +27,37 @@ template all(alias pred) { } } +`` + +Block statement form: +```D +ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +if +{ + static assert(isForwardRange!R); + static assert(Rs.length > 0, "need a needle to countUntil with"); + static foreach (alias N; Rs) + static assert(isForwardRange!(N) == isInputRange!(N), "needles that are ranges must be forward ranges"); + static foreach (n; needles) + static assert(is(typeof(startsWith!pred(haystack, n))), + "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, "~n.stringof~"`)); +} +``` + +Mixed: + +```D +ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +if(isForwardRange!R) +if(Rs.length > 0, "need a needle to countUntil with") +if +{ + static foreach (alias N; Rs) + static assert(isForwardRange!(N) == isInputRange!(N), "needles that are ranges must be forward ranges"); + static foreach (n; needles) + static assert(is(typeof(startsWith!pred(haystack, n))), + "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, "~n.stringof~"`)); +} ``` ### Reference @@ -53,12 +87,13 @@ possible to provide better daignostics as to which clauses have failed. However provides no way to translate particularly verbose constraints to a user not intimately familiar with the constraint. -This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple `if` constraints, -each with an optional message, such that the compiler is much better placed to provide better diagnostics, -such as: - -* indicating if a clause is satisfied, -* indicating if a clause is the same as another overload (e.g. range functions and `isRange!Range`) (not implemented yet) +This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple expression `if` constraints, +each with an optional message (similar to what was done with contracts in DIP1009), as well as adding a block statement form that +allows the use of static foreach to eliminate the need for recursive templates in template constrains +(similar to the `in` contract form prior to DIP1009). +This will put the compiler in a much better position to provide useful diagnostics, such as indicating which clauses are not satisfied +and allowing the template author to provide messages in the case of non-intuitive formulations of constraints +e.g. `if(isForwardRange!(R) == isInputRange!(R), "needles that are ranges must be forward ranges")`. Using the particularly egregious example of the first overload of `std.algorithm.searching.countUntil`, its current signature of @@ -73,55 +108,37 @@ if (isForwardRange!R || is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) ``` -would be be written as +would be be written using the block statement form to elimiate the recursive constraint as ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) -if (isForwardRange!R) -if (Rs.length > 0) -if (isForwardRange!(Rs[0]) == isInputRange!(Rs[0]), "needles that are ranges must be forward ranges") -if (is(typeof(startsWith!pred(haystack, needles[0]))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles`") -if (Rs.length == 1 || is(typeof(countUntil!pred(haystack, needles[1 .. $]))),"multiple needles requires all constraints for all needles to be satisfied") +if(isForwardRange!R) +if(Rs.length > 0, "need a needle to countUntil with") +if +{ + static foreach (n; needles) + { + static assert(isForwardRange!(typeof(n)) == isInputRange!(typeof(n)), + "`"~n.stringof ~"`: needles that are ranges must be forward ranges"); + static assert(is(typeof(startsWith!pred(haystack, n))), + "predicate `" ~ pred.stringof "` must be valid for"~ + "`startsWith!pred(haystack, "~n.stringof~"`)); + } +} ``` -and would print on error using `countUntil("foo", notARangeOrChar)` (with the current implementation of this DIP) +the first two constraints do not require the use of the block statement form to use a static foreach so +they can be done in the expression style. + +This could print on error using `countUntil("foo", inputRangeOfInt)` ``` example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduce function from argument types !()(string,NotARange), candidates are: /path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) - satisfied: isForwardRange!R - satisfied: Rs.length > 0 - not satisfied: needles that are ranges must be forward ranges" - not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, needle)` for each needle in `needles` - satisfied: multiple needles requires all constraints for all needles to be satisfied + not satisfied: `inputRangeOfInt`: needles that are ranges must be forward ranges + not satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, inputRangeOfInt)` /path/to/std/algorithm/searching.d(835): std.algorithm.searching.countUntil(alias pred = "a == b", R, N)(R haystack, N needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) /path/to/std/algorithm/searching.d(913): std.algorithm.searching.countUntil(alias pred, R)(R haystack) if (isInputRange!R && is(typeof(unaryFun!pred(haystack.front)) : bool)) ``` -###Optional additional proposal: make static foreach work with constraints (not implemented yet) - -While this deals nicely with the first four constraints of `countUntil`, errors due to the last constraint are still difficult to understand. -One way to get better error mesage would be to enable static foreach: - -```D -ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) -if (isForwardRange!R) -if (Rs.length > 0, "need a needle to countUntil with") -static foreach (alias N; Rs) - if (isForwardRange!(N) == isInputRange!(N), "needles that are ranges must be forward ranges") -static foreach (n; needles) - if (is(typeof(startsWith!pred(haystack, n))), "predicate `" ~ pred.stringof "` must be valid for `startsWith!pred(haystack, "~n.stringof~"`)) -``` -such that the supplemental message for `countUntil("foo", "bar", inputRangeofChar)` could be -``` -/path/to/std/algorithm/searching.d(747): std.algorithm.searching.countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) - satisfied: isForwardRange!R - satisfied: Rs.length > 0 - static foreach (alias N; Rs) - satisfied: needles that are ranges must be forward ranges": - not satisfied: needles that are ranges must be forward ranges": - static foreach (n; needles) - satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, "bar")` - satisfied: predicate `a == b` must be valid for `startsWith!pred(haystack, inputRangeofChar)` -``` ## Description Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. @@ -149,40 +166,63 @@ template foo(T) if (constraint1!T, " Constraint1 not met for " ~ T.stringof) ``` -###Template Grammar changes +###Grammar changes -All references to `Constraint` in the spec now reference `Constraints`. +```diff ++Constraints: ++ Constraint ++ Constraint Constraints -`Constraint` is changed from -``` -Constraint: - if ( Expression ) -``` -to -``` Constraint: - if ( Expression ) - if ( AssignExpression , AssignExpression ) -``` +- if ( Expression ) ++ if ( AssertArguments ) ++ if BlockStatement + +FuncDeclaratorSuffix: + Parameters MemberFunctionAttributes[opt] +- TemplateParameters Parameters MemberFunctionAttributes[opt] Constraint[opt] ++ TemplateParameters Parameters MemberFunctionAttributes[opt] Constraints[opt] + +TemplateDeclaration: +- template Identifier TemplateParameters Constraint[opt] { DeclDefs[opt] } ++ template Identifier TemplateParameters Constraints[opt] { DeclDefs[opt] } + +ConstructorTemplate: +- this TemplateParameters Parameters MemberFunctionAttributes[opt] Constraint[opt] : +- this TemplateParameters Parameters MemberFunctionAttributes[opt] Constraint[opt] FunctionBody ++ this TemplateParameters Parameters MemberFunctionAttributes[opt] Constraints[opt] : ++ this TemplateParameters Parameters MemberFunctionAttributes[opt] Constraints[opt] FunctionBody + +ClassTemplateDeclaration: +- class Identifier TemplateParameters Constraint[opt] BaseClassList[opt] AggregateBody +- class Identifier TemplateParameters BaseClassList[opt] Constraint[opt] AggregateBody ++ class Identifier TemplateParameters Constraints[opt] BaseClassList[opt] AggregateBody ++ class Identifier TemplateParameters BaseClassList[opt] Constraints[opt] AggregateBody + +InterfaceTemplateDeclaration: +- interface Identifier TemplateParameters Constraint[opt] BaseInterfaceList[opt] AggregateBody +- interface Identifier TemplateParameters BaseInterfaceList Constraint AggregateBody ++ interface Identifier TemplateParameters Constraints[opt] BaseInterfaceList[opt] AggregateBody ++ interface Identifier TemplateParameters BaseInterfaceList Constraints AggregateBody + +StructTemplateDeclaration: +- struct Identifier TemplateParameters Constraint[opt] AggregateBody ++ struct Identifier TemplateParameters Constraints[opt] AggregateBody + +UnionTemplateDeclaration: +- union Identifier TemplateParameters Constraint[opt] AggregateBody ++ union Identifier TemplateParameters Constraints[opt] AggregateBody + +TemplateMixinDeclaration: +- mixin template Identifier TemplateParameters Constraint[opt] { DeclDefs[opt] } ++ mixin template Identifier TemplateParameters Constraints[opt] { DeclDefs[opt] } -(or with static foreach -``` -Constraint: - if ( Expression ) - if ( AssignExpression , AssignExpression ) - StaticForeach Constraint -``` -) -and `Constraints` is defined as -``` -Constraints: - Constraint - Constraint Constraints ``` + ## Breaking Changes and Deprecations -N/A. This DIP is purely additive. +N/A. The current template constraint syntax becomes a single expression constraint. ## Copyright & License From 0644d18d878ece568c04eb1c3dde28a5e0fb9a0c Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:27:01 +0800 Subject: [PATCH 25/41] missing ` --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index f48b2251e..41bee066a 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -27,7 +27,7 @@ template all(alias pred) { } } -`` +``` Block statement form: ```D From a2a39360bde0fec0c920f085360410b981f6a552 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:27:34 +0800 Subject: [PATCH 26/41] Remove reference template text --- DIPs/DIP1xxx.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 41bee066a..d09fd1230 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -62,9 +62,6 @@ if ### Reference -Optional links to reference material such as existing discussions, research papers -or any other supplementary materials. - ## Contents * [Rationale](#rationale) * [Description](#description) From 281858ea7b80102916165f063d7ea52abb03c6e0 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:28:13 +0800 Subject: [PATCH 27/41] Fix sentence flow. --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index d09fd1230..f1227c56b 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -78,7 +78,7 @@ the compiler will print out all the in scope overloads and their constraints, wi of which constraints have failed. While it is not possible in the general case to provide useful information as to what constraints -have failed and why, because a constraint may have an arbitrary combination of logic. However the vast +have failed and why, because a constraint may have an arbitrary combination of logic, the vast majority of constraints are expressed in Conjuntive Normal Form (CNF). In this case it is definitely possible to provide better daignostics as to which clauses have failed. However the current grammer provides no way to translate particularly verbose constraints to a user not intimately familiar with From ba5323edc6bad465c00fa0ac0382029c917f089b Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:40:44 +0800 Subject: [PATCH 28/41] Add block statement form to description --- DIPs/DIP1xxx.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index f1227c56b..48d47a32b 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -153,14 +153,27 @@ if (constraint1!T && constraint2!T && constraint3!T) ``` -The also applies to constraints on template functions, methods, classes and structs. + +The block statement form +```D +template foo(T) +if +{ + ...constraints... +} +``` +may contain only `static assert`s, `enum` and `alias` declarations and `static foreach` and `static if` statements. +Each `static assert` in the block statement, including those in unrolled `static foreach`statements and satisfied +`static if`statements, must pass for the template to be a viable overload. + +This also applies to constraints on template functions, methods, classes and structs. The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. ```D template foo(T) -if (constraint1!T, " Constraint1 not met for " ~ T.stringof) +if (isForwardRange!T == isInputRange!T, T.stringof ~" must be a forward range if it is a range") ``` ###Grammar changes @@ -213,7 +226,6 @@ UnionTemplateDeclaration: TemplateMixinDeclaration: - mixin template Identifier TemplateParameters Constraint[opt] { DeclDefs[opt] } + mixin template Identifier TemplateParameters Constraints[opt] { DeclDefs[opt] } - ``` From b5395195044237afd6fe75e9c13d615ec00e043d Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:42:01 +0800 Subject: [PATCH 29/41] add interface and union to the description. --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 48d47a32b..60fca265e 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -166,7 +166,7 @@ may contain only `static assert`s, `enum` and `alias` declarations and `static f Each `static assert` in the block statement, including those in unrolled `static foreach`statements and satisfied `static if`statements, must pass for the template to be a viable overload. -This also applies to constraints on template functions, methods, classes and structs. +This also applies to constraints on template functions, methods, classes, interfaces, structs and unions. The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. From b1d939859e7a531c362663e20590df987e95eff9 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:43:05 +0800 Subject: [PATCH 30/41] ditto template mixin --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 60fca265e..77969b0dc 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -166,7 +166,7 @@ may contain only `static assert`s, `enum` and `alias` declarations and `static f Each `static assert` in the block statement, including those in unrolled `static foreach`statements and satisfied `static if`statements, must pass for the template to be a viable overload. -This also applies to constraints on template functions, methods, classes, interfaces, structs and unions. +This also applies to constraints on template mixins, functions, methods, classes, interfaces, structs and unions. The optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. From 39ade6b42d9e0448945a88616a07853eb9022fb6 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 14:48:59 +0800 Subject: [PATCH 31/41] Don't make it sound like only one block constraint is allowed --- DIPs/DIP1xxx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 77969b0dc..9e4bf2c29 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -85,8 +85,8 @@ provides no way to translate particularly verbose constraints to a user not inti the constraint. This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple expression `if` constraints, -each with an optional message (similar to what was done with contracts in DIP1009), as well as adding a block statement form that -allows the use of static foreach to eliminate the need for recursive templates in template constrains +each with an optional message (similar to what was done with contracts in DIP1009), as well as block statements that +allows the use of `static foreach` and declare `alias`es and `enum`s to eliminate the need for recursive templates in template constrains (similar to the `in` contract form prior to DIP1009). This will put the compiler in a much better position to provide useful diagnostics, such as indicating which clauses are not satisfied and allowing the template author to provide messages in the case of non-intuitive formulations of constraints @@ -110,7 +110,7 @@ would be be written using the block statement form to elimiate the recursive con ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if(isForwardRange!R) -if(Rs.length > 0, "need a needle to countUntil with") +if(Rs.length > 0, "need a needle to countUntil with") // example messge, probably not needed for something this simple if { static foreach (n; needles) From 6d6cf4d0a2df09f67849769f49e359a645d65628 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:03:12 +0800 Subject: [PATCH 32/41] Reword abstract --- DIPs/DIP1xxx.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 9e4bf2c29..d76165ef6 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -10,10 +10,11 @@ ## Abstract -Allow multiple expression based `if` template constraints with an optional message to be printed in the -event that overload resolution fails (similar to `static assert`) as well as block statement form that allows the use of `static foreach`. -That is to say, template constraint `if` becomes to `static foreach` and `static assert` as contract precondition `in` -is to `foreach` and `assert`. +Allow multiple `if` template constraints, for the expression form allow an optional message to be printed in the +event that overload resolution fails (similar to `static assert`), as well as a block statement +form of template constraint that allows the use of `static foreach`. +That is to say, template constraint `if` becomes the static form of contract precondition `in`. + The template is considered a valid overload iff each of the constraints is satified. Expression form: From d29e19eafbc5bf5d245ef9bafa4b4dd293221a11 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:06:12 +0800 Subject: [PATCH 33/41] Add references --- DIPs/DIP1xxx.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index d76165ef6..4f8926eee 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -63,6 +63,10 @@ if ### Reference +https://github.com/dlang/phobos/pull/6607 + +https://issues.dlang.org/show_bug.cgi?id=13683 + ## Contents * [Rationale](#rationale) * [Description](#description) From dfc309ecd7ebe9b686573249ede95fc8b28fbc58 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:10:14 +0800 Subject: [PATCH 34/41] Typo --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 4f8926eee..7298f0249 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -84,7 +84,7 @@ of which constraints have failed. While it is not possible in the general case to provide useful information as to what constraints have failed and why, because a constraint may have an arbitrary combination of logic, the vast -majority of constraints are expressed in Conjuntive Normal Form (CNF). In this case it is definitely +majority of constraints are expressed in Conjunctive Normal Form (CNF). In this case it is definitely possible to provide better daignostics as to which clauses have failed. However the current grammer provides no way to translate particularly verbose constraints to a user not intimately familiar with the constraint. From ada290ae175ecdba18c0da261b96f46eae8f4b6c Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:20:24 +0800 Subject: [PATCH 35/41] Update DIP1xxx.md --- DIPs/DIP1xxx.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 7298f0249..aba838039 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -89,9 +89,9 @@ possible to provide better daignostics as to which clauses have failed. However provides no way to translate particularly verbose constraints to a user not intimately familiar with the constraint. -This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple expression `if` constraints, -each with an optional message (similar to what was done with contracts in DIP1009), as well as block statements that -allows the use of `static foreach` and declare `alias`es and `enum`s to eliminate the need for recursive templates in template constrains +This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple `if` constraints, +the expression form with an optional message (similar to what was done with contracts in DIP1009), as well as block statements that +allows the use of `static foreach` and to declare `alias`es and `enum`s to eliminate the need for recursive templates in template constrains (similar to the `in` contract form prior to DIP1009). This will put the compiler in a much better position to provide useful diagnostics, such as indicating which clauses are not satisfied and allowing the template author to provide messages in the case of non-intuitive formulations of constraints @@ -143,8 +143,7 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc ## Description -Template constraints are changed to allow multiple multiple `if` template constraints with an optional message. -Each constraint must be satisfied to be a viable overload candiate. That is +Template constraints are changed to allow multiple `if` template constraints. The expression form: ```D template foo(T) if (constraint1!T) @@ -158,8 +157,15 @@ if (constraint1!T && constraint2!T && constraint3!T) ``` +The optional constraint message can be used to provide a more easily uderstood description of why a +constraint has not been met. + +```D +template foo(T) +if (isForwardRange!T == isInputRange!T, T.stringof ~" must be a forward range if it is a range") +``` -The block statement form +The block statement form: ```D template foo(T) if @@ -167,19 +173,13 @@ if ...constraints... } ``` -may contain only `static assert`s, `enum` and `alias` declarations and `static foreach` and `static if` statements. +may contain only `static assert`, `static foreach` and `static if` statements and `enum` and `alias` declarations. Each `static assert` in the block statement, including those in unrolled `static foreach`statements and satisfied `static if`statements, must pass for the template to be a viable overload. -This also applies to constraints on template mixins, functions, methods, classes, interfaces, structs and unions. - -The optional constraint message can be used to provide a more easily uderstood description of why a -constraint has not been met. +All `static assert` must pass for the template to be viable. -```D -template foo(T) -if (isForwardRange!T == isInputRange!T, T.stringof ~" must be a forward range if it is a range") -``` +This also applies to constraints on template mixins, functions, methods, classes, interfaces, structs and unions. ###Grammar changes From c59a6364176247f03c68655bf66daa5c0c8697df Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:23:55 +0800 Subject: [PATCH 36/41] Update DIP1xxx.md --- DIPs/DIP1xxx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index aba838039..97dbefe6c 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -174,10 +174,10 @@ if } ``` may contain only `static assert`, `static foreach` and `static if` statements and `enum` and `alias` declarations. -Each `static assert` in the block statement, including those in unrolled `static foreach`statements and satisfied -`static if`statements, must pass for the template to be a viable overload. +Each `static assert` in the block statement, including those in unrolled `static foreach` statements and satisfied +`static if` statements, must pass for the constraint to be satisfied. -All `static assert` must pass for the template to be viable. +All constraints must be satisfied for the template to be viable. This also applies to constraints on template mixins, functions, methods, classes, interfaces, structs and unions. From 0d607bfab3e699fb09e82faccc6e67eb4208a0ec Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 26 Aug 2018 15:40:32 +0800 Subject: [PATCH 37/41] Update DIP1xxx.md --- DIPs/DIP1xxx.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 97dbefe6c..47b5e385e 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -143,29 +143,31 @@ example.d(42): Error: template `std.algorithm.searching.countUntil` cannot deduc ## Description -Template constraints are changed to allow multiple `if` template constraints. The expression form: +Template constraints are changed to allow multiple `if` template constraints. +All constraints must be satisfied for the template to be viable. +Examples given are for `template`s but also apply to constraints on template mixins, functions, methods, classes, +interfaces, structs and unions. + +### Expression Form + +The expression form is: ```D template foo(T) if (constraint1!T) if (constraint2!T) if (constraint3!T) { ... } ``` -is semantically equivalent to -```D -template foo(T) -if (constraint1!T && - constraint2!T && - constraint3!T) -``` -The optional constraint message can be used to provide a more easily uderstood description of why a + +An optional constraint message can be used to provide a more easily uderstood description of why a constraint has not been met. ```D template foo(T) if (isForwardRange!T == isInputRange!T, T.stringof ~" must be a forward range if it is a range") ``` +###Block Statement Form -The block statement form: +The block statement form is: ```D template foo(T) if @@ -173,13 +175,10 @@ if ...constraints... } ``` -may contain only `static assert`, `static foreach` and `static if` statements and `enum` and `alias` declarations. -Each `static assert` in the block statement, including those in unrolled `static foreach` statements and satisfied -`static if` statements, must pass for the constraint to be satisfied. - -All constraints must be satisfied for the template to be viable. - -This also applies to constraints on template mixins, functions, methods, classes, interfaces, structs and unions. +where `...constraints...` may contain only `static assert`, `static foreach` and `static if` statements and `enum` and `alias` declarations. +The declarations are local to the scope of the constraint or `static foreach` or `static if` they are declared in. +Each `static assert` in the block statement in satisfied `static if` statements, +including those in unrolled `static foreach` statements, must pass for the constraint to be satisfied. ###Grammar changes From 31f6b87a33a32f45271cabb6743acf8ddd6a081d Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Mon, 14 Jan 2019 08:22:49 +0800 Subject: [PATCH 38/41] Update DIP1xxx.md --- DIPs/DIP1xxx.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 47b5e385e..1c36945c6 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -49,8 +49,8 @@ Mixed: ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) -if(isForwardRange!R) -if(Rs.length > 0, "need a needle to countUntil with") +if (isForwardRange!R) +if (Rs.length > 0, "need a needle to countUntil with") if { static foreach (alias N; Rs) @@ -95,7 +95,7 @@ allows the use of `static foreach` and to declare `alias`es and `enum`s to elimi (similar to the `in` contract form prior to DIP1009). This will put the compiler in a much better position to provide useful diagnostics, such as indicating which clauses are not satisfied and allowing the template author to provide messages in the case of non-intuitive formulations of constraints -e.g. `if(isForwardRange!(R) == isInputRange!(R), "needles that are ranges must be forward ranges")`. +e.g. `if (isForwardRange!(R) == isInputRange!(R), "needles that are ranges must be forward ranges")`. Using the particularly egregious example of the first overload of `std.algorithm.searching.countUntil`, its current signature of @@ -114,8 +114,8 @@ would be be written using the block statement form to elimiate the recursive con ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) -if(isForwardRange!R) -if(Rs.length > 0, "need a needle to countUntil with") // example messge, probably not needed for something this simple +if (isForwardRange!R) +if (Rs.length > 0, "need a needle to countUntil with") // example messge, probably not needed for something this simple if { static foreach (n; needles) @@ -165,7 +165,7 @@ constraint has not been met. template foo(T) if (isForwardRange!T == isInputRange!T, T.stringof ~" must be a forward range if it is a range") ``` -###Block Statement Form +### Block Statement Form The block statement form is: ```D @@ -180,7 +180,7 @@ The declarations are local to the scope of the constraint or `static foreach` or Each `static assert` in the block statement in satisfied `static if` statements, including those in unrolled `static foreach` statements, must pass for the constraint to be satisfied. -###Grammar changes +### Grammar changes ```diff +Constraints: From 3a9c1f44ac8c735e2e6578b21f5822b16b86930a Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Mon, 14 Jan 2019 11:11:13 +0800 Subject: [PATCH 39/41] Fix moar typos! --- DIPs/DIP1xxx.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 1c36945c6..01529c2b1 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -85,13 +85,13 @@ of which constraints have failed. While it is not possible in the general case to provide useful information as to what constraints have failed and why, because a constraint may have an arbitrary combination of logic, the vast majority of constraints are expressed in Conjunctive Normal Form (CNF). In this case it is definitely -possible to provide better daignostics as to which clauses have failed. However the current grammer +possible to provide better daignostics as to which clauses have failed. However the current grammar provides no way to translate particularly verbose constraints to a user not intimately familiar with the constraint. This DIP therefore proposes to formalise the use of CNF constraints by allowing multiple `if` constraints, the expression form with an optional message (similar to what was done with contracts in DIP1009), as well as block statements that -allows the use of `static foreach` and to declare `alias`es and `enum`s to eliminate the need for recursive templates in template constrains +allows the use of `static foreach` and to declare `alias`es and `enum`s to eliminate the need for recursive templates in template constraints (similar to the `in` contract form prior to DIP1009). This will put the compiler in a much better position to provide useful diagnostics, such as indicating which clauses are not satisfied and allowing the template author to provide messages in the case of non-intuitive formulations of constraints @@ -110,12 +110,12 @@ if (isForwardRange!R || is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) ``` -would be be written using the block statement form to elimiate the recursive constraint as +would be be written using the block statement form to eliminate the recursive constraint as ```D ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R) -if (Rs.length > 0, "need a needle to countUntil with") // example messge, probably not needed for something this simple +if (Rs.length > 0, "need a needle to countUntil with") // example message, probably not needed for something this simple if { static foreach (n; needles) @@ -158,7 +158,7 @@ if (constraint2!T) if (constraint3!T) { ... } ``` -An optional constraint message can be used to provide a more easily uderstood description of why a +An optional constraint message can be used to provide a more easily understood description of why a constraint has not been met. ```D From 5b251179f1609b751e92044c98f1ac39984e967b Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Wed, 16 Jan 2019 17:33:05 +0800 Subject: [PATCH 40/41] Restore balance to the ~~force~~ parentheses. --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index 01529c2b1..c071a608d 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -24,7 +24,7 @@ template all(alias pred) bool all(Range)(Range range) if (isInputRange!Range) if (is(typeof(unaryFun!pred(range.front))), - "`" ~ pred.stringof[1..$-1] ~ "` isn't a unary predicate function for range.front")) + "`" ~ pred.stringof[1..$-1] ~ "` isn't a unary predicate function for range.front") { } } From 155efcfdf2355734e8ceb36ffcd9dfd3e45340bf Mon Sep 17 00:00:00 2001 From: Marco Leise Date: Wed, 16 Jan 2019 22:47:08 +0800 Subject: [PATCH 41/41] Update DIPs/DIP1xxx.md Co-Authored-By: thewilsonator --- DIPs/DIP1xxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIP1xxx.md b/DIPs/DIP1xxx.md index c071a608d..ae070a1e2 100644 --- a/DIPs/DIP1xxx.md +++ b/DIPs/DIP1xxx.md @@ -36,7 +36,7 @@ ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if { static assert(isForwardRange!R); - static assert(Rs.length > 0, "need a needle to countUntil with"); + static assert(Rs.length > 0, "need at least one needle as stop condition"); static foreach (alias N; Rs) static assert(isForwardRange!(N) == isInputRange!(N), "needles that are ranges must be forward ranges"); static foreach (n; needles)