使用 lambda 调用时编译时折叠会导致错误
Compile-time fold results in error when called with a lambda
我试图在编译时 fold
一个数组,并将结果存储在 enum
中。当枚举(和对 fold
的调用)处于模块级别时它工作得很好,但是当它都包含在使用 lambda 调用的结构 和 中时编译失败。
下面是一些失败代码的简单示例:
import std.algorithm.iteration;
import std.stdio;
struct Foo
{
// Version 1 (works)
//enum x = [ 1, 2, 3 ].fold!"a * b";
// Version 2 (works)
//enum x = [ 1, 2, 3 ].fold!mult;
// Version 3 (broken)
enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "x = ", x);
}
// Outside of the struct, it works
enum y = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "y = ", y);
int mult(int a, int b)
{
return a * b;
}
void main(){}
(1、2版本,被注释掉了,编译正常,只是3版本有问题。)
编译时出现如下错误:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): while evaluating: `static assert(((int)).length == fun.length)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3697): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3718): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3636): Error: template instance `broken.Foo.reduce!((a, b) => a * b).reduceImpl!(false, int[], int)` error instantiating
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(4086): instantiated from here: `reduce!(int[])`
.\broken.d(13): instantiated from here: `fold!(int[])`
x = .\broken.d(13): Error: CTFE failed because of previous errors in `fold`
.\broken.d(16): while evaluating `pragma(msg, x)`
y = 6
Failed: ["C:\D\dmd2\windows\bin\dmd.exe", "-v", "-o-", ".\broken.d", "-I."]
我尝试查看错误中提到的源代码,但其中使用的概念超出了我目前对 D 的了解。
我最初假设 lambda 表达式在编译时可能无法正常工作,但 enum y
计算正确,所以我猜这不是...
我正在使用 DMD v2.086.1,但在使用 LDC 1.16.0 和 1.14.0 时遇到了同样的问题。
这是因为编译器不太擅长确定 lambda 是否需要访问其上下文。如果你写了这样的东西:
struct S {
int n;
int fun() {
import std.algorithm.iteration : fold;
return [1,2,3].fold!((a,b) => a*b*n);
}
}
应该很清楚,上面的lambda需要访问结构中的n
。以同样的方式,编译器在 enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);
的警告方面犯了错误,并假设 Foo
中存在一些会影响计算结果的状态。归档为 issue 20077.
您已经找到了一些变通方法,还有一个值得一提——向 lambda 添加参数类型:
enum x = [ 1, 2, 3 ].fold!((int a, int b) => a * b);
这样,编译器可以更早地找出 lambda 需要的信息,并且能够确定它不需要访问周围的范围。
我试图在编译时 fold
一个数组,并将结果存储在 enum
中。当枚举(和对 fold
的调用)处于模块级别时它工作得很好,但是当它都包含在使用 lambda 调用的结构 和 中时编译失败。
下面是一些失败代码的简单示例:
import std.algorithm.iteration;
import std.stdio;
struct Foo
{
// Version 1 (works)
//enum x = [ 1, 2, 3 ].fold!"a * b";
// Version 2 (works)
//enum x = [ 1, 2, 3 ].fold!mult;
// Version 3 (broken)
enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "x = ", x);
}
// Outside of the struct, it works
enum y = [ 1, 2, 3 ].fold!((a, b) => a * b);
pragma(msg, "y = ", y);
int mult(int a, int b)
{
return a * b;
}
void main(){}
(1、2版本,被注释掉了,编译正常,只是3版本有问题。)
编译时出现如下错误:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3690): while evaluating: `static assert(((int)).length == fun.length)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3697): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3718): Error: `this.__lambda2` has no value
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(3636): Error: template instance `broken.Foo.reduce!((a, b) => a * b).reduceImpl!(false, int[], int)` error instantiating
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\iteration.d(4086): instantiated from here: `reduce!(int[])`
.\broken.d(13): instantiated from here: `fold!(int[])`
x = .\broken.d(13): Error: CTFE failed because of previous errors in `fold`
.\broken.d(16): while evaluating `pragma(msg, x)`
y = 6
Failed: ["C:\D\dmd2\windows\bin\dmd.exe", "-v", "-o-", ".\broken.d", "-I."]
我尝试查看错误中提到的源代码,但其中使用的概念超出了我目前对 D 的了解。
我最初假设 lambda 表达式在编译时可能无法正常工作,但 enum y
计算正确,所以我猜这不是...
我正在使用 DMD v2.086.1,但在使用 LDC 1.16.0 和 1.14.0 时遇到了同样的问题。
这是因为编译器不太擅长确定 lambda 是否需要访问其上下文。如果你写了这样的东西:
struct S {
int n;
int fun() {
import std.algorithm.iteration : fold;
return [1,2,3].fold!((a,b) => a*b*n);
}
}
应该很清楚,上面的lambda需要访问结构中的n
。以同样的方式,编译器在 enum x = [ 1, 2, 3 ].fold!((a, b) => a * b);
的警告方面犯了错误,并假设 Foo
中存在一些会影响计算结果的状态。归档为 issue 20077.
您已经找到了一些变通方法,还有一个值得一提——向 lambda 添加参数类型:
enum x = [ 1, 2, 3 ].fold!((int a, int b) => a * b);
这样,编译器可以更早地找出 lambda 需要的信息,并且能够确定它不需要访问周围的范围。