Sdk: Error creating super mixin on class with no default 0 arg ctor

Created on 31 Oct 2018  路  14Comments  路  Source: dart-lang/sdk

Repro:

class NoDefaultZeroArgCtor {                                                     
  NoDefaultZeroArgCtor(int x);                                                   

  int foo() => 122;                                                              
}                                                                                

mixin FooMixin on NoDefaultZeroArgCtor {                                         
  int foo() => super.foo();                                                      
}                                                                                

class UsesFoo extends NoDefaultZeroArgCtor with FooMixin {                       
  UsesFoo(int x) : super(x);                                                     
}                                                                                

void main() {                                                                    
  print(new UsesFoo(0).foo());                                                   
} 

via dart:

lib/mixin.dart:1: Warning: Interpreting this as package URI, 'package:test/mixin.dart'.
lib/mixin.dart:1: Error: The superclass, 'NoDefaultZeroArgCtor', has no unnamed constructor that takes no arguments.

via dartanalyzer:

Analyzing lib/mixin.dart...
No issues found!

The latter seems desirable to me, though I can only hazard a guess at if there's a good reason why the former is the required/correct behavior.

P1 area-front-end type-bug

All 14 comments

Classifying as "area-language" for now since we're not sure whether analyzer or front end behavior is correct. @leafpetersen, @eernstg, @lrhn, can you let us know which behavior is correct, so we can reassign the bug to analyzer or front end as appropriate?

The analyzer is correct, and so is the code.

Mixin declarations are not class declarations, and their super-class constraints are not super-classes. Mixin declarations have no constructors, and they do not need to call a super-constructor, so should make no difference which constructors their super-constraint classes have.

I get the same error from both dart2js and the VM, so it's most likely a front-end issue.
(It may be that both back-ends have implemented the same error with the same error message, but it's less likely than it being a front-end error).

If I change the code to:

class X {}
class NoDefaultZeroArgCtor implements X {                                                     
  NoDefaultZeroArgCtor(int x);                                                   
...
}                                                                                

mixin FooMixin on NoDefaultZeroArgCtor, X {    
...

then the program runs. It seems that the front-end is introducing an abstract interface in the case where there are more than one super-constraint, and that interface does have an unnamed nullary constructor, so their hack works. It should work in the one-constraint case too.

@kmillikin

The corresponding code with old-style super mixins would have had:

class FooMixin extends NoDefaultZeroArgCtor {                                         
  int foo() => super.foo();                                                      
}

which would have signaled the same error; and there was no way to repair it by adding a constructor to FooMixin because then it couldn't be used as a mixin.

So this is a problem affecting new code using mixin declarations, but not for porting existing code using old-style super mixins because it wouldn't have worked in the first place.

I'm testing a fix.

I'm running into this bug with the 2.1.0 candidate.

/cc @JekCharlsonYu @dgrove

See https://travis-ci.org/dart-lang/site-www/builds/455227237. I've got a smaller repro, if you want it.

Here's the smaller repro (passes analysis but fails to run unless you swap in the commented-out lines):

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

abstract class Performer {
  Performer(String name);
  String name;
}

class Musician extends Performer with Musical {
//  Musician.withName(String name) : super(name);
//  Musician() : super('Anonymous');
  Musician(String name) : super(name);
}


mixin MusicalPerformer on Musician {
  bool canDance = true;

  @override
  void entertainMe() =>
      canDance ? dance() : super.entertainMe();

  void dance() => print('Dancing');
}

class SingerDancer extends Musician with MusicalPerformer {
//  SingerDancer(String name) : super.withName(name);
  SingerDancer(String name) : super(name);
}


void main() {
//  var musician = Musician.withName('Kathy');
  var musician = Musician('Kathy'); // Musician.withName('Kathy');
  musician.canPlayPiano = true;
  musician.entertainMe(); // Playing piano

  var singerDancer = SingerDancer('Todd');
  singerDancer.entertainMe(); // Dancing
}

I suppose @MichaelRFairhurst's repro is better/shorter. :)

This is fixed but the fix somehow missed the final 2.1 dev release. The fix landed before the last push to dev, so I assumed that it had made it in and didn't request a merge to dev. I'm sorry about that.

So this is now an (extremely) annoying bug in the new 2.1 feature that will be fixed in the next release.

@kmillikin if this is supposed to go into the 2.1.1 stable release, please file a merge-to-stable request.

Not sure if this is helpful, but here is an example that fails on 2.1.0

class Cook = Person with AwesomeCookingSkills;

class Person {
  final String name;

  Person(this.name);
}

mixin AwesomeCookingSkills on Person {
  String get bestDish => "$name makes the best chocolate.";
}

main() {
  final cook = Cook("Johnny Appleseed");
  /// Error: The superclass, 'Person', has no unnamed constructor that takes no arguments.

  print(cook.bestDish);
}

And here is a Gist if it's useful for tracking this https://dartpad.dartlang.org/b4f766621b01ffa5270e5de93ef592b5

Did this miss 2.1.1 or is it just not in the changelog? https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#211---2019-02-18

It should be in 2.1.1.

ad2db25 is in 2.1.1:

$ git tag --contains ad2db25
2.1.1

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matanlurey picture matanlurey  路  3Comments

DartBot picture DartBot  路  3Comments

55555Mohit55555 picture 55555Mohit55555  路  3Comments

brooth picture brooth  路  3Comments

nex3 picture nex3  路  3Comments