blob: 4320462b60c4409ce7886c47fa2ac6a80cafe962 [file] [log] [blame] [edit]
// Copyright (c) 2025, 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.
part of '../../ast.dart';
/// Generalized notion of a variable.
sealed class Variable extends TreeNode {
VariableContext get context => parent as VariableContext;
/// The cosmetic name of the variable from the source code, if exists.
String? get cosmeticName;
void set cosmeticName(String? value);
int flags = 0;
@override
void toTextInternal(AstPrinter printer) {
printer.write(printer.getVariableName(this));
}
}
/// This is a helper class to enable mixing a mixin into concrete
/// implementations of the sealed class [ExpressionVariable]. It's not supposed
/// to be used as a type annotation, but purely for declaring the class
/// hierarchy.
abstract interface class IExpressionVariable implements TreeNode {
abstract DartType type;
abstract String? cosmeticName;
abstract VariableInitialization? variableInitialization;
abstract Expression? initializer;
abstract bool isFinal;
abstract bool isConst;
abstract bool isLate;
abstract bool isInitializingFormal;
abstract bool isSynthesized;
abstract bool isHoisted;
abstract bool hasDeclaredInitializer;
abstract bool isCovariantByClass;
abstract bool isRequired;
abstract bool isCovariantByDeclaration;
abstract bool isLowered;
abstract bool isWildcard;
abstract bool isSuperInitializingFormal;
abstract bool isErroneouslyInitialized;
bool get isAssignable;
ExpressionVariable get asExpressionVariable;
}
/// The root of the sealed hierarchy of non-type variables.
sealed class ExpressionVariable extends Variable
implements IExpressionVariable {
/// Static type of the variable.
@override
abstract DartType type;
/// Initialization node for the variable, if available.
@override
abstract VariableInitialization? variableInitialization;
/// Derived from [variableInitialization], if available.
@override
abstract Expression? initializer;
@override
abstract bool isFinal;
@override
abstract bool isConst;
@override
abstract bool isLate;
@override
abstract bool isInitializingFormal;
@override
abstract bool isSynthesized;
@override
abstract bool isHoisted;
@override
abstract bool hasDeclaredInitializer;
@override
abstract bool isCovariantByClass;
@override
abstract bool isRequired;
@override
abstract bool isCovariantByDeclaration;
@override
abstract bool isLowered;
@override
abstract bool isWildcard;
@override
abstract bool isSuperInitializingFormal;
@override
abstract bool isErroneouslyInitialized;
@override
bool get isAssignable;
@override
ExpressionVariable get asExpressionVariable => this;
}
/// Local variables. They aren't Statements. A [LocalVariable] is "declared" in
/// the [VariableContext] it appears in. [VariableInitialization]
/// (which is a [Statement]) marks the spot of the original variable declaration
/// in the Dart program.
class LocalVariable extends ExpressionVariable {
@override
String? cosmeticName;
@override
DartType type;
@override
VariableInitialization? variableInitialization;
LocalVariable({this.cosmeticName, required DartType? type})
: type = type ?? const DynamicType();
static const int FlagFinal = 1 << 0;
static const int FlagWildcard = 1 << 1;
static const int FlagConst = 1 << 2;
static const int FlagLate = 1 << 3;
static const int FlagLowered = 1 << 4;
static const int FlagHoisted = 1 << 5;
@override
bool get isFinal => flags & FlagFinal != 0;
@override
void set isFinal(bool value) {
flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal);
}
@override
bool get isWildcard => flags & FlagWildcard != 0;
@override
void set isWildcard(bool value) {
flags = value ? (flags | FlagWildcard) : (flags & ~FlagWildcard);
}
@override
bool get isConst => flags & FlagConst != 0;
@override
void set isConst(bool value) {
flags = value ? (flags | FlagConst) : (flags & ~FlagConst);
}
@override
bool get isLate => flags & FlagLate != 0;
@override
void set isLate(bool value) {
flags = value ? (flags | FlagLate) : (flags & ~FlagLate);
}
@override
bool get isLowered => flags & FlagLowered != 0;
@override
void set isLowered(bool value) {
flags = value ? (flags | FlagLowered) : (flags & ~FlagLowered);
}
@override
bool get isHoisted => flags & FlagHoisted != 0;
@override
void set isHoisted(bool value) {
flags = value ? (flags | FlagHoisted) : (flags & ~FlagHoisted);
}
@override
bool get isCovariantByClass {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByClass(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isCovariantByDeclaration {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByDeclaration(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isErroneouslyInitialized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isErroneouslyInitialized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get hasDeclaredInitializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set hasDeclaredInitializer(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isRequired {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isRequired(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSuperInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSuperInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSynthesized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSynthesized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isAssignable {
if (isConst) return false;
if (isFinal) {
if (isLate) return variableInitialization?.initializer == null;
return false;
}
return true;
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitLocalVariable(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitLocalVariable(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
String toString() {
return "LocalVariable(${toStringInternal()})";
}
@override
Expression? get initializer => variableInitialization?.initializer;
@override
void set initializer(Expression? value) {
if (value != null && variableInitialization == null) {
throw new StateError("Attempt to assign initializer to variable "
"without an initialization node.");
}
variableInitialization!.initializer = value;
}
String? get name => cosmeticName;
}
/// Abstract parameter class, the parent for positional and named parameters.
sealed class FunctionParameter extends ExpressionVariable {
/// Function parameters can't be `const` or `late`, so they are assignable if
/// they aren't final.
@override
bool get isAssignable => !isFinal;
/// Function parameters don't have initializers, only default values.
@override
VariableInitialization? get variableInitialization => null;
@override
void set variableInitialization(VariableInitialization? value) {}
static const int FlagFinal = 1 << 0;
static const int FlagWildcard = 1 << 1;
static const int FlagCovariantByClass = 1 << 2;
static const int FlagCovariantByDeclaration = 1 << 3;
static const int FlagInitializingFormal = 1 << 4;
static const int FlagSuperInitializingFormal = 1 << 5;
static const int FlagRequired = 1 << 6;
@override
bool get isFinal => flags & FlagFinal != 0;
@override
void set isFinal(bool value) {
flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal);
}
@override
bool get isWildcard => flags & FlagWildcard != 0;
@override
void set isWildcard(bool value) {
flags = value ? (flags | FlagWildcard) : (flags & ~FlagWildcard);
}
@override
bool get isCovariantByClass => flags & FlagCovariantByClass != 0;
@override
void set isCovariantByClass(bool value) {
flags = value
? (flags | FlagCovariantByClass)
: (flags & ~FlagCovariantByClass);
}
@override
bool get isCovariantByDeclaration => flags & FlagCovariantByDeclaration != 0;
@override
void set isCovariantByDeclaration(bool value) {
flags = value
? (flags | FlagCovariantByDeclaration)
: (flags & ~FlagCovariantByDeclaration);
}
@override
bool get isInitializingFormal => flags & FlagInitializingFormal != 0;
@override
void set isInitializingFormal(bool value) {
flags = value
? (flags | FlagInitializingFormal)
: (flags & ~FlagInitializingFormal);
}
@override
bool get isSuperInitializingFormal =>
flags & FlagSuperInitializingFormal != 0;
@override
void set isSuperInitializingFormal(bool value) {
flags = value
? (flags | FlagSuperInitializingFormal)
: (flags & ~FlagSuperInitializingFormal);
}
@override
bool get isRequired => flags & FlagRequired != 0;
@override
void set isRequired(bool value) {
flags = value ? (flags | FlagRequired) : (flags & ~FlagRequired);
}
@override
bool get isErroneouslyInitialized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isErroneouslyInitialized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get hasDeclaredInitializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set hasDeclaredInitializer(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSynthesized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSynthesized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isConst {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isConst(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isLate {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isLate(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isHoisted {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isHoisted(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isLowered {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isLowered(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
}
/// Positional parameters. The [cosmeticName] field is optional and doesn't
/// affect the runtime semantics of the program.
class PositionalParameter extends FunctionParameter {
@override
String? cosmeticName;
@override
DartType type;
PositionalParameter({this.cosmeticName, required this.type});
@override
bool get isRequired {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isRequired(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitPositionalParameter(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitPositionalParameter(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
String toString() {
return "PositionalParameter(${toStringInternal()})";
}
@override
Expression? get initializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set initializer(Expression? value) {
throw new UnsupportedError("${this.runtimeType}");
}
String? get name => cosmeticName;
}
/// Named parameters. The [name] field is mandatory.
class NamedParameter extends FunctionParameter {
String name;
@override
String get cosmeticName => name;
@override
void set cosmeticName(String? value) {
name = value!;
}
@override
DartType type;
NamedParameter({required this.name, required this.type});
@override
R accept<R>(TreeVisitor<R> v) => v.visitNamedParameter(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitNamedParameter(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
String toString() {
return "NamedParameter(${toStringInternal()})";
}
@override
Expression? get initializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set initializer(Expression? value) {
throw new UnsupportedError("${this.runtimeType}");
}
}
/// The variable storage for `this`.
class ThisVariable extends ExpressionVariable {
@override
String get cosmeticName => "this";
@override
void set cosmeticName(String? value) {}
@override
VariableInitialization? get variableInitialization => null;
@override
void set variableInitialization(VariableInitialization? value) {}
@override
DartType type;
ThisVariable({required this.type});
@override
bool get isFinal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isFinal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isWildcard {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isWildcard(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isCovariantByClass {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByClass(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isCovariantByDeclaration {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByDeclaration(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSuperInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSuperInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isErroneouslyInitialized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isErroneouslyInitialized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get hasDeclaredInitializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set hasDeclaredInitializer(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isRequired {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isRequired(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSynthesized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSynthesized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isConst {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isConst(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isLate {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isLate(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isHoisted {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isHoisted(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isLowered {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isLowered(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitThisVariable(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitThisVariable(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
String toString() {
return "ThisVariable(${toStringInternal()})";
}
@override
bool get isAssignable => false;
@override
Expression? get initializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set initializer(Expression? value) {
throw new UnsupportedError("${this.runtimeType}");
}
String? get name => cosmeticName;
}
/// A variable introduced during desugaring. Such variables don't correspond to
/// any variable declared by the programmer.
class SyntheticVariable extends ExpressionVariable {
@override
String? cosmeticName;
@override
DartType type;
@override
VariableInitialization? variableInitialization;
SyntheticVariable({this.cosmeticName, required this.type});
static const int FlagFinal = 1 << 0;
static const int FlagLowered = 1 << 1;
static const int FlagHoisted = 1 << 2;
@override
bool get isFinal => flags & FlagFinal != 0;
@override
void set isFinal(bool value) {
flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal);
}
@override
bool get isLowered => flags & FlagLowered != 0;
@override
void set isLowered(bool value) {
flags = value ? (flags | FlagLowered) : (flags & ~FlagLowered);
}
@override
bool get isHoisted => flags & FlagHoisted != 0;
@override
void set isHoisted(bool value) {
flags = value ? (flags | FlagHoisted) : (flags & ~FlagHoisted);
}
@override
bool get isCovariantByClass {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByClass(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isCovariantByDeclaration {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isCovariantByDeclaration(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isErroneouslyInitialized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isErroneouslyInitialized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get hasDeclaredInitializer {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set hasDeclaredInitializer(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isRequired {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isRequired(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSuperInitializingFormal {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSuperInitializingFormal(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isSynthesized {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isSynthesized(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isConst {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isConst(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isLate {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isLate(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
bool get isWildcard {
throw new UnsupportedError("${this.runtimeType}");
}
@override
void set isWildcard(bool value) {
throw new UnsupportedError("${this.runtimeType}");
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitSyntheticVariable(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitSyntheticVariable(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
String toString() {
return "SyntheticVariable(${toStringInternal()})";
}
@override
bool get isAssignable => !isConst && !isFinal;
@override
Expression? get initializer => variableInitialization?.initializer;
@override
void set initializer(Expression? value) {
if (value != null && variableInitialization == null) {
throw new StateError("Attempt to assign initializer to variable "
"without an initialization node.");
}
variableInitialization!.initializer = value;
}
String? get name => cosmeticName;
}
/// The enum reflecting the kind of a variable context. A context is
/// [assertCaptured] if it contains the variables captured in a closure within
/// an `assert` and not captured anywhere outside of `assert`s.
enum CaptureKind {
notCaptured,
captured,
assertCaptured;
}
/// The box storing some of the variables in the scope it's associated with. It
/// serves as the "declaration" of the variables it contains for the runtime
/// environments.
class VariableContext extends TreeNode {
final CaptureKind captureKind;
final List<Variable> variables;
VariableContext({required this.captureKind, required this.variables});
@override
R accept<R>(TreeVisitor<R> v) {
// TODO(cstefantsova): Implement accept.
throw new UnimplementedError();
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
// TODO(cstefantsova): Implement accept1.
throw new UnimplementedError();
}
@override
void transformChildren(Transformer v) {
// TODO(cstefantsova): Implement transformChildren.
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
// TODO(cstefantsova): Implement transformOrRemoveChildren.
}
@override
void visitChildren(Visitor v) {
// TODO(cstefantsova): Implement visitChildren.
}
@override
String toString() {
return "VariableContext(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('[');
for (int index = 0; index < variables.length; index++) {
if (index > 0) {
printer.write(', ');
}
variables[index].toTextInternal(printer);
}
printer.write(']');
}
}
/// The collection of the contexts pertaining to a scope-inducing node. The
/// [Scopes] are supposed to be treated as the points of declaration of the
/// variables they contain. They aren't [Statement]s, but a runtime may choose
/// to interpret the [Scope] in an executable way before any [Statement]s or
/// [Expression]s of its node.
class Scope extends TreeNode {
final List<VariableContext> contexts;
Scope({required this.contexts});
@override
R accept<R>(TreeVisitor<R> v) {
// TODO(cstefantsova): Implement accept.
throw new UnimplementedError();
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
// TODO(cstefantsova): Implement accept1.
throw new UnimplementedError();
}
@override
void transformChildren(Transformer v) {
// TODO(cstefantsova): Implement transformChildren.
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
// TODO(cstefantsova): Implement transformOrRemoveChildren.
}
@override
void visitChildren(Visitor v) {
// TODO(cstefantsova): Implement visitChildren.
}
@override
String toString() {
return "Scope(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('[');
for (int index = 0; index < contexts.length; index++) {
if (index > 0) {
printer.write(', ');
}
contexts[index].toTextInternal(printer);
}
printer.write(']');
}
}
/// The root of the sealed hierarchy of the nodes that provide a scope, such as
/// loops, functions, and blocks.
sealed class ScopeProvider {
/// The scope of the [ScopeProvider].
///
/// It's represented as nullable due to the experimental status of the
/// feature. When the feature isn't enabled, [scope] should return null.
Scope? get scope;
}