Types2: Initial support for binary expressions

This commit is contained in:
Simon Binder 2019-12-30 20:14:29 +01:00
parent 22525b24c3
commit 4484890609
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
3 changed files with 95 additions and 0 deletions

View File

@ -17,6 +17,33 @@ class CopyTypeFrom extends TypeRelationship {
CopyTypeFrom(this.target, this.other); CopyTypeFrom(this.target, this.other);
} }
/// Dependency declaring that [target] has a type that matches all of [from].
class CopyEncapsulating extends TypeRelationship {
final Typeable target;
final List<Typeable> from;
CopyEncapsulating(this.target, this.from);
}
/// Dependency declaring that [first] and [second] have the same type. This is
/// an optional dependency that will only be applied when one type is known and
/// the other is not.
class HaveSameType extends TypeRelationship {
final Typeable first;
final Typeable second;
HaveSameType(this.first, this.second);
}
/// Dependency declaring that, if no better option is found, [target] should
/// have the specified [defaultType].
class DefaultType extends TypeRelationship {
final Typeable target;
final ResolvedType defaultType;
DefaultType(this.target, this.defaultType);
}
enum CastMode { numeric, boolean } enum CastMode { numeric, boolean }
/// Dependency declaring that [target] has the same type as [other] after /// Dependency declaring that [target] has the same type as [other] after

View File

@ -93,6 +93,69 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
} }
} }
@override
void visitBinaryExpression(BinaryExpression e, TypeExpectation arg) {
switch (e.operator.type) {
case TokenType.and:
case TokenType.or:
session.checkAndResolve(e, const ResolvedType.bool(), arg);
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
// logic expressions, so children must be boolean
visitChildren(e, const ExactTypeExpectation.laxly(ResolvedType.bool()));
break;
case TokenType.equal:
case TokenType.exclamationEqual:
case TokenType.lessMore:
case TokenType.less:
case TokenType.lessEqual:
case TokenType.more:
case TokenType.moreEqual:
// comparison. Returns bool, copying nullability from children.
session.checkAndResolve(e, const ResolvedType.bool(), arg);
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
// Not technically a requirement, but assume lhs and rhs have the same
// type.
session.addRelationship(HaveSameType(e.left, e.right));
visitChildren(e, arg);
break;
case TokenType.plus:
case TokenType.minus:
session.addRelationship(CopyEncapsulating(e, [e.left, e.right]));
break;
// all of those only really make sense for integers
case TokenType.shiftLeft:
case TokenType.shiftRight:
case TokenType.pipe:
case TokenType.ampersand:
case TokenType.percent:
const type = ResolvedType(type: BasicType.int);
session.checkAndResolve(e, type, arg);
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
visitChildren(e, const ExactTypeExpectation.laxly(type));
break;
case TokenType.doublePipe:
// string concatenation.
const stringType = ResolvedType(type: BasicType.text);
session.checkAndResolve(e, stringType, arg);
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
const childExpectation = ExactTypeExpectation.laxly(stringType);
visit(e.left, childExpectation);
visit(e.right, childExpectation);
break;
default:
throw StateError('Binary operator ${e.operator.type} not recognized '
'by types2. At $e');
}
}
@override
void visitIsExpression(IsExpression e, TypeExpectation arg) {
session.checkAndResolve(e, const ResolvedType.bool(), arg);
session.hintNullability(e, false);
visitChildren(e, const NoTypeExpectation());
}
void _handleWhereClause(HasWhereClause stmt) { void _handleWhereClause(HasWhereClause stmt) {
// assume that a where statement is a boolean expression. Sqlite internally // assume that a where statement is a boolean expression. Sqlite internally
// casts (https://www.sqlite.org/lang_expr.html#booleanexpr), so be lax // casts (https://www.sqlite.org/lang_expr.html#booleanexpr), so be lax

View File

@ -56,4 +56,9 @@ void main() {
const ResolvedType.bool(), const ResolvedType.bool(),
); );
}); });
test('infers type in a string concatenation', () {
expect(_resolveFirstVariable("SELECT '' || :foo"),
const ResolvedType(type: BasicType.text));
});
} }