Skip to content

Raw types in bounds should be allowed when the raw type bound is ground #28580

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

Closed
leafpetersen opened this issue Jan 31, 2017 · 30 comments
Closed
Assignees
Labels
legacy-area-analyzer Use area-devexp instead. P2 A bug or feature request we're likely to work on type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Milestone

Comments

@leafpetersen
Copy link
Member

If a parameterized type A is used somewhere in a type bound with no type arguments, it is currently treated as an error in strong mode unless the type parameter to A has no bound. This should be relaxed to also allow this in any case where the bound on the type parameter to A does not reference any other type parameters to A. Examples:

class A<T> {}

/// Already allowed, A is treated as A<dynamic>
class B<T extends A> {}

class C<T extends A<int>> {}

/// Currently disallowed, should be allowed, C is treated as C<A<int>>
class D<T extends C> {}

/// Currently disallowed, should be allowed, List<C> is treated as List<C<A<int>>
class E<T extends List<C>> {}
@bwilkerson bwilkerson added P2 A bug or feature request we're likely to work on type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Jan 31, 2017
@mit-mit
Copy link
Member

mit-mit commented Jan 31, 2017

This does not block 1.22; tagging 1.23

@mit-mit mit-mit added this to the 1.23 milestone Jan 31, 2017
@eernstg
Copy link
Member

eernstg commented Feb 1, 2017

As I argued elsewhere, we should consider treating A as A<Object> rather than A<dynamic>: Allowing dynamic invocation of arbitrary members on expressions whose type is the type argument of A is a non-trivial source of unsoundness, so we may wish to ask programmers to request that explicitly—and write the type they want if they are actually not happy about Object.

@scheglov scheglov self-assigned this Feb 11, 2017
@scheglov
Copy link
Contributor

scheglov commented Feb 11, 2017

I implemented this change, partially.

There is a problem though. If we allow "instantiate to bounds" for bounds, we cannot resolve bounds in arbitrary order, we need to do this in dependency order. This works with the new analysis driver, because we can resynthesize in any order. But not with the old tasks based analysis. And we're going to remove it in the near future, so I think it is not worth making it work with the old implementation.

@dgrove
Copy link
Contributor

dgrove commented Mar 15, 2017

Any updates here?

@scheglov
Copy link
Contributor

I'm working on moving DDC to the new analysis driver, so that we could use resynthesized types.
Once this is done, we will be unblocked to implement this feature only in one place.

@dgrove
Copy link
Contributor

dgrove commented Mar 20, 2017

Is this likely to make it for 1.23?

@scheglov
Copy link
Contributor

Yes, I think so.

@scheglov
Copy link
Contributor

scheglov added a commit that referenced this issue Mar 21, 2017
This feature works only with the new analysis driver, because it is
implemented only in summaries linker.

R=brianwilkerson@google.com, leafp@google.com
BUG= #28580

Review-Url: https://codereview.chromium.org/2762863002 .
@scheglov
Copy link
Contributor

ef527a9

@scheglov
Copy link
Contributor

Unfortunately, I had to roll the change back. There are problems that I was not able to fix quickly enough. At this point I don't think I will able to make it for 1.23.

@scheglov scheglov reopened this Mar 22, 2017
@scheglov scheglov removed their assignment Mar 22, 2017
@leafpetersen leafpetersen modified the milestones: 1.24, 1.23 Mar 24, 2017
@dgrove
Copy link
Contributor

dgrove commented May 12, 2017

Is this happening for 1.24?

@dgrove
Copy link
Contributor

dgrove commented May 23, 2017

@leafpetersen is this happening for 1.24? (ie, this week)

@leafpetersen leafpetersen modified the milestones: 1.25, 1.24 May 24, 2017
@leafpetersen
Copy link
Member Author

Moving to 1.25. @scheglov @bwilkerson is this feasible to implement in the analyzer for 1.25, or should we leave this to the new front end to resolve?

@devoncarew devoncarew added this to the 2.0-alpha2 milestone Jan 26, 2018
@devoncarew
Copy link
Member

@leafpetersen, is this a blocker for alpha2? If no, I'll move it out of the milestone.

@devoncarew devoncarew modified the milestones: 2.0-alpha2, 2.0-beta1 Feb 16, 2018
@leafpetersen
Copy link
Member Author

Moving it out is fine.

@bwilkerson
Copy link
Member

Closing as obsolete. Please re-open if that's not the case.

@leafpetersen
Copy link
Member Author

This still seems to reproduce with bleeding edge analyzer.

@dgrove
Copy link
Contributor

dgrove commented Mar 22, 2018

@bwilkerson - can you please move this to Dart2-beta3/beta4/stable, or remove the milestone?

@bwilkerson bwilkerson modified the milestones: (Please move issues from the milestone) 2.0-beta1, Dart2 Beta 3, Dart2 Beta 4 Mar 23, 2018
@MichaelRFairhurst MichaelRFairhurst self-assigned this Apr 4, 2018
@bwilkerson
Copy link
Member

@MichaelRFairhurst Any update?

@MichaelRFairhurst
Copy link
Contributor

I ran into merge issues trying to patch in the reverted CL since its pretty out of date at this point. Didn't get it working by the time I got derailed.

Thanks for the reminder, I'll get back on it.

@MichaelRFairhurst
Copy link
Contributor

Just to give an update on the progress of this, I was able to merge the reverted change and update it to work for function types.

I did however just hit a stack overflow that will be tough to fix. Now the problem is that linking the summaries depends on instantiating to bounds, which requires linking summaries.

a.dart

class A<T extends B> {}

b.dart

class B<T extends A> {}

Since the infinite loop here is caused by typeA.instantiateToBounds() calling typeB.typeArguments calling typeB.instantiateToBounds(), I'm not sure I see a place where I can easily track & break this.

I will continue to work on it but may need to go over it with @scheglov

@eernstg
Copy link
Member

eernstg commented Apr 13, 2018

In the example, B must have a simple bound (search 'We say' in this document), which is true if A has a simple bound, which is true if B has a simple bound, and it is specifically stated that such a circularity implies that the answer is 'No'. This means that the example has a compile-time error.

So the function that determines whether a given bound is simple should keep a set of bound declarations visited so far, and return false if at any point a bound declaration is visited which is already in that set.

Note that it is not sufficient to check whether you ever get back to the first bound, because you can have eventually-cyclic sequences like 1, 2, 3, 4, 3, 4, 3, 4, ..., where you never revisit 1.

So how does that fit into the current implementation, is there no such bool simpleBound(...)? Does it get lost in modular compilation and summaries and stuff? Otherwise, it should be doable, and hopefully not too costly. In a lot of cases it may be possible to avoid allocating an actual Set, which might be important; maybe it would be useful to call a depth-limited function first (that is naïve in that it ignores cycles), taking an int depth argument and bailing out at a fixed depth (say, 5), and then calling the expensive one that uses a Set if the naïve function couldn't conclude anything.

@MichaelRFairhurst
Copy link
Contributor

MichaelRFairhurst commented Apr 13, 2018 via email

@eernstg
Copy link
Member

eernstg commented Apr 13, 2018

Before a bound B is instantiated to its bounds there should be a check that B has simple bounds. That should be enough to catch the cycle.

@MichaelRFairhurst
Copy link
Contributor

MichaelRFairhurst commented Apr 13, 2018 via email

@eernstg
Copy link
Member

eernstg commented Apr 13, 2018

I shouldn't try to guess my way through these things because I don't know the CFE implementation details. But it seems reasonable to say that the deserialization should check that bounds are simple, because it's apparently executing the instantiate-to-bound operation that shouldn't even be attempted. Maybe the CFE should attach some kind of metadata to each type variable indicating whether it has or does not have a simple bound, such that the answer comes directly, pre-computed, from the serialized data?

@chloestefantsova
Copy link
Contributor

@eernstg I think @MichaelRFairhurst is talking about the implementation in analyzer, not CFE. Fasta is going to have the check for simple bounds soon (see CL 41264). The work on this check was postponed a while ago because of other higher priority tasks.

FWIW, instantiate-to-bound is run eagerly in fasta, and there's no need to run it whenever we want to access type variables.

@eernstg
Copy link
Member

eernstg commented Apr 13, 2018

OK, thanks @stefantsov! — and it still stands that I should just stop guessing at implementation details. ;)

@MichaelRFairhurst
Copy link
Contributor

a few hurdles overcome to get to: https://dart-review.googlesource.com/c/sdk/+/52062

dart-bot pushed a commit that referenced this issue Apr 20, 2018
One thing to be careful of here is that function type bounds may be
left unbounded. However, their parameters and return type cannot refer
to a type parameter for resolve-to-bounds to work.

There are also a large number of ways that this can loop infinitely, and
a number of new guards had to be added (as well as some test cases) to
catch them all.

Change-Id: I14322f5f71d1f7b73b27a870553e5c588b2c7e2e
Reviewed-on: https://dart-review.googlesource.com/52062
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
@MichaelRFairhurst
Copy link
Contributor

This change has landed, and hopefully it sticks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legacy-area-analyzer Use area-devexp instead. P2 A bug or feature request we're likely to work on type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

9 participants