mirror of https://github.com/AMT-Cheif/drift.git
Types2: Initial support for binary expressions
This commit is contained in:
parent
22525b24c3
commit
4484890609
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue