[cfe] Don't cache constant in a lazy region

When unevaluated constants are found in conditions, a lazy region
is created for evaluating the subexpression, ensure that constant
locals are replaced within the subexpressions while still leaving the
subexpressions themselves unevaluated.

In these lazy regions, the result of evaluating the subexpressions
should not be cached, since these subexpressions are not themselves
unevaluated and should be evaluated if encountered elsewhere.

This was previously not done, leading to constants not being
evaluated even when these didn't contain any unevaluated constants.

Closes #51823

Change-Id: Ibfc02fef3ecfcd2f1d2e9e1451f0eb71dde6fb42
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290963
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2023-03-24 08:07:57 +00:00 committed by Commit Queue
parent 6ef15e48bb
commit 290706f497
18 changed files with 445 additions and 32 deletions

View file

@ -2628,8 +2628,12 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
if (evaluatedResult is AbortConstant) {
nodeCache.remove(node);
return evaluatedResult;
} else {
} else if (lazyDepth == 0) {
nodeCache[node] = evaluatedResult;
} else {
// Don't cache nodes evaluated in a lazy region, since these are not
// themselves unevaluated but just part of an unevaluated constant.
nodeCache.remove(node);
}
result = evaluatedResult;
}

View file

@ -87,9 +87,9 @@ Try correcting the name to the name of an existing getter, or defining a getter
request.{core::Object::hashCode}{core::int};
}
static method main() → void {
self::expect(false, #C1);
self::expect(true, #C2);
self::expect(false, #C1);
self::expect(false, #C2);
self::expect(true, #C4);
self::expect(false, #C6);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
@ -97,6 +97,10 @@ static method expect(dynamic expected, dynamic actual) → dynamic {
}
constants {
#C1 = false
#C2 = true
#C1 = "dart.library.io"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = "dart.library.html"
#C4 = eval const core::bool::fromEnvironment(#C3)
#C5 = "dart.library.foo"
#C6 = eval const core::bool::fromEnvironment(#C5)
}

View file

@ -87,9 +87,9 @@ Try correcting the name to the name of an existing getter, or defining a getter
request.{core::Object::hashCode}{core::int};
}
static method main() → void {
self::expect(false, #C1);
self::expect(true, #C2);
self::expect(false, #C1);
self::expect(false, #C2);
self::expect(true, #C4);
self::expect(false, #C6);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
@ -97,6 +97,16 @@ static method expect(dynamic expected, dynamic actual) → dynamic {
}
constants {
#C1 = false
#C2 = true
#C1 = "dart.library.io"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = "dart.library.html"
#C4 = eval const core::bool::fromEnvironment(#C3)
#C5 = "dart.library.foo"
#C6 = eval const core::bool::fromEnvironment(#C5)
}
Extra constant evaluation status:
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:41:23 -> BoolConstant(false)
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:42:22 -> BoolConstant(true)
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:43:23 -> BoolConstant(false)
Extra constant evaluation: evaluated: 29, effectively constant: 3

View file

@ -87,9 +87,9 @@ Try correcting the name to the name of an existing getter, or defining a getter
request.{core::Object::hashCode}{core::int};
}
static method main() → void {
self::expect(false, #C1);
self::expect(true, #C2);
self::expect(false, #C1);
self::expect(false, #C2);
self::expect(true, #C4);
self::expect(false, #C6);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
@ -97,6 +97,10 @@ static method expect(dynamic expected, dynamic actual) → dynamic {
}
constants {
#C1 = false
#C2 = true
#C1 = "dart.library.io"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = "dart.library.html"
#C4 = eval const core::bool::fromEnvironment(#C3)
#C5 = "dart.library.foo"
#C6 = eval const core::bool::fromEnvironment(#C5)
}

View file

@ -87,9 +87,9 @@ Try correcting the name to the name of an existing getter, or defining a getter
request.{core::Object::hashCode}{core::int};
}
static method main() → void {
self::expect(false, #C1);
self::expect(true, #C2);
self::expect(false, #C1);
self::expect(false, #C2);
self::expect(true, #C4);
self::expect(false, #C6);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
@ -97,6 +97,10 @@ static method expect(dynamic expected, dynamic actual) → dynamic {
}
constants {
#C1 = false
#C2 = true
#C1 = "dart.library.io"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = "dart.library.html"
#C4 = eval const core::bool::fromEnvironment(#C3)
#C5 = "dart.library.foo"
#C6 = eval const core::bool::fromEnvironment(#C5)
}

View file

@ -87,9 +87,9 @@ Try correcting the name to the name of an existing getter, or defining a getter
request.{core::Object::hashCode}{core::int};
}
static method main() → void {
self::expect(false, #C1);
self::expect(true, #C2);
self::expect(false, #C1);
self::expect(false, #C2);
self::expect(true, #C4);
self::expect(false, #C6);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
@ -97,6 +97,16 @@ static method expect(dynamic expected, dynamic actual) → dynamic {
}
constants {
#C1 = false
#C2 = true
#C1 = "dart.library.io"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = "dart.library.html"
#C4 = eval const core::bool::fromEnvironment(#C3)
#C5 = "dart.library.foo"
#C6 = eval const core::bool::fromEnvironment(#C5)
}
Extra constant evaluation status:
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:41:23 -> BoolConstant(false)
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:42:22 -> BoolConstant(true)
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///conditional_import.dart:43:23 -> BoolConstant(false)
Extra constant evaluation: evaluated: 29, effectively constant: 3

View file

@ -1 +1,2 @@
--target=dart2js
--target=dart2js
--no-defines

View file

@ -0,0 +1,7 @@
// Copyright (c) 2023, 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.
import 'issue51823_lib.dart';
const a = const bool.fromEnvironment('foo') ? E.a : E.b;

View file

@ -0,0 +1,59 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = #C9;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = #C12;
enum-element static const field iss::E a = #C10;
enum-element static const field iss::E b = #C11;
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic {
#L1:
switch(e) /*isExplicitlyExhaustive*/ {
#L2:
case #C10:
{
return 0;
}
#L3:
case #C11:
{
return 1;
}
}
}
constants {
#C1 = "foo"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = 0.0
#C4 = "a"
#C5 = eval iss::E{index:#C3, _name:#C4}
#C6 = 1.0
#C7 = "b"
#C8 = eval iss::E{index:#C6, _name:#C7}
#C9 = eval #C2 ?{iss::E} #C5 : #C8
#C10 = iss::E {index:#C3, _name:#C4}
#C11 = iss::E {index:#C6, _name:#C7}
#C12 = <iss::E>[#C10, #C11]
}
Constructor coverage from constants:
org-dartlang-testcase:///issue51823_lib.dart:
- E. (from org-dartlang-testcase:///issue51823_lib.dart:5:6)
- _Enum. (from org-dartlang-sdk:///lib/core/enum.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,63 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = #C9;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = #C12;
enum-element static const field iss::E a = #C10;
enum-element static const field iss::E b = #C11;
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic {
#L1:
switch(e) /*isExplicitlyExhaustive*/ {
#L2:
case #C10:
{
return 0;
}
#L3:
case #C11:
{
return 1;
}
}
}
constants {
#C1 = "foo"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = 0.0
#C4 = "a"
#C5 = eval iss::E{index:#C3, _name:#C4}
#C6 = 1.0
#C7 = "b"
#C8 = eval iss::E{index:#C6, _name:#C7}
#C9 = eval #C2 ?{iss::E} #C5 : #C8
#C10 = iss::E {index:#C3, _name:#C4}
#C11 = iss::E {index:#C6, _name:#C7}
#C12 = <iss::E>[#C10, #C11]
}
Extra constant evaluation status:
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///issue51823.dart:7:45 -> InstanceConstant(const E{_Enum.index: 1.0, _Enum._name: "b"})
Extra constant evaluation: evaluated: 7, effectively constant: 1
Constructor coverage from constants:
org-dartlang-testcase:///issue51823_lib.dart:
- E. (from org-dartlang-testcase:///issue51823_lib.dart:5:6)
- _Enum. (from org-dartlang-sdk:///lib/core/enum.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,3 @@
import 'issue51823_lib.dart';
const a = const bool.fromEnvironment('foo') ? E.a : E.b;

View file

@ -0,0 +1,3 @@
import 'issue51823_lib.dart';
const a = const bool.fromEnvironment('foo') ? E.a : E.b;

View file

@ -0,0 +1,63 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = #C9;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
import "dart:_internal" as _in;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = #C12;
enum-element static const field iss::E a = #C10;
enum-element static const field iss::E b = #C11;
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic {
#L1:
switch(e) /*isExplicitlyExhaustive*/ {
#L2:
case #C10:
{
return 0;
}
#L3:
case #C11:
{
return 1;
}
#L4:
default:
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
}
}
constants {
#C1 = "foo"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = 0.0
#C4 = "a"
#C5 = eval iss::E{index:#C3, _name:#C4}
#C6 = 1.0
#C7 = "b"
#C8 = eval iss::E{index:#C6, _name:#C7}
#C9 = eval #C2 ?{iss::E} #C5 : #C8
#C10 = iss::E {index:#C3, _name:#C4}
#C11 = iss::E {index:#C6, _name:#C7}
#C12 = <iss::E*>[#C10, #C11]
}
Constructor coverage from constants:
org-dartlang-testcase:///issue51823_lib.dart:
- E. (from org-dartlang-testcase:///issue51823_lib.dart:5:6)
- _Enum. (from org-dartlang-sdk:///lib/core/enum.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,63 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = #C9;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
import "dart:_internal" as _in;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = #C12;
enum-element static const field iss::E a = #C10;
enum-element static const field iss::E b = #C11;
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic {
#L1:
switch(e) /*isExplicitlyExhaustive*/ {
#L2:
case #C10:
{
return 0;
}
#L3:
case #C11:
{
return 1;
}
#L4:
default:
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
}
}
constants {
#C1 = "foo"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = 0.0
#C4 = "a"
#C5 = eval iss::E{index:#C3, _name:#C4}
#C6 = 1.0
#C7 = "b"
#C8 = eval iss::E{index:#C6, _name:#C7}
#C9 = eval #C2 ?{iss::E} #C5 : #C8
#C10 = iss::E {index:#C3, _name:#C4}
#C11 = iss::E {index:#C6, _name:#C7}
#C12 = <iss::E*>[#C10, #C11]
}
Constructor coverage from constants:
org-dartlang-testcase:///issue51823_lib.dart:
- E. (from org-dartlang-testcase:///issue51823_lib.dart:5:6)
- _Enum. (from org-dartlang-sdk:///lib/core/enum.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,36 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "dart:core" as core;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = const core::bool::fromEnvironment("foo") ?{iss::E} iss::E::a : iss::E::b;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = const <iss::E>[iss::E::a, iss::E::b];
enum-element static const field iss::E a = const iss::E::•(0, "a");
enum-element static const field iss::E b = const iss::E::•(1, "b");
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic
;
Extra constant evaluation status:
Evaluated with empty environment: ConditionalExpression @ org-dartlang-testcase:///issue51823.dart:7:45 -> InstanceConstant(const E{_Enum.index: 1.0, _Enum._name: "b"})
Evaluated with empty environment: FactoryConstructorInvocation @ org-dartlang-testcase:///issue51823.dart:7:17 -> BoolConstant(false)
Evaluated: StaticGet @ org-dartlang-testcase:///issue51823.dart:7:49 -> InstanceConstant(const E{_Enum.index: 0.0, _Enum._name: "a"})
Evaluated: StaticGet @ org-dartlang-testcase:///issue51823.dart:7:55 -> InstanceConstant(const E{_Enum.index: 1.0, _Enum._name: "b"})
Evaluated: ListLiteral @ org-dartlang-testcase:///issue51823_lib.dart:5:6 -> ListConstant(const <E*>[const E{_Enum.index: 0.0, _Enum._name: "a"}, const E{_Enum.index: 1.0, _Enum._name: "b"}])
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///issue51823_lib.dart:5:10 -> InstanceConstant(const E{_Enum.index: 0.0, _Enum._name: "a"})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///issue51823_lib.dart:5:13 -> InstanceConstant(const E{_Enum.index: 1.0, _Enum._name: "b"})
Extra constant evaluation: evaluated: 12, effectively constant: 7

View file

@ -0,0 +1,67 @@
library /*isNonNullableByDefault*/;
import self as self;
import "issue51823_lib.dart" as iss;
import "org-dartlang-testcase:///issue51823_lib.dart";
static const field iss::E a = #C9;
library /*isNonNullableByDefault*/;
import self as iss;
import "dart:core" as core;
import "dart:_internal" as _in;
class E extends core::_Enum /*isEnum*/ {
static const field core::List<iss::E> values = #C12;
enum-element static const field iss::E a = #C10;
enum-element static const field iss::E b = #C11;
const constructor •(core::int #index, core::String #name) → iss::E
: super core::_Enum::•(#index, #name)
;
method _enumToString() → core::String
return "E.${this.{core::_Enum::_name}{core::String}}";
}
static method method(iss::E e) → dynamic {
#L1:
switch(e) /*isExplicitlyExhaustive*/ {
#L2:
case #C10:
{
return 0;
}
#L3:
case #C11:
{
return 1;
}
#L4:
default:
throw new _in::ReachabilityError::•("`null` encountered as case in a switch statement with a non-nullable type.");
}
}
constants {
#C1 = "foo"
#C2 = eval const core::bool::fromEnvironment(#C1)
#C3 = 0.0
#C4 = "a"
#C5 = eval iss::E{index:#C3, _name:#C4}
#C6 = 1.0
#C7 = "b"
#C8 = eval iss::E{index:#C6, _name:#C7}
#C9 = eval #C2 ?{iss::E} #C5 : #C8
#C10 = iss::E {index:#C3, _name:#C4}
#C11 = iss::E {index:#C6, _name:#C7}
#C12 = <iss::E*>[#C10, #C11]
}
Extra constant evaluation status:
Evaluated with empty environment: ConstantExpression @ org-dartlang-testcase:///issue51823.dart:7:45 -> InstanceConstant(const E{_Enum.index: 1.0, _Enum._name: "b"})
Extra constant evaluation: evaluated: 9, effectively constant: 1
Constructor coverage from constants:
org-dartlang-testcase:///issue51823_lib.dart:
- E. (from org-dartlang-testcase:///issue51823_lib.dart:5:6)
- _Enum. (from org-dartlang-sdk:///lib/core/enum.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,14 @@
// Copyright (c) 2023, 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 E { a, b }
method(E e) {
switch (e) {
case E.a:
return 0;
case E.b:
return 1;
}
}

View file

@ -113,10 +113,8 @@ Evaluated: TypeLiteral @ org-dartlang-testcase:///various_2_lib.dart:17:27 -> Ty
Evaluated: StaticTearOff @ org-dartlang-testcase:///various_2_lib.dart:18:12 -> StaticTearOffConstant(identical)
Evaluated with empty environment: ConditionalExpression @ org-dartlang-testcase:///various_2_lib.dart:20:39 -> InstantiationConstant(id2<int*>)
Evaluated with empty environment: FactoryConstructorInvocation @ org-dartlang-testcase:///various_2_lib.dart:20:11 -> BoolConstant(false)
Evaluated with empty environment: Instantiation @ org-dartlang-testcase:///various_2_lib.dart:20:41 -> InstantiationConstant(id1<int*>)
Evaluated: StaticTearOff @ org-dartlang-testcase:///various_2_lib.dart:20:41 -> StaticTearOffConstant(id1)
Evaluated with empty environment: Instantiation @ org-dartlang-testcase:///various_2_lib.dart:20:47 -> InstantiationConstant(id2<int*>)
Evaluated: StaticTearOff @ org-dartlang-testcase:///various_2_lib.dart:20:47 -> StaticTearOffConstant(id2)
Evaluated: Instantiation @ org-dartlang-testcase:///various_2_lib.dart:20:41 -> InstantiationConstant(id1<int*>)
Evaluated: Instantiation @ org-dartlang-testcase:///various_2_lib.dart:20:47 -> InstantiationConstant(id2<int*>)
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various_2_lib.dart:21:24 -> InstanceConstant(const Class<int*>{Class.field: 0})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various_2_lib.dart:22:25 -> InstanceConstant(const Class<dynamic>{Class.field: const <int*>[42]})
Evaluated: TypeLiteral @ org-dartlang-testcase:///various_2_lib.dart:23:29 -> TypeLiteralConstant(dynamic Function(dynamic)*)
@ -130,4 +128,4 @@ Evaluated: MapLiteral @ org-dartlang-testcase:///various_2_lib.dart:34:39 -> Map
Evaluated: ListConcatenation @ org-dartlang-testcase:///various_2_lib.dart:38:32 -> ListConstant(const <int*>[0])
Evaluated: SetConcatenation @ org-dartlang-testcase:///various_2_lib.dart:39:31 -> SetConstant(const <int*>{0})
Evaluated: MapConcatenation @ org-dartlang-testcase:///various_2_lib.dart:40:7 -> MapConstant(const <int*, String*>{0: "foo"})
Extra constant evaluation: evaluated: 54, effectively constant: 53
Extra constant evaluation: evaluated: 52, effectively constant: 51