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
DFE fails to resolve methods inherited from mixin #29758
Comments
Here is a snippet that demonstrates the problem:
Dart VM is happy to parse and run it:
Both dartanalyzer and dart2js complain about Mixin referencing super
|
dart2js and dartanalyzer errors perhaps should be disregarded as they seem to be out of date with DEP https://github.com/gbracha/lessRestrictedMixins. Per that DEP, DFE seems to be actually correct with it's diagnostic that |
I believe that the proposal requires an "extends" clause and not just "implements". dart2js hasn't implemented this DEP. Edit: since Peter assigned this issue to 'area-fasta', I'm guessing this might be related to the transition of the analyzer to the new frontend. |
correct that dart2js doesn't implement it, analyzer has it under the |
Ian provided good feedback that using 'extends' on mixin declaration leaves possibility to extend mixin directly outside of mixin application. On the other hand, if you use 'implements', then attempt to extend mixin will result in analyze-time error. See http://shortn/_5rg2Fqu4Qi for the example. |
From an implementation perspective, it would be extremely helpful if the work around of adding the superclass as specified could be used for now until the the language team has had a chance to hear feedback. |
Sure, Peter, I agree! |
The DEP has been implemented, so it's superseded by the ECMA language specification (4th Edition, Dec 2015), which contains the "new" mixin/super specification. It's correct what Florian says, to have a So class B { int foo() => 37; }
class M extends B { int bar() => super.foo(); }
class A implements B { int foo() => 42; }
class N = A with M; This works, For the example above, if |
@lrhn in your example, M has no superclass that provides a |
Thanks, fixed. |
Phew :-) |
@lrhn , thanks! Can you please weigh in on options(if any?) on how to make mixin classes final(not extendable)? |
If you want to prevent people from subclassing any class, just add this:
This also works for mixins. |
Ah, sorry, I meant prevent subclassing with analysis-time(rather than runtime) error(like http://shortn/_5rg2Fqu4Qi describes). |
This will also be a warning at analysis time. |
Currently there seem to be no warnings produced by dartanalyzer. What the warning would be about?
|
This is what you want:
|
Ah, got it, Thanks Peter! |
|
@aam do you have a list of the places where Flutter is affected by this change? I'm happy to apply the fix. |
Thank you, @Hixie |
So looking at this more closely (@aam sent me the list of places to change), there is another problem with this. How do you specify that a mixin expects to only be used in a situation that also mixes in two other (unrelated) classes?
Here's a reduced version of this problem, in the syntax that works today: class RealSuper {
RealSuper(this.quux);
final int quux;
}
abstract class Mixin1 extends RealSuper {
factory Mixin1._() => null;
void foo() {
print("Mixin1.foo $quux");
}
}
abstract class Mixin2 extends RealSuper {
factory Mixin2._() => null;
void bar() {
print("Mixin2.bar $quux");
}
}
abstract class Mixin3 extends RealSuper implements Mixin1, Mixin2 {
factory Mixin3._() => null;
void baz() {
print("Mixin3.baz $quux");
}
@override
void foo() {
print("Mixin3.foo $quux");
super.foo();
}
@override
void bar() {
print("Mixin3.bar $quux");
super.bar();
}
}
class C extends RealSuper with Mixin1, Mixin2, Mixin3 {
C() : super(1);
}
void main() {
new C()
..foo()
..bar()
..baz();
} How do I do this if we can't use "implements"? |
@Hixie, I'm not sure I understand your question, but if I add these two methods to RealSuper, your example works:
Did you mean to make C extend RealSuper? |
RealSuper doesn't know about Abstract classes Mixin1 and Mixin2 introduce new features that can be used by concrete classes that implement the RealSuper interface. Mixin3 is intended for use by such concrete classes that mix in both Mixin1 and Mixin2. C is a class that mixes all three in. |
Is this what you're looking for:
|
Aha, yup, that seems to work. Let me try to deploy that. Thanks! |
class RealSuper {
RealSuper(this.quux);
final int quux;
}
abstract class Interface {
factory Interface._() => null;
void foo();
}
abstract class Mixin1 extends RealSuper with Interface {
factory Mixin1._() => null;
@override
void foo() {
print("Mixin1.foo $quux");
}
}
// This mixin wants to be mixed into a class that inherits from a class that
// extends RealSuper and implements Interface. It doesn't know about Mixin1,
// which allows classes to easily satisfy those conditions.
abstract class Mixin2 extends RealSuper implements Interface {
factory Mixin2._() => null;
@override
void foo() {
super.foo();
print("Mixin2.foo $quux");
}
}
class C extends RealSuper with Mixin1, Mixin2 {
C() : super(1);
}
void main() {
new C()
..foo();
} |
Here you have When you later try to apply The example is equivalent to the declarations: class Anon = RealSuper with Interface;
abstract class Mixin1 extends Anon { ... }
class C extends RealSuper with Mixin1, Mixin2 { ... } // Warning, RealSuper doesn't implement Anon. (If you meant to write |
@lrhn , currently the only warning DFE produces is at line 26 in method
because |
The only warnings I got from the last code snippet I pasted were that I hadn't documented the public APIs. It works great. :-) |
If you're just running it in the VM, then you won't get any warnings (since the VM just don't do static warnings), but if the analyzer doesn't warn, then it's a bug.
and
together means that your program should have a warning. Looking at
|
As implemented, this works, and is very useful. If the spec is less useful we should fix the spec. |
That's right, @kasperl , |
Yeah, we're looking for a way to do what is shown in the last snippet above: #29758 (comment) |
@peter-ahe-google: This issue is currently assigned to you, Peter. Are you planning any changes here or did this issue just happen to get stuck? @aam: Do you know what the current state of affairs is? |
Any class that contains a method with a super call must extend a class that provides the method. One way to accomplish that is to make sure that Interface.foo isn't an abstract method and have Mixin2 mixin Interface:
Using this approach, Mixin2 can be analyzed as a standalone class whose super calls are all fully resolved. The guide line is: if you want to use super in a mixin, the mixin must be defined as a subclass of another class (or mixin application) that provides a non-abstract version of that method. This is no different from any other class that uses super. I'm not sure why you define Mixin1 and Mixin2 as subclasses of RealSuper, this would be just as fine:
|
That's not a solution, because Interface is out of your control (e.g. it comes from another package). |
(In our specific case, Interface is actually an abstract class that other people extend.) |
This case isn't academic, it's something that's deployed today in Flutter's framework, which works fine with today's VM and analyzer. |
An alternative to adding the concrete implementation to Interface, you can add it to a private class:
|
How should we handle this program:
|
I think the preferred way forward is to think about this as a language issue that probably needs both a short term and a longer term solution. @floitschG @lrhn: Are you okay with taking this issue over? |
Just to be clear: What is "this issue"? There has been a lot of discussion about various short-comings of the current mixin specification. That is:
is a solution to the original issue. There are two other issues mentioned:
For the former, we can add a warning, something like:
The analyzer can give such a hint today if it wants to, we don't need to specify it now, but I'm sure it will become an official warning (strong-mode error) when we do address mixins properly. For the "cannot have multiple super-constraints", I'm not sure there is a short-term solution, unless doing the proper redesign can be done in the short term. |
I would imagine it would be handled the same as today.
You could also catch this at compile time, when |
@Hixie, @aam, @lrhn: For what it's worth, I have a patch to the new frontend that allows the code in comment #29758 (comment) to run without issues. This could be a way to make progress and ease the transition to the new frontend -- even if the code is not conforming to the current specification. The patch disables the warning on |
In Flutter code
super.attach(owner);
inRenderObjectWithChildMixin
that is used as a mixin forRenderView
should be resolved toRenderObject#attach
but it gets unresolved:The text was updated successfully, but these errors were encountered: