Bug 18199 – Error with lambda in struct initializer
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-01-06T01:41:42Z
Last change time
2018-04-10T10:51:40Z
Assigned to
John Belmonte
Creator
elronnd
Comments
Comment #0 by elronnd — 2018-01-06T01:41:42Z
With the following code:
struct Bla {
int delegate(int, int) fun;
}
void main() {
Bla bla1 = Bla((int a, int b) { return a + b; }); // works
Bla bla2 = {(int a, int b) { return a + b; }}; // errors
}
There is this cryptic error:
t.d(6): Error: found } when expecting ; following statement
t.d(6): Deprecation: use { } for an empty statement, not ;
t.d(8): Error: semicolon expected, not EOF
t.d(8): Error: found EOF when expecting } following compound statement
If nothing else, the error message should be improved, but shouldn't the second line be valid?
Comment #1 by elronnd — 2018-01-06T05:12:53Z
Oh, one more thing.
Bla bla = {(int a, int b) => a + b};
works fine.
Comment #2 by john — 2018-03-17T16:51:06Z
The memberName:expression variant of initialization is also affected:
struct Foo {
int function(int) bar;
}
static Foo foo = {
bar : function(x) { return 2 * x; }, // broken
//bar : (x) => 2 * x, // works
};
This seems to be a parsing issue where the struct initializer can't handle nested curly brackets.
Comment #3 by john — 2018-03-18T00:02:38Z
I'm trying to locate relevant code in the dmd source code, any pointers would be appreciated.
The emitted "Deprecation: use { } for an empty statement, not ;" error is telling. We can infer that the compiler is incorrectly interpreting the initializer expression as a statement.
To clarify, the struct initializer test aborts on any semicolon or return token, which can happen in any function literal with curly brackets.
I believe these are all the cases which need to additionally be tolerated:
function (parameters) { statements... }
delegate (parameters) { statements... }
(parameters) { statements... }
{ statements... }
Comment #6 by john — 2018-03-18T13:04:42Z
Here is my understanding of the current code's intention. When presented with "MyType foo = { ... }", the parser must infer whether the RHS is a struct initializer or a 0-parameter function literal without knowing anything about MyType. A contrived example of the two cases, respectively:
// RHS is struct initializer (e.g. MyType is struct with a string field)
static MyType foo = {
"a" + "b",
};
// RHS is function literal (e.g. MyType is alias of void function())
static MyType bar = {
"a" + "b";
};
So the implementation uses the presence of semicolon or return tokens to infer that the RHS is a function literal. However that solution yields the wrong answer when a struct initializer happens to hold certain forms of function literals.
To fix this the parser may require an isFunctionLiteral() at least handling the "{ ... }" form. A caveat with this approach is that a malformed function literal will be parsed as a struct initializer, yielding confusing error reports.
Comment #7 by john — 2018-03-18T22:22:24Z
Besides the code's documented case of "{}" (could be empty struct initializer or empty parameterless function literal), there is another ambiguous case:
static MyStruct foo = {
{ return 1 + 1; }
};
where RHS could either be a struct initializer (with member type "int function()") or function literal with a needless added scope.
In the "{}" case, the code sides with struct initializer. This makes sense since the function literal could be disambiguated in several ways (e.g. as "function() {}").
Given that, I propose to resolve this bug as follows:
1) document this other ambiguous case
2) document why we side with struct literal in ambiguous cases (i.e. since you can disambiguate function literal cases via extra syntax)
3) change the parser to only abort struct literal look-ahead if semicolon or return token appears at top level of curly brackets, so that struct initializer may contain function literals
The attempted change to parser.d broke some code in a test application. It's possible to have a function body with no colon or return tokens at the top level (e.g. a body with just a switch statement).
I also realized that the current dmd code can misinterpret a function literal as a struct initializer as well. Contrived, but consider the following function body which has no colon or return at any scope:
static MyFun foo = {
final switch(5) {
}
};
So the "color or return" test is clearly not robust for discerning function literal from struct initializer.
New plan is to try to implement a simple lookahead to test for struct initializer.
Comment #10 by github-bugzilla — 2018-04-10T10:51:39Z
Commits pushed to master at https://github.com/dlang/dmdhttps://github.com/dlang/dmd/commit/019f0016cf7a6e11584fe0188f9e692215cce212
fix Issue 18199 - Error with lambda in struct initializer
Allow struct initializers to include all function literal forms.
Previously, a function literal containing semicolon or return tokens
would cause the containing struct initializer to be incorrectly
treated as a parameterless funtion literal (i.e. {statements...}).
The issue is resolved by only aborting the struct initializer
lookahead when statement tokens appear at the top curly bracket scope.
A bug in the converse case, where a function literal containing no
semicolon or return tokens would be incorrectly parsed as a struct
initializer, is also addressed.
Also, document another ambiguous case in function literal vs.
struct initializer, as well as explain why these are resolved as
struct initializer.
https://github.com/dlang/dmd/commit/1edd074334f9f65d75d318df347f377d69eb002e
Merge pull request #8051 from belm0/fix-18199
fix Issue 18199 - Error with lambda in struct initializer
merged-on-behalf-of: Mike Franklin <[email protected]>