-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Insert type checks when necessary when we redirect factories in place #31590
Comments
Even in strong mode, where returning something not
|
As Johnni mentioned, I agreed that it seems appropriate to check every step of a redirection chain, but also that the current language specification does not imply firmly that these checks should be performed. For a redirecting generative constructor it says that
and for a redirecting factory constructor it says
The latter pretty clearly specifies that one invocation should be replaced by the other invocation (repeatedly, if a redirecting factory constructor redirects to another redirecting factory constructor), and the former at least allows for that interpretation. Hence, it seems reasonable to claim that static and dynamic checks should not be performed on the actual arguments relative to the invocations that were replaced, it's only necessary to check the final invocation. I'll write a proposal for a specification update saying that all these checks must be performed. This could presumably be implemented as a single check per argument where it's needed, and a single check on the result if needed, where the check will succeed if and only if all the corresponding checks would have succeeded. So, presumably, it won't have to cost anything, it would just be more correct. ;-) With that proposal in the pipeline I'd still need to get buy-in from Lasse and Leaf, but we would at least know how far we've come in the clarification of this issue. |
Here's the CL for that. Note that there is no guarantee at this point that the language will indeed be adjusted to follow this specification. If the specification is adjusted as proposed in that CL, I believe that all the static and dynamic checks requested in this issue would be correct. However, we still need to resolve exactly which checks that would be (in the current version of the CL, redirecting factory constructors allow downcasts during redirection, but redirecting generative constructors do not). We may want to change that, for consistency, but the current (seemingly inconsistent) approach arose for a reason, so it's not completely obvious what to do. |
Updated said CL; at this point it does not suffer from the problem described above (redirecting generative & factory constructors are now treated much more uniformly, and they do allow downcasts during redirection, and it is checked). |
The spec CL is in rather good shape now. It's not yet done, reviews awaited. Note that downcasts are allowed during the redirection for a redirecting generative constructor, but not for a redirecting factory constructor. This is what Lasse and I concluded was the most reasonable approach (and I think we're lucky that this is what strong mode does already ;-). |
Is this spec change going in? |
My latest remark in the CL is 'PTAL', I'll send a reminder. I do not think that there is anything in there which is about the substance, so it should be safe to assume that it will be landed with the same meaning. |
The current specification seems to allow (or even mandate) that a redirecting constructor (factory or generative) must be considered as a syntactic shorthand for an invocation of the redirection target, with no checks applied statically or dynamically according to the declarations in the redirected constructor. So the following would be OK: class A { A(num n) { print(n); } A.foo(int i): this(i); } main() => new A.foo(3.0); This CL changes dartLangSpec.tex to mandate all the checks (static and dynamic) for the declaration of the redirecting constructor as well as each one of the redirection targets. Note that the analyzer already rejects the above program, which lessens the disruption and the implementation burden, but compilers would presumably need to have the dynamic checks implemented. Underlying issues: #31590, #32049, #32511. Change-Id: Icc15da6b817e4e678cdfc8829a1e06458756eb4b Reviewed-on: https://dart-review.googlesource.com/28140 Reviewed-by: Leaf Petersen <leafp@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Said CL has been landed in 715d597. Note in particular that a generative redirecting constructor passes arbitrary arguments on to its redirectee and they are subject to an assignability check (and a dynamic check if there's a downcast), whereas a factory redirecting constructor passes exactly the same arguments along that it receives, and in that case the types must be safe (so each parameter type of the redirecting constructor must be the same as the corresponding parameter of the redirectee, or a subtype thereof—no downcasts allowed and hence no dynamic checks needed). Of course, the original invocation of a redirecting factory constructor may contain downcasts, it's just the step from the redirecting constructor to the redirectee which cannot involve a downcast. Other than that, I don't think there should be anything particularly surprising. |
The current specification seems to allow (or even mandate) that a redirecting constructor (factory or generative) must be considered as a syntactic shorthand for an invocation of the redirection target, with no checks applied statically or dynamically according to the declarations in the redirected constructor. So the following would be OK: class A { A(num n) { print(n); } A.foo(int i): this(i); } main() => new A.foo(3.0); This CL changes dartLangSpec.tex to mandate all the checks (static and dynamic) for the declaration of the redirecting constructor as well as each one of the redirection targets. Note that the analyzer already rejects the above program, which lessens the disruption and the implementation burden, but compilers would presumably need to have the dynamic checks implemented. Underlying issues: dart-lang/sdk#31590, dart-lang/sdk#32049, dart-lang/sdk#32511. Change-Id: Icc15da6b817e4e678cdfc8829a1e06458756eb4b Reviewed-on: https://dart-review.googlesource.com/28140 Reviewed-by: Leaf Petersen <leafp@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
This CL was landed in 9282a34, and this changes the specification to require dynamic checks (if needed) on actual arguments passed to a redirecting factory constructor: class A implements B {
var x;
A(Object this.x);
}
class B {
factory B(String s) = A;
}
main() {
B(42 as dynamic);
} With the old specification the invocation of the In other words, with respect to actual arguments, this spec update introduces the requirement for exactly the kind of checking that @efortuna requested in the first place (and it was a mistake that the spec previously did not require them). On the other hand, there is no need to check the type of the returned object dynamically, because the spec always (since Ivan Posva committed it in 2013, at least ;-) required the redirectee to "have a function type" which is a subtype of the function type of the redirecting factory constructor. The new spec update makes sure that we take type arguments into account when we compute the function type of the redirectee, but that's just a bug fix and the intention hasn't changed. |
This seems to have been fixed a while back. All of the examples now give the proper runtime error. |
Currently, the kernel FE compiles redirecting factories like so:
(dart code)
class A {
A(int i) {}
A.foo(int j) = A;
}
main() {
new A.foo(3);
}
(kernel)
class A extends core::Object {
static field dynamic _redirecting# = [self::A::foo];
constructor •(core::int i) → void
: super core::Object::•() {}
static factory foo(int j) → self::A
let dynamic #redirecting_factory = self::A::• in invalid-expression;
}
static method main() → dynamic {
new self::A::•(3);
}
Specifically, that is, instead of calling "new A.foo(3)" we directly call "new A(3)" which is what the redirecting factory constructor calls. Great.
The problem is, sometimes a redirecting constructor can return a subtype or something that's not related type-wise at all. We need to insert a check at the call-site when compiling in "checked mode" and strong mode (something like
(new self::A::•(3) as A)
though it doesn't really make sense in this trivial example) since we're essentially doing inlining on behalf of the redirecting factory constructor to ensure that the inlined type is indeed the type claimed by the factory constructor.@johnniwinther said he talked with @eernstg about this issue.
The text was updated successfully, but these errors were encountered: