[CFE] Fix crash when enum has duplicate constructors
Change-Id: I5ee6ef32431eda38c63a3eb3f3ded6b74e2c79df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396800
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/lib/src/source/source_enum_builder.dart b/pkg/front_end/lib/src/source/source_enum_builder.dart
index 341c642..650bd52 100644
--- a/pkg/front_end/lib/src/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/source/source_enum_builder.dart
@@ -161,9 +161,11 @@
super.buildScopes(coreLibrary);
_createSynthesizedMembers(coreLibrary);
+ // Include duplicates to install the formals on all constructors to avoid a
+ // crash later.
Iterator<MemberBuilder> iterator =
nameSpace.filteredConstructorNameIterator(
- includeDuplicates: false, includeAugmentations: true);
+ includeDuplicates: true, includeAugmentations: true);
while (iterator.moveNext()) {
MemberBuilder member = iterator.current;
if (member is DeclaredSourceConstructorBuilder) {
@@ -612,6 +614,11 @@
constructorName = constructorName == "new" ? "" : constructorName;
MemberBuilder? constructorBuilder =
nameSpace.lookupConstructor(constructorName);
+ // TODO(CFE Team): Should there be a conversion to an invalid expression
+ // instead? That's what happens on classes.
+ while (constructorBuilder?.next != null) {
+ constructorBuilder = constructorBuilder?.next as MemberBuilder;
+ }
ArgumentsImpl arguments;
List<Expression> enumSyntheticArguments = <Expression>[
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart
new file mode 100644
index 0000000..5849de5
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+enum Foo {
+ a, b;
+
+ const Foo();
+ const Foo();
+}
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.expect
new file mode 100644
index 0000000..7d436ca
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.expect
@@ -0,0 +1,41 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:9:9: Error: 'Foo' is already declared in this scope.
+// const Foo();
+// ^^^
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:8:9: Context: Previous declaration of 'Foo'.
+// const Foo();
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::_Enum /*isEnum*/ {
+ static const field core::List<self::Foo> values = #C7;
+ enum-element static const field self::Foo a = #C3;
+ enum-element static const field self::Foo b = #C6;
+ const constructor •(core::int #index, core::String #name) → self::Foo
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Foo.${this.{core::_Enum::_name}{core::String}}";
+}
+
+constants {
+ #C1 = 0
+ #C2 = "a"
+ #C3 = self::Foo {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "b"
+ #C6 = self::Foo {index:#C4, _name:#C5}
+ #C7 = <self::Foo>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///duplicate_enum_constructor.dart:
+- Foo. (from org-dartlang-testcase:///duplicate_enum_constructor.dart:8:9)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.modular.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.modular.expect
new file mode 100644
index 0000000..7d436ca
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.modular.expect
@@ -0,0 +1,41 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:9:9: Error: 'Foo' is already declared in this scope.
+// const Foo();
+// ^^^
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:8:9: Context: Previous declaration of 'Foo'.
+// const Foo();
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::_Enum /*isEnum*/ {
+ static const field core::List<self::Foo> values = #C7;
+ enum-element static const field self::Foo a = #C3;
+ enum-element static const field self::Foo b = #C6;
+ const constructor •(core::int #index, core::String #name) → self::Foo
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Foo.${this.{core::_Enum::_name}{core::String}}";
+}
+
+constants {
+ #C1 = 0
+ #C2 = "a"
+ #C3 = self::Foo {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "b"
+ #C6 = self::Foo {index:#C4, _name:#C5}
+ #C7 = <self::Foo>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///duplicate_enum_constructor.dart:
+- Foo. (from org-dartlang-testcase:///duplicate_enum_constructor.dart:8:9)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.outline.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.outline.expect
new file mode 100644
index 0000000..7d00431
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.outline.expect
@@ -0,0 +1,31 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:9:9: Error: 'Foo' is already declared in this scope.
+// const Foo();
+// ^^^
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:8:9: Context: Previous declaration of 'Foo'.
+// const Foo();
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::_Enum /*isEnum*/ {
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::a, self::Foo::b];
+ enum-element static const field self::Foo a = const self::Foo::•(0, "a");
+ enum-element static const field self::Foo b = const self::Foo::•(1, "b");
+ const constructor •(core::int #index, core::String #name) → self::Foo
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Foo.${this.{core::_Enum::_name}{core::String}}";
+}
+
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///duplicate_enum_constructor.dart:5:6 -> ListConstant(const <Foo>[const Foo{_Enum.index: 0, _Enum._name: "a"}, const Foo{_Enum.index: 1, _Enum._name: "b"}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///duplicate_enum_constructor.dart:6:3 -> InstanceConstant(const Foo{_Enum.index: 0, _Enum._name: "a"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///duplicate_enum_constructor.dart:6:6 -> InstanceConstant(const Foo{_Enum.index: 1, _Enum._name: "b"})
+Extra constant evaluation: evaluated: 8, effectively constant: 3
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.transformed.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.transformed.expect
new file mode 100644
index 0000000..7d436ca
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.strong.transformed.expect
@@ -0,0 +1,41 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:9:9: Error: 'Foo' is already declared in this scope.
+// const Foo();
+// ^^^
+// pkg/front_end/testcases/regress/duplicate_enum_constructor.dart:8:9: Context: Previous declaration of 'Foo'.
+// const Foo();
+// ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::_Enum /*isEnum*/ {
+ static const field core::List<self::Foo> values = #C7;
+ enum-element static const field self::Foo a = #C3;
+ enum-element static const field self::Foo b = #C6;
+ const constructor •(core::int #index, core::String #name) → self::Foo
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Foo.${this.{core::_Enum::_name}{core::String}}";
+}
+
+constants {
+ #C1 = 0
+ #C2 = "a"
+ #C3 = self::Foo {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "b"
+ #C6 = self::Foo {index:#C4, _name:#C5}
+ #C7 = <self::Foo>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///duplicate_enum_constructor.dart:
+- Foo. (from org-dartlang-testcase:///duplicate_enum_constructor.dart:8:9)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline.expect
new file mode 100644
index 0000000..64990cc
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+enum Foo {
+ a,
+ b;
+
+ const Foo();
+ const Foo();
+}
diff --git a/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..64990cc
--- /dev/null
+++ b/pkg/front_end/testcases/regress/duplicate_enum_constructor.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+enum Foo {
+ a,
+ b;
+
+ const Foo();
+ const Foo();
+}