Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion issue for grammar ambiguities with non-nullable types #144

Closed
Tracked by #110
leafpetersen opened this issue Dec 17, 2018 · 12 comments
Closed
Tracked by #110

Discussion issue for grammar ambiguities with non-nullable types #144

leafpetersen opened this issue Dec 17, 2018 · 12 comments
Assignees
Labels
nnbd NNBD related issues

Comments

@leafpetersen
Copy link
Member

leafpetersen commented Dec 17, 2018

This issue is to discuss and resolve any parsing ambiguities associated with non-nullable type syntax.

Known potential ambiguities:

  • a is int ? - 3 : 3;
    • this is valid pre-NNBD code, does it change meaning after NNBD?

cc @lrhn @munificent @eernstg @danrubel

@danrubel
Copy link

My understanding is that the code above should be interpreted the same in both situations. It is the parser's responsibility to perform arbitrary lookahead in this situation to determine that the ? is part of a conditional expression and not part of the named type.

@lrhn
Copy link
Member

lrhn commented Dec 18, 2018

With set/map literals, {a as int?-3:3} is either a set literal with a conditional expression or a map literal with 3 as value and a as int?-3 as key ... except that we probably wouldn't allow the - operations on a nullable type. Not sure that helps us when parsing, but it might suggest which disambiguation to choose.

Also, I feared that a is int?.toString() would be valid syntax, but we seem to not allow . or ?. after the is-check (we do allow .. though, so if we want to have ?.. as well, it might become a problem).

Maybe we could change the syntax for is and as so they delimit the type: x.is<int>, x.as<int>, with the extra benefit of working better in chains of computations.

The cheapest parsing algorithm would always combine ? with a previous potential type, but that would also prevent us from allowing int? as a type literal, because then someVar ? foo : bar wouldn't parse (someVar? is a type expression, followed by foo, which is just wrong).

The next simpler algorithm would never combine a type with ? if it can possibly be parsed any other way, so a is int?.toString() would "work". That may require arbitrary look-ahead.

I'm sure there are variants in-between, where the parsing depends on the position in the code (is a type expected here or not, with x is ... ? ... as the problem case.

@munificent
Copy link
Member

This is a gnarly one. Let's say we take:

{ a as int ? - 3 : 3 }

and default to interpreting it as:

{ (a as int) ? -3 : 3}

If that's not what you want, how do you get the other interpretation where ? is part of the type? We don't currently allow you to parenthesize a type annotation. So one option is to allow that, then require:

{ a as (int?) -3 : 3}

That feels a little weird to me because in typical formatting, the ? already looks like it binds tighter as a type (no space before the ?) than as a conditional operator.

The other option is to default the other way. Treat the ? as part of the type. If you want it to not be, you have to explicitly parenthesize:

{ (a as int) ? -3 : 3}

I think we could probably do this by shuffling around the grammar so that a conditional expression's conditional prohibits a testTest or testCast expression. You'd have to get through it by going through primary and parenthesizing.

This is a breaking change and, unfortunately, I think a fairly common one.

@leafpetersen
Copy link
Member Author

This is a gnarly one. Let's say we take:

{ a as int ? - 3 : 3 }

and default to interpreting it as:

{ (a as int) ? -3 : 3}

If that's not what you want, how do you get the other interpretation where ? is part of the type?

Seems to me that asking whether a is int? is probably something you essentially never want to do. We currently don't provide any way to do this (if you ask a is int in current Dart, it returns false for null).

Perhaps we should just disallow T? in is checks, or else allow it but prefer the (a is int) ? e1 : e2 parsing? If you really want to do an is check on a nullable type then you can use a type alias (once we have general type aliases).

@yjbanov
Copy link

yjbanov commented Dec 18, 2018

When would one want A ? to mean nullable A? I think A? (i.e. no space between A and ?) is the only reasonable way to state it. The same goes for the conditional operator. There has to be whitespace preceding ?.

@bwilkerson
Copy link
Member

This is a gnarly one. Let's say we take:

{ a as int ? - 3 : 3 }

and default to interpreting it as:

{ (a as int) ? -3 : 3}

If that's not what you want, how do you get the other interpretation where ? is part of the type?

Assuming I needed to write that, I'd want to write it as { (a as int?) - 3 : 3}.

@munificent
Copy link
Member

Seems to me that asking whether a is int? is probably something you essentially never want to do.

I suppose so, but it feels weird to prevent a semantically meaningful operator for syntactic reasons. If we add other kinds of type annotations, we'll probably run into this problem again.

I can also imagine code that might want to do this. Consider some API that accepts int, null, or String, and forwards the first two to some other API that accepts int?.

Assuming I needed to write that, I'd want to write it as { (a as int?) - 3 : 3}.

Good point. :)

@leafpetersen
Copy link
Member Author

Ok, so for the conditional expression ambiguity, it sounds like we can resolve by first trying to parse as a conditional expression, and so prefer the parse (a is int) ? -3 : 3 over the parse (a is int?) -3 : 3.

@danrubel
Copy link

danrubel commented Dec 19, 2018 via email

@leafpetersen
Copy link
Member Author

A note on grammar: the grammar for types will only allow a single ?. That is:

type' ::= functionType
          | qualified typeArguments? 

type ::= type' `?`?

So

  int ? ? x = 3;

is a parse error.

Also, int??String parses as (int) ?? (String).

@leafpetersen leafpetersen added the nnbd NNBD related issues label Jan 23, 2019
@danrubel
Copy link

Agreed. That is the case and I've just added parser tests to ensure as much.

@lrhn
Copy link
Member

lrhn commented Feb 3, 2020

The grammar has now been defined, including the ambiguity introduced by null-aware index operators.

@lrhn lrhn closed this as completed Feb 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
nnbd NNBD related issues
Projects
None yet
Development

No branches or pull requests

6 participants