Partial constant evaluation for record literals

Change-Id: I53db27f67ab1244b7e25a08002ae165840c7c012
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256544
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson
2022-08-26 21:53:21 +00:00
committed by Commit Bot
parent a3f8672778
commit d40f06ee2a
9 changed files with 90 additions and 7 deletions
+9
View File
@@ -4083,9 +4083,18 @@ abstract class PropertyAccess
/// Clients may not extend, implement or mix-in this class.
@experimental
abstract class RecordLiteral implements Literal {
/// Return the token representing the 'const' keyword, or `null` if the
/// literal is not a constant.
Token? get constKeyword;
/// Return the syntactic elements used to compute the fields of the record.
NodeList<Expression> get fields;
/// Return `true` if this literal is a constant expression, either because the
/// keyword `const` was explicitly provided or because no keyword was provided
/// and this expression is in a constant context.
bool get isConst;
/// Return the left parenthesis.
Token get leftParenthesis;
@@ -102,6 +102,9 @@ abstract class TypeProvider {
/// Return the element representing the built-in class `Record`.
ClassElement get recordElement;
/// Return the type representing the built-in type `Record`.
InterfaceType get recordType;
/// Return the element representing the built-in class `Set`.
ClassElement get setElement;
+12 -3
View File
@@ -4206,7 +4206,8 @@ abstract class ExpressionImpl extends AstNodeImpl
child is MapLiteralEntry ||
child is SpreadElement ||
child is IfElement ||
child is ForElement) {
child is ForElement ||
child is RecordLiteral) {
var parent = child.parent;
if (parent is ConstantContextForExpressionImpl) {
return true;
@@ -9636,6 +9637,9 @@ class PropertyAccessImpl extends CommentReferableExpressionImpl
}
class RecordLiteralImpl extends LiteralImpl implements RecordLiteral {
@override
Token? constKeyword;
@override
Token leftParenthesis;
@@ -9647,14 +9651,15 @@ class RecordLiteralImpl extends LiteralImpl implements RecordLiteral {
/// Initialize a newly created record literal.
RecordLiteralImpl(
{required this.leftParenthesis,
{this.constKeyword,
required this.leftParenthesis,
required List<Expression> fields,
required this.rightParenthesis}) {
_fields._initialize(this, fields);
}
@override
Token get beginToken => leftParenthesis;
Token get beginToken => constKeyword ?? leftParenthesis;
@override
Token get endToken => rightParenthesis;
@@ -9662,9 +9667,13 @@ class RecordLiteralImpl extends LiteralImpl implements RecordLiteral {
@override
NodeList<Expression> get fields => _fields;
@override
bool get isConst => constKeyword != null || inConstantContext;
@override
// TODO(paulberry): add commas.
ChildEntities get _childEntities => super._childEntities
..addToken('constKeyword', constKeyword)
..addToken('leftParenthesis', leftParenthesis)
..addNodeList('fields', fields)
..addToken('rightParenthesis', rightParenthesis);
@@ -1012,6 +1012,41 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
return _getConstantValue(node, node.propertyName);
}
@override
DartObjectImpl? visitRecordLiteral(RecordLiteral node) {
if (!node.isConst) {
// TODO(brianwilkerson) Merge the error codes into a single error code or
// declare a new error code specific to records.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL, node);
return null;
}
var nodeType = node.staticType;
if (nodeType == null) {
return null;
}
var positionalFields = <DartObjectImpl>[];
var namedFields = <String, DartObjectImpl>{};
for (var field in node.fields) {
if (field is NamedExpression) {
var name = field.name.label.name;
var value = field.expression.accept(this);
if (value == null) {
return null;
}
namedFields[name] = value;
} else {
var value = field.accept(this);
if (value == null) {
return null;
}
positionalFields.add(value);
}
}
return DartObjectImpl(
typeSystem, nodeType, RecordState(positionalFields, namedFields));
}
@override
DartObjectImpl? visitSetOrMapLiteral(SetOrMapLiteral node) {
// Note: due to dartbug.com/33441, it's possible that a set/map literal
@@ -397,6 +397,7 @@ class TypeProviderImpl extends TypeProviderBase {
return _recordElement ??= _getClassElement(_coreLibrary, 'Record');
}
@override
InterfaceType get recordType {
return _recordType ??= recordElement.instantiate(
typeArguments: const [],
@@ -1029,8 +1029,10 @@ class AstBinaryReader {
}
RecordLiteralImpl _readRecordLiteral() {
final fields = _readNodeList<Expression>();
final node = RecordLiteralImpl(
var flags = _readByte();
var fields = _readNodeList<Expression>();
var node = RecordLiteralImpl(
constKeyword: AstBinaryFlags.isConst(flags) ? Tokens.const_() : null,
leftParenthesis: Tokens.openParenthesis(),
fields: fields,
rightParenthesis: Tokens.closeParenthesis(),
@@ -630,6 +630,11 @@ class AstBinaryWriter extends ThrowingAstVisitor<void> {
@override
void visitRecordLiteral(RecordLiteral node) {
_writeByte(Tag.RecordLiteral);
_writeByte(
AstBinaryFlags.encode(
isConst: node.constKeyword != null,
),
);
_writeNodeList(node.fields);
_storeExpression(node);
}
@@ -5,6 +5,7 @@
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/element/element.dart';
@@ -810,6 +811,25 @@ const void Function(int) g = self.C.f;
_assertTypeArguments(result, ['int']);
}
test_visitRecordLiteral_withoutEnvironment() async {
await resolveTestCode(r'''
const a = (1, 'b', c: false);
''');
var result = _evaluateConstant('a');
var type = result.type;
if (type is! RecordType) {
fail('Expected a record type');
}
var positionalFields = type.positionalFields;
var namedFields = type.namedFields;
expect(positionalFields, hasLength(2));
expect(positionalFields[0].type, typeProvider.intType);
expect(positionalFields[1].type, typeProvider.stringType);
expect(namedFields, hasLength(1));
expect(namedFields[0].name, 'c');
expect(namedFields[0].type, typeProvider.boolType);
}
test_visitSimpleIdentifier_className() async {
await resolveTestCode('''
const a = C;
@@ -2299,8 +2299,7 @@ class DartObjectImplTest {
) {
return DartObjectImpl(
_typeSystem,
_typeProvider.recordElement.instantiate(
typeArguments: [], nullabilitySuffix: NullabilitySuffix.none),
_typeProvider.recordType,
RecordState(positionalFields, namedFields),
);
}