Skip to content

Commit

Permalink
0.7.2 wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanblake4 committed Nov 17, 2023
1 parent 18d6a88 commit 9d2d4a7
Show file tree
Hide file tree
Showing 37 changed files with 664 additions and 175 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.7.2
- Support inferring variable types when using the `if (x is Y)` pattern
- Support for setters with a different type than getters / fields of the
same name
- Support for return statements without a value
- Fix type resolution of super constructor parameters
- Add binding for `ArgumentError`

## 0.7.1
- Eliminate requirement to call setup() on the Runtime. Setup is now
invoked automatically and the setup method is deprecated.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void main() {
class Cat {
Cat(this.name);
final String name;
String speak() => "I'm $name!';
String speak() => "I'm $name!";
}
String main() {
final cat = Cat('Fluffy');
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/bindgen/bindgen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/// Adapted from code by Alex Wallen (@a-wallen)
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
Expand All @@ -14,6 +13,7 @@ import 'dart:io' as io;
import 'package:package_config/package_config.dart';
import 'package:path/path.dart';

/// Adapted from code by Alex Wallen (@a-wallen)
class Bindgen {
static final resourceProvider = PhysicalResourceProvider.INSTANCE;
final includedPaths = [resourceProvider.pathContext.current];
Expand Down
4 changes: 2 additions & 2 deletions lib/src/eval/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,8 @@ class Compiler implements BridgeDeclarationRegistry, EvalPluginRegistry {
_ctx.resetStack();
});
});
} on CompileError catch (e) {
throw e.copyWithContext(_ctx);
} on CompileError catch (e, stk) {
Error.throwWithStackTrace(e.copyWithContext(_ctx), stk);
}

for (final library in reachableLibraries) {
Expand Down
52 changes: 51 additions & 1 deletion lib/src/eval/compiler/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ class CompilerContext with ScopeContext {
List<String> typeNames = [];
List<Set<int>> typeTypes = [];
List<bool> scopeDoesClose = [];
List<ContextSaveState> typeInferenceSaveStates = [];
List<ContextSaveState> typeUninferenceSaveStates = [];
List<bool> inTypeInferenceContext = [];
final List<Variable> caughtExceptions = [];
PrescanContext? preScan;
int nearestAsyncFrame = -1;
Expand Down Expand Up @@ -290,9 +293,53 @@ class CompilerContext with ScopeContext {
super.restoreState(initial);
scopeDoesClose = initial.scopeDoesClose;
}

void enterTypeInferenceContext() {
typeInferenceSaveStates.add(saveState());
inTypeInferenceContext.add(true);
}

void inferTypes() {
final inferredLocals = typeInferenceSaveStates.removeLast().locals;
typeUninferenceSaveStates.add(saveState());
final _myLocals = [...locals];
for (var i = 0;
i < math.min(inferredLocals.length, _myLocals.length);
i++) {
final inferredLocalsMap = inferredLocals[i];
final _myLocalsMap = _myLocals[i];

inferredLocalsMap.forEach((key, value) {
final myLocal = _myLocalsMap[key];
if (myLocal != null && myLocal.type != value.type) {
locals[i][key] =
myLocal.copyWith(type: value.type.copyWith(boxed: myLocal.boxed));
}
});
}
}

void uninferTypes() {
final uninferredLocals = typeUninferenceSaveStates.removeLast().locals;
final _myLocals = [...locals];
for (var i = 0;
i < math.min(uninferredLocals.length, _myLocals.length);
i++) {
final uninferredLocalsMap = uninferredLocals[i];
final _myLocalsMap = _myLocals[i];

uninferredLocalsMap.forEach((key, value) {
final myLocal = _myLocalsMap[key];
if (myLocal != null && myLocal.type != value.type) {
locals[i][key] =
myLocal.copyWith(type: value.type.copyWith(boxed: myLocal.boxed));
}
});
}
}
}

class ContextSaveState {
class ContextSaveState with ScopeContext {
ContextSaveState.of(AbstractScopeContext context)
: locals = [
...context.locals.map((e) => {...e})
Expand All @@ -303,4 +350,7 @@ class ContextSaveState {
List<Map<String, Variable>> locals;
List<bool> scopeDoesClose;
List<int> allocNest;

@override
int pushOp(EvcOp op, int length) => throw UnimplementedError();
}
3 changes: 2 additions & 1 deletion lib/src/eval/compiler/declaration/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ void compileConstructorDeclaration(
_type = TypeRef.fromAnnotation(ctx, ctx.library, p.type!);
}
_type ??= TypeRef.lookupFieldType(ctx,
TypeRef.lookupDeclaration(ctx, ctx.library, parent), p.name.lexeme);
TypeRef.lookupDeclaration(ctx, ctx.library, parent), p.name.lexeme,
source: p);
_type ??= V?.type;
_type ??= CoreTypes.dynamic.ref(ctx);

Expand Down
5 changes: 1 addition & 4 deletions lib/src/eval/compiler/declaration/method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ int compileMethodDeclaration(MethodDeclaration d, CompilerContext ctx,

ctx.beginAllocScope(existingAllocLen: (d.parameters?.parameters.length ?? 0));
ctx.scopeFrameOffset += d.parameters?.parameters.length ?? 0;
ctx.setLocal(
'#this',
Variable(
0, ctx.visibleTypes[ctx.library]![ctx.currentClass!.name.lexeme]!));
ctx.setLocal('#this', Variable(0, TypeRef.$this(ctx)!));
final resolvedParams = d.parameters == null
? <PossiblyValuedParameter>[]
: resolveFPLDefaults(ctx, d.parameters, true, allowUnboxed: true);
Expand Down
6 changes: 0 additions & 6 deletions lib/src/eval/compiler/expression/assignment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:dart_eval/src/eval/compiler/builtins.dart';
import 'package:dart_eval/src/eval/compiler/context.dart';
import 'package:dart_eval/src/eval/compiler/errors.dart';
import 'package:dart_eval/src/eval/compiler/expression/expression.dart';
import 'package:dart_eval/src/eval/compiler/macros/branch.dart';
import 'package:dart_eval/src/eval/compiler/statement/statement.dart';
Expand All @@ -14,11 +13,6 @@ Variable compileAssignmentExpression(
final R = compileExpression(e.rightHandSide, ctx);

if (e.operator.type == TokenType.EQ) {
final lType = L.resolveType(ctx).resolveTypeChain(ctx);
if (!R.type.resolveTypeChain(ctx).isAssignableTo(ctx, lType)) {
throw CompileError(
'Syntax error: cannot assign value of type ${R.type} to $lType');
}
return L.setValue(ctx, R);
} else if (e.operator.type.binaryOperatorOfCompoundAssignment ==
TokenType.QUESTION_QUESTION) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/eval/compiler/expression/await.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Variable compileAwaitExpression(AwaitExpression e, CompilerContext ctx) {
final subject = compileExpression(e.expression, ctx);
final type = subject.type.resolveTypeChain(ctx);

if (!type.isAssignableTo(ctx, TypeRef.stdlib(ctx, 'dart:core', 'Future'))) {
if (!type.isAssignableTo(ctx, CoreTypes.future.ref(ctx))) {
throw CompileError("Cannot await something that isn't a Future");
}

Expand Down
2 changes: 2 additions & 0 deletions lib/src/eval/compiler/expression/is.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Variable compileIsExpression(IsExpression e, CompilerContext ctx) {
final slot = TypeRef.fromAnnotation(ctx, ctx.library, e.type);
final not = e.notOperator != null;

V.inferType(ctx, slot);

/// If the type is definitely a subtype of the slot, we can just return true.
if (V.type.isAssignableTo(ctx, slot, forceAllowDynamic: false)) {
return BuiltinValue(boolval: !not).push(ctx);
Expand Down
14 changes: 4 additions & 10 deletions lib/src/eval/compiler/expression/method_invocation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,9 @@ Variable compileMethodInvocation(CompilerContext ctx, MethodInvocation e,
ctx.offsetTracker.setOffset(loc, offset);
}
ctx.pushOp(PushReturnValue.make(), PushReturnValue.LEN);
TypeRef? thisType;
if (ctx.currentClass != null) {
thisType =
ctx.visibleTypes[ctx.library]![ctx.currentClass!.name.lexeme]!;
}
mReturnType =
method.methodReturnType?.toAlwaysReturnType(ctx, thisType, [], {}) ??
AlwaysReturnType(CoreTypes.dynamic.ref(ctx), true);
mReturnType = method.methodReturnType
?.toAlwaysReturnType(ctx, TypeRef.$this(ctx), [], {}) ??
AlwaysReturnType(CoreTypes.dynamic.ref(ctx), true);
final _returnType = mReturnType.type?.copyWith(
boxed: L != null ||
!(mReturnType.type?.isUnboxedAcrossFunctionBoundaries ?? false));
Expand Down Expand Up @@ -346,8 +341,7 @@ DeclarationOrBridge<MethodDeclaration, BridgeMethodDef> resolveInstanceMethod(
final $extendsBridgeType =
bridge is BridgeClassDef ? bridge.type.$extends : null;
if ($extendsBridgeType == null && bridge is! BridgeEnumDef) {
throw CompileError(
'Method not found $methodName on $_bottomType', source);
throw CompileError('Unknown method $_bottomType.$methodName', source);
}
final $extendsType = bridge is BridgeEnumDef
? CoreTypes.enumType.ref(ctx)
Expand Down
6 changes: 4 additions & 2 deletions lib/src/eval/compiler/expression/prefix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ Variable compilePrefixExpression(CompilerContext ctx, PrefixExpression e) {
V.type != CoreTypes.int.ref(ctx) &&
V.type != CoreTypes.double.ref(ctx)) {
throw CompileError(
'Unary operator "-" is currently only supported for ints and doubles');
'Unary prefix "-" is currently only supported for ints and doubles (type: ${V.type})',
e);
} else if (method == '!' && V.type != CoreTypes.bool.ref(ctx)) {
throw CompileError(
'Unary operator "!" is currently only supported for bools');
'Unary prefix "!" is currently only supported for bools (type: ${V.type})',
e);
}

if (method == "!") {
Expand Down
38 changes: 25 additions & 13 deletions lib/src/eval/compiler/helpers/argument_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ Pair<List<Variable>, Map<String, Variable>> compileArgumentList(
if (!_arg.type.resolveTypeChain(ctx).isAssignableTo(ctx, paramType)) {
throw CompileError(
'Cannot assign argument of type ${_arg.type.toStringClear(ctx, paramType)}'
' to parameter "${param.name!.lexeme}" of type ${paramType.toStringClear(ctx, _arg.type)}');
' to parameter "${param.name!.lexeme}" of type ${paramType.toStringClear(ctx, _arg.type)}',
source ?? parameterHost);
}

if (typeAnnotation != null) {
Expand Down Expand Up @@ -224,7 +225,8 @@ Pair<List<Variable>, Map<String, Variable>>
ArgumentList argumentList,
List<KnownMethodArg> params,
Map<String, KnownMethodArg> namedParams,
{List<Variable> before = const []}) {
{List<Variable> before = const [],
AstNode? source}) {
final _args = <Variable>[];
final _push = <Variable>[];
final _namedArgs = <String, Variable>{};
Expand Down Expand Up @@ -281,7 +283,8 @@ Pair<List<Variable>, Map<String, Variable>>
.boxIfNeeded(ctx);
if (!_arg.type.resolveTypeChain(ctx).isAssignableTo(ctx, paramType)) {
throw CompileError(
'Cannot assign argument of type ${_arg.type} to parameter of type $paramType');
'Cannot assign argument of type ${_arg.type} to parameter of type $paramType',
source);
}
_push.add(_arg);
_namedArgs[param.name] = _arg;
Expand Down Expand Up @@ -375,7 +378,8 @@ Pair<List<Variable>, Map<String, Variable>> compileArgumentListWithBridge(
}
if (!_arg.type.resolveTypeChain(ctx).isAssignableTo(ctx, paramType)) {
throw CompileError(
'Cannot assign argument of type ${_arg.type} to parameter of type $paramType');
'Cannot assign argument of type ${_arg.type} to parameter of type $paramType',
argumentList);
}
_push.add(_arg);
_namedArgs[param.name] = _arg;
Expand All @@ -401,7 +405,7 @@ TypeRef _resolveFieldFormalType(CompilerContext ctx, int decLibrary,
final $class = parameterHost.parent as NamedCompilationUnitMember;
return TypeRef.lookupFieldType(ctx,
TypeRef.lookupDeclaration(ctx, decLibrary, $class), param.name.lexeme,
forFieldFormal: true) ??
forFieldFormal: true, source: param) ??
CoreTypes.dynamic.ref(ctx);
}

Expand Down Expand Up @@ -435,22 +439,30 @@ TypeRef resolveSuperFormalType(CompilerContext ctx, int decLibrary,
} else {
final cstr = superCstr.declaration as ConstructorDeclaration;
for (final _param in cstr.parameters.parameters) {
if (_param is SimpleFormalParameter &&
_param.name!.lexeme == param.name.lexeme) {
final _type = _param.type;
var __param =
_param is DefaultFormalParameter ? _param.parameter : _param;
if (__param.name?.lexeme != param.name.lexeme) {
continue;
}
if (__param is SimpleFormalParameter) {
final _type = __param.type;
if (_type == null) {
return CoreTypes.dynamic.ref(ctx);
}
return TypeRef.fromAnnotation(ctx, $super.file, _type);
} else if (_param is FieldFormalParameter) {
return _resolveFieldFormalType(ctx, decLibrary, _param, cstr);
} else if (_param is SuperFormalParameter) {
return resolveSuperFormalType(ctx, decLibrary, _param, cstr);
} else if (__param is FieldFormalParameter) {
return _resolveFieldFormalType(ctx, decLibrary, __param, cstr);
} else if (__param is SuperFormalParameter) {
return resolveSuperFormalType(ctx, decLibrary, __param, cstr);
} else {
throw CompileError(
'Unknown parameter type ${__param.runtimeType}', __param);
}
}
}

throw CompileError(
'Could not find parameter ${param.name.value()} in the referenced superclass constructor',
param);
param,
decLibrary);
}
3 changes: 1 addition & 2 deletions lib/src/eval/compiler/helpers/return.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ StatementInfo doReturn(
var _value = value.boxIfNeeded(ctx);

if (!_value.type.isAssignableTo(ctx, expected)) {
if (_value.type
.isAssignableTo(ctx, TypeRef.stdlib(ctx, 'dart:core', 'Future'))) {
if (_value.type.isAssignableTo(ctx, CoreTypes.future.ref(ctx))) {
final vta = _value.type.specifiedTypeArgs;
final vtype = vta.isEmpty ? CoreTypes.dynamic.ref(ctx) : vta[0];
if (vtype.isAssignableTo(ctx, expected)) {
Expand Down
3 changes: 3 additions & 0 deletions lib/src/eval/compiler/macros/branch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ StatementInfo macroBranch(
required MacroStatementClosure thenBranch,
MacroStatementClosure? elseBranch}) {
ctx.beginAllocScope();
ctx.enterTypeInferenceContext();
final conditionResult = condition(ctx).unboxIfNeeded(ctx);

final rewriteCond = JumpIfFalse.make(conditionResult.scopeFrameOffset, -1);
final rewritePos = ctx.pushOp(rewriteCond, JumpIfFalse.LEN);

final _initialState = ctx.saveState();

ctx.inferTypes();
ctx.beginAllocScope();
final thenResult = thenBranch(ctx, expectedReturnType);
ctx.endAllocScope();
ctx.uninferTypes();

ctx.resolveBranchStateDiscontinuity(_initialState);

Expand Down
Loading

0 comments on commit 9d2d4a7

Please sign in to comment.