D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 7184 - parse error on *(x)++
Summary: parse error on *(x)++
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: Other All
: P3 normal
Assignee: No Owner
URL:
Keywords: bootcamp, pull, spec
: 15463 (view as issue list)
Depends on:
Blocks:
 
Reported: 2011-12-29 16:27 UTC by timon.gehr
Modified: 2023-07-14 09:48 UTC (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description timon.gehr 2011-12-29 16:27:58 UTC
void main(){
    int[2] y;
    int *x = y.ptr;
    *(x)++=0;
}

Error: expression expected, not '='
Error: C style cast illegal, use cast(x)++0
Error: found '0' when expecting ';' following statement

The code should compile.




Workaround if parentheses are a result of code generation:

void main(){
    int[2] y;
    int *x = y.ptr;
    *(*&x)++=0;
}
Comment 1 RazvanN 2017-11-02 10:01:49 UTC
Issuing an error is the correct behavior. As you can see in the grammar [1], 
*(x)++ is parsed the following way: *UnaryExpression. If a parenthesis is encountered, then the parser expects a type : (type).identifier/templateInstance.
Since none of the above is actually encountered, the parser presumes you tried to do a C style cast. Note that dropping the useless parens or doing *((x))++ works since the parser then knows that the outermost () hold a primary expression.

The behavior is according to the spec, so I will close this as invalid. 

[1] https://dlang.org/spec/grammar.html#UnaryExpression
Comment 2 anonymous4 2017-11-02 16:35:34 UTC
Is the grammar ordered? It expects braces through PowExpression: https://dlang.org/spec/grammar.html#PowExpression
Comment 3 Simen Kjaeraas 2017-11-03 08:01:41 UTC
(In reply to RazvanN from comment #1)
> The behavior is according to the spec, so I will close this as invalid. 

It's perfectly possible for the spec to be in error (there's even the spec keyword in Bugzilla). In this case, the code looks perfectly sensible for someone who doesn't know the spec in detail, and getting a compiler error like this is not expected.

Could the spec in this case be amended to allow the example code to compile, without breaking code elsewhere? I don't know. If you know it's impossible, please explain why, and close this bug again.
Comment 4 RazvanN 2017-11-03 09:50:15 UTC
(In reply to Simen Kjaeraas from comment #3)
> (In reply to RazvanN from comment #1)
> > The behavior is according to the spec, so I will close this as invalid. 
> 
> It's perfectly possible for the spec to be in error (there's even the spec
> keyword in Bugzilla). In this case, the code looks perfectly sensible for
> someone who doesn't know the spec in detail, and getting a compiler error
> like this is not expected.
> 
> Could the spec in this case be amended to allow the example code to compile,
> without breaking code elsewhere? I don't know. If you know it's impossible,
> please explain why, and close this bug again.

It's not that it is impossible, rather that it requires much effort to allow a redundant use of ().
Comment 5 anonymous4 2017-11-03 12:31:33 UTC
Ah, so the problem is that in expression (a[b]).c it's undecidable if the braced expression should be parsed as type or value.
Comment 6 Simen Kjaeraas 2017-11-03 14:47:45 UTC
You're at least partly right, though (a[b]).c does work. The smallest example of this bug is probably this:

unittest {
    (a)++;
}

That should of course fail to compile as well, but for other reasons. This should compile:

unittest {
    int a;
    (a)++;
}

Are there any cases where this is valid with 'a' being a type? No. The compiler even complains about exactly that: "C style cast illegal, use cast(a)++0". ++(T), where T has overloaded static opUnary, should and does work, but the postfix version does not, because the lowering doesn't make sense.

Another example of where this bug shows up is with call expressions:

void foo(int n) {}
unittest {
    (foo)(3);
}

(parameter added because the compiler gives up and spews other errors when getting empty parentheses)

For comparison, (a)++ compiles and runs with expected semantics in C#, C++ and Javascript.

It seems to me that this is in fact not a spec error, but that the parser is misbehaving when reporting this error. It gives up when it sees that the next character after (a) is not a period[0], while the grammar seems to support a test for PowExpression, to find that (a) is a PrimaryExpression on the form (Expression), and continue down that route. The error message is printed long before the parser knows that the stuff inside the parentheses is a type.

[0]: https://github.com/dlang/dmd/blob/1fa67d062b8d755b11722ea112af63cb34cc06b7/src/ddmd/parse.d#L7959
Comment 7 Simen Kjaeraas 2017-11-03 18:16:05 UTC
Unsurprisingly, the function call variant is somewhat unfixable. (T)(a+b) is a very common cast in C, and looks a lot like a function call where T is a function. Even (T)(1,2,3) is valid C.

It would probably be possible to make this an error at a later point - when we know that the content of the parentheses does evaluate to a type.

Anyways, PR: https://github.com/dlang/dmd/pull/7281
Comment 8 timon.gehr 2017-11-04 02:36:23 UTC
(In reply to RazvanN from comment #1)
> Issuing an error is the correct behavior. As you can see in the grammar [1], 
> *(x)++ is parsed the following way: *UnaryExpression. If a parenthesis is
> encountered, then the parser expects a type :
> (type).identifier/templateInstance.

That is the bug. The grammar allows the derivation

UnaryExpression
      \
    PowExpression
        \
      PostfixExpression
          \
        PostfixExpression ++
            \
         ( PrimaryExpression )
              \
              ...
                \
             Identifier

Aside: It is a bad idea to try and distinguish types from non-types in the parser, because it just can't. I guess this grew out of the way the parser and semantic analysis were developed. It's not a very good design.

> Since none of the above is actually encountered, the parser presumes you
> tried to do a C style cast. Note that dropping the useless parens

The parens were not useless, they just became useless because I created a reduced test case.

> or doing
> *((x))++ works since the parser then knows that the outermost () hold a
> primary expression.
> ...

That's a workaround.

> The behavior is according to the spec,

Certainly not. The spec is also bad though. Types should be part of the expression grammar.

> so I will close this as invalid. 

It's not invalid.

> 
> [1] https://dlang.org/spec/grammar.html#UnaryExpression
Comment 9 timon.gehr 2017-11-04 02:38:00 UTC
(In reply to timon.gehr from comment #8)
> (In reply to RazvanN from comment #1)
> > Issuing an error is the correct behavior. As you can see in the grammar [1], 
> > *(x)++ is parsed the following way: *UnaryExpression. If a parenthesis is
> > encountered, then the parser expects a type :
> > (type).identifier/templateInstance.
> 
> That is the bug. The grammar allows the derivation
> 
> UnaryExpression
>       \
>     PowExpression
>         \
>       PostfixExpression
>           \
>         PostfixExpression ++
>             \
>          ( PrimaryExpression )
>               \
>               ...
>                 \
>              Identifier
> 

Should have been:

UnaryExpression
      \
    PowExpression
        \
      PostfixExpression
          \
        PostfixExpression ++
            \
          PrimaryExpression
              \
           ( Expression )
                \
                ...
                  \
               Identifier
Comment 10 RazvanN 2017-11-06 14:10:17 UTC
(In reply to timon.gehr from comment #9)
> (In reply to timon.gehr from comment #8)
> > (In reply to RazvanN from comment #1)
> > > Issuing an error is the correct behavior. As you can see in the grammar [1], 
> > > *(x)++ is parsed the following way: *UnaryExpression. If a parenthesis is
> > > encountered, then the parser expects a type :
> > > (type).identifier/templateInstance.
> > 
> > That is the bug. The grammar allows the derivation
> > 
> > UnaryExpression
> >       \
> >     PowExpression
> >         \
> >       PostfixExpression
> >           \
> >         PostfixExpression ++
> >             \
> >          ( PrimaryExpression )
> >               \
> >               ...
> >                 \
> >              Identifier
> > 
> 
> Should have been:
> 
> UnaryExpression
>       \
>     PowExpression
>         \
>       PostfixExpression
>           \
>         PostfixExpression ++
>             \
>           PrimaryExpression
>               \
>            ( Expression )
>                 \
>                 ...
>                   \
>                Identifier

That is correct. I looked at the code which tests that the content of the parentheses is a type and at the UnaryExpression expression grammar so I presumed that you cannot have the given construct. I understand now that indeed this is not acceptable behavior. Thanks
Comment 11 Seb 2018-05-05 15:42:49 UTC
PR https://github.com/dlang/dmd/pull/8218
Comment 12 RazvanN 2022-10-24 13:30:29 UTC
*** Issue 15463 has been marked as a duplicate of this issue. ***
Comment 13 Dlang Bot 2023-07-13 19:36:06 UTC
@ntrel created dlang/dmd pull request #15410 "Fix Issue 7184 - parse error on *(x)++" fixing this issue:

- Fix Issue 7184 - parse error on *(x)++

https://github.com/dlang/dmd/pull/15410
Comment 14 Dlang Bot 2023-07-14 09:48:28 UTC
dlang/dmd pull request #15410 "Fix Issue 7184 - parse error on *(x)++" was merged into master:

- 50e5492929f4afe27177824371407094d93029dc by Nick Treleaven:
  Fix Issue 7184 - parse error on *(x)++

https://github.com/dlang/dmd/pull/15410