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

Instantiate to bounds for error cases is different in Analyzer and FrontEnd #31074

Closed
scheglov opened this Issue Oct 11, 2017 · 6 comments

Comments

4 participants
@scheglov
Contributor

scheglov commented Oct 11, 2017

class C<T extends C<T>> {}
C c;
true
library;
import self as self;
import "dart:core" as core;

class C<T extends self::C<self::C::T>> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
}
static field self::C<dynamic> c;

Analyzer produces C<C<dynamic>> c.
FrontEnd produces C<dynamic> c.

https://dart-review.googlesource.com/c/sdk/+/5765 does not give exact answer, but as I understand it, I think that the front-end behavior is closer to the specification proposal, because

If no bound is given in Fj, the selected value for Xj is dynamic.

and

If this process terminates then the selected value is the final
value of the bound in Fj. If it does not terminate, the instantiate
to bounds process has failed.

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Oct 12, 2017

Member

Either choice is a valid super-bounded type. The question is which is more useful to users.
If the class had been declared as class C<T extends Comparable<T>> {} instead, then it makes some sense to pick C<Comparable<dynamic>> over C<dynamic> because the former helps the user more.
That is, it expands T to the bound but replaces recursive references by dynamic, rather than just giving up on expanding and replacing T with dynamic at the top. (One vs zero expansions of the bound, either will do, two is right out).

Member

lrhn commented Oct 12, 2017

Either choice is a valid super-bounded type. The question is which is more useful to users.
If the class had been declared as class C<T extends Comparable<T>> {} instead, then it makes some sense to pick C<Comparable<dynamic>> over C<dynamic> because the former helps the user more.
That is, it expands T to the bound but replaces recursive references by dynamic, rather than just giving up on expanding and replacing T with dynamic at the top. (One vs zero expansions of the bound, either will do, two is right out).

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Oct 12, 2017

Member

Konstantin, thanks for the input! I haven't finished, and it's getting late here in CEST, so I'll finish the updates to instantiate-to-bound.md tomorrow.

Edit next day: The informal spec CL has now been landed. The issue about whether we will or will not create super-bounded types implicitly will be resolved separately.

Member

eernstg commented Oct 12, 2017

Konstantin, thanks for the input! I haven't finished, and it's getting late here in CEST, so I'll finish the updates to instantiate-to-bound.md tomorrow.

Edit next day: The informal spec CL has now been landed. The issue about whether we will or will not create super-bounded types implicitly will be resolved separately.

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Oct 13, 2017

Member

Added the other issue ("enhance instantiate-to-bound such that it also works with a typedef") to the same discussion where the language team works on "create super-bounded types?".

Member

eernstg commented Oct 13, 2017

Added the other issue ("enhance instantiate-to-bound such that it also works with a typedef") to the same discussion where the language team works on "create super-bounded types?".

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Nov 17, 2017

Member

instantiate-to-bound.md has been debated again and the ability to unfold an infinite type once and create a super-bounded type has been added. In other words, class C<X extends C<X>> {...} allows for raw usages like C c, which means C<C<dynamic>>. If the developer prefers to make it an error when we step outside the unfolding, it's possible to use an explicit super-bounded type like C<C<Object>> (so all member lookups except for the members of Object will now be compile-time errors when we step outside) or even, when we support that, C<C<void>> (so that even touching the object where the static typing stops will be an error).

The relevant CL is here, and I expect it to land very soon.

Member

eernstg commented Nov 17, 2017

instantiate-to-bound.md has been debated again and the ability to unfold an infinite type once and create a super-bounded type has been added. In other words, class C<X extends C<X>> {...} allows for raw usages like C c, which means C<C<dynamic>>. If the developer prefers to make it an error when we step outside the unfolding, it's possible to use an explicit super-bounded type like C<C<Object>> (so all member lookups except for the members of Object will now be compile-time errors when we step outside) or even, when we support that, C<C<void>> (so that even touching the object where the static typing stops will be an error).

The relevant CL is here, and I expect it to land very soon.

@kmillikin kmillikin added this to Incoming in Dart Front End Jan 3, 2018

@jensjoha

This comment has been minimized.

Show comment
Hide comment
@jensjoha

jensjoha Jan 10, 2018

Contributor

Compiling

class C<T extends C<T>> {}
C c;
class D<T extends Comparable<T>> {} 
D d;

with --strong yields the following:

library;
import self as self;
import "dart:core" as core;

class C<T extends self::C<self::C::T>> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
}
class D<T extends core::Comparable<self::D::T>> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
}
static field self::C<self::C<dynamic>> c;
static field self::D<core::Comparable<dynamic>> d;

which seems to be what we want. Can we close this issue?

Contributor

jensjoha commented Jan 10, 2018

Compiling

class C<T extends C<T>> {}
C c;
class D<T extends Comparable<T>> {} 
D d;

with --strong yields the following:

library;
import self as self;
import "dart:core" as core;

class C<T extends self::C<self::C::T>> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
}
class D<T extends core::Comparable<self::D::T>> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
}
static field self::C<self::C<dynamic>> c;
static field self::D<core::Comparable<dynamic>> d;

which seems to be what we want. Can we close this issue?

@jensjoha jensjoha moved this from Incoming Untriaged to Verify Fixed in Dart Front End Jan 10, 2018

@scheglov

This comment has been minimized.

Show comment
Hide comment
@scheglov

scheglov Jan 10, 2018

Contributor

SGTM

Contributor

scheglov commented Jan 10, 2018

SGTM

@scheglov scheglov closed this Jan 10, 2018

@jensjoha jensjoha moved this from Verify Fixed to Done in Dart Front End Jan 11, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment