Skip to content

Commit 9f6c372

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Analyzer: forward const constructors to mixin apps as const
Fixes #41072 Relevant text from spec: > If $S_q$ is a generative const constructor, and $MC$ does > not declare any instance variables, $C_q$ is also a const > constructor. Relevant language tests: language_2/mixin_constructor_forwarding/const_constructor* Change-Id: I8ad9e9dd3907b5dd29aa8f10aab6ec604a1aaa4b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150682 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]>
1 parent 1ce143c commit 9f6c372

File tree

4 files changed

+142
-9
lines changed

4 files changed

+142
-9
lines changed

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,9 @@ class ClassElementImpl extends AbstractClassElementImpl
10491049
var substitution =
10501050
Substitution.fromPairs(superClassParameters, argumentTypes);
10511051

1052+
bool typeHasInstanceVariables(InterfaceType type) =>
1053+
type.element.fields.any((e) => !e.isSynthetic);
1054+
10521055
// Now create an implicit constructor for every constructor found above,
10531056
// substituting type parameters as appropriate.
10541057
return constructorsToForward
@@ -1057,6 +1060,9 @@ class ClassElementImpl extends AbstractClassElementImpl
10571060
ConstructorElementImpl(superclassConstructor.name, -1);
10581061
implicitConstructor.isSynthetic = true;
10591062
implicitConstructor.redirectedConstructor = superclassConstructor;
1063+
var hasMixinWithInstanceVariables = mixins.any(typeHasInstanceVariables);
1064+
implicitConstructor.isConst =
1065+
superclassConstructor.isConst && !hasMixinWithInstanceVariables;
10601066
List<ParameterElement> superParameters = superclassConstructor.parameters;
10611067
int count = superParameters.length;
10621068
if (count > 0) {

pkg/analyzer/test/generated/compile_time_error_code.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,58 @@ main() {
618618
]);
619619
}
620620

621+
test_constWithNonConst_mixinApplication_constSuperConstructor() async {
622+
await assertNoErrorsInCode(r'''
623+
mixin M {}
624+
class A {
625+
const A();
626+
}
627+
class B = A with M;
628+
const b = const B();
629+
''');
630+
}
631+
632+
test_constWithNonConst_mixinApplication_constSuperConstructor_field() async {
633+
await assertErrorsInCode(r'''
634+
mixin M {
635+
int i = 0;
636+
}
637+
class A {
638+
const A();
639+
}
640+
class B = A with M;
641+
var b = const B();
642+
''', [
643+
error(CompileTimeErrorCode.CONST_WITH_NON_CONST, 78, 5),
644+
]);
645+
}
646+
647+
test_constWithNonConst_mixinApplication_constSuperConstructor_getter() async {
648+
await assertNoErrorsInCode(r'''
649+
mixin M {
650+
int get i => 0;
651+
}
652+
class A {
653+
const A();
654+
}
655+
class B = A with M;
656+
var b = const B();
657+
''');
658+
}
659+
660+
test_constWithNonConst_mixinApplication_constSuperConstructor_setter() async {
661+
await assertNoErrorsInCode(r'''
662+
mixin M {
663+
set(int i) {}
664+
}
665+
class A {
666+
const A();
667+
}
668+
class B = A with M;
669+
var b = const B();
670+
''');
671+
}
672+
621673
test_constWithNonConstantArgument_annotation() async {
622674
await assertErrorsInCode(r'''
623675
class A {

pkg/analyzer/test/src/dart/resolution/class_alias_test.dart

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analyzer/src/error/codes.dart';
56
import 'package:test_reflective_loader/test_reflective_loader.dart';
67

78
import '../../../generated/elements_types_mixin.dart';
@@ -90,7 +91,6 @@ class X = Object with A, Function, B;
9091
);
9192
}
9293

93-
@failingTest
9494
test_implicitConstructors_const() async {
9595
await assertNoErrorsInCode(r'''
9696
class A {
@@ -103,7 +103,58 @@ class C = A with M;
103103
104104
const x = const C();
105105
''');
106-
// TODO(scheglov) add also negative test with fields
106+
}
107+
108+
test_implicitConstructors_const_field() async {
109+
await assertErrorsInCode(r'''
110+
class A {
111+
const A();
112+
}
113+
114+
class M {
115+
int i = 0;
116+
}
117+
118+
class C = A with M;
119+
120+
const x = const C();
121+
''', [
122+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 83,
123+
5),
124+
error(CompileTimeErrorCode.CONST_WITH_NON_CONST, 83, 5),
125+
]);
126+
}
127+
128+
test_implicitConstructors_const_getter() async {
129+
await assertNoErrorsInCode(r'''
130+
class A {
131+
const A();
132+
}
133+
134+
class M {
135+
int get i => 0;
136+
}
137+
138+
class C = A with M;
139+
140+
const x = const C();
141+
''');
142+
}
143+
144+
test_implicitConstructors_const_setter() async {
145+
await assertNoErrorsInCode(r'''
146+
class A {
147+
const A();
148+
}
149+
150+
class M {
151+
set(int i) {}
152+
}
153+
154+
class C = A with M;
155+
156+
const x = const C();
157+
''');
107158
}
108159

109160
test_implicitConstructors_dependencies() async {

pkg/analyzer/test/src/summary/resynthesize_common.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,30 @@ class E {
280280
''');
281281
}
282282

283+
test_class_alias_with_const_constructors() async {
284+
addLibrarySource('/a.dart', '''
285+
class Base {
286+
const Base._priv();
287+
const Base();
288+
const Base.named();
289+
}
290+
''');
291+
var library = await checkLibrary('''
292+
import "a.dart";
293+
class M {}
294+
class MixinApp = Base with M;
295+
''');
296+
checkElementText(library, r'''
297+
import 'a.dart';
298+
class M {
299+
}
300+
class alias MixinApp extends Base with M {
301+
synthetic const MixinApp() = Base;
302+
synthetic const MixinApp.named() = Base.named;
303+
}
304+
''');
305+
}
306+
283307
test_class_alias_with_forwarding_constructors() async {
284308
addLibrarySource('/a.dart', '''
285309
class Base {
@@ -1854,33 +1878,33 @@ class A/*codeOffset=0, codeLength=10*/ {
18541878
class B/*codeOffset=12, codeLength=10*/ {
18551879
}
18561880
class alias Raw/*codeOffset=28, codeLength=29*/ extends Object with A, B {
1857-
synthetic Raw() = Object;
1881+
synthetic const Raw() = Object;
18581882
}
18591883
/// Comment 1.
18601884
/// Comment 2.
18611885
class alias HasDocComment/*codeOffset=59, codeLength=69*/ extends Object with A, B {
1862-
synthetic HasDocComment() = Object;
1886+
synthetic const HasDocComment() = Object;
18631887
}
18641888
@Object()
18651889
class alias HasAnnotation/*codeOffset=130, codeLength=49*/ extends Object with A, B {
1866-
synthetic HasAnnotation() = Object;
1890+
synthetic const HasAnnotation() = Object;
18671891
}
18681892
/// Comment 1.
18691893
/// Comment 2.
18701894
@Object()
18711895
class alias AnnotationThenComment/*codeOffset=181, codeLength=87*/ extends Object with A, B {
1872-
synthetic AnnotationThenComment() = Object;
1896+
synthetic const AnnotationThenComment() = Object;
18731897
}
18741898
/// Comment 1.
18751899
/// Comment 2.
18761900
@Object()
18771901
class alias CommentThenAnnotation/*codeOffset=270, codeLength=87*/ extends Object with A, B {
1878-
synthetic CommentThenAnnotation() = Object;
1902+
synthetic const CommentThenAnnotation() = Object;
18791903
}
18801904
/// Comment 2.
18811905
@Object()
18821906
class alias CommentAroundAnnotation/*codeOffset=374, codeLength=74*/ extends Object with A, B {
1883-
synthetic CommentAroundAnnotation() = Object;
1907+
synthetic const CommentAroundAnnotation() = Object;
18841908
}
18851909
''',
18861910
withCodeRanges: true,
@@ -5558,7 +5582,7 @@ class C extends Object with M {
55585582
dynamic foo() {}
55595583
}
55605584
class alias D extends Object with M {
5561-
synthetic D() = Object;
5585+
synthetic const D() = Object;
55625586
}
55635587
''');
55645588
}

0 commit comments

Comments
 (0)