mirror of https://github.com/AMT-Cheif/drift.git
Fix nullability analysis for COALESCE and IFNULL
This commit is contained in:
parent
7b86f69102
commit
33fd36df80
|
@ -38,8 +38,10 @@ class CopyEncapsulating extends TypeRelation implements MultiSourceRelation {
|
|||
final List<Typeable?> from;
|
||||
|
||||
final CastMode? cast;
|
||||
final EncapsulatingNullability nullability;
|
||||
|
||||
CopyEncapsulating(this.target, this.from, [this.cast]);
|
||||
CopyEncapsulating(this.target, this.from,
|
||||
[this.cast, this.nullability = EncapsulatingNullability.nullIfAny]);
|
||||
}
|
||||
|
||||
/// Dependency declaring that [first] and [second] have the same type. This is
|
||||
|
@ -77,6 +79,11 @@ enum CastMode {
|
|||
boolean,
|
||||
}
|
||||
|
||||
enum EncapsulatingNullability {
|
||||
nullIfAny,
|
||||
nullIfAll,
|
||||
}
|
||||
|
||||
/// Dependency declaring that [target] has the same type as [other] after
|
||||
/// casting it with [cast].
|
||||
class CopyAndCast extends TypeRelation implements DirectedRelation {
|
||||
|
|
|
@ -118,7 +118,7 @@ class TypeGraph {
|
|||
if (edge is CopyEncapsulating) {
|
||||
if (!knowsType(edge.target)) {
|
||||
final fromTypes = edge.from.map((t) => this[t]).where((e) => e != null);
|
||||
var encapsulated = _encapsulate(fromTypes);
|
||||
var encapsulated = _encapsulate(fromTypes, edge.nullability);
|
||||
|
||||
if (encapsulated != null) {
|
||||
if (edge.cast != null) {
|
||||
|
@ -148,19 +148,31 @@ class TypeGraph {
|
|||
}
|
||||
}
|
||||
|
||||
ResolvedType? _encapsulate(Iterable<ResolvedType?> targets) {
|
||||
ResolvedType? _encapsulate(
|
||||
Iterable<ResolvedType?> targets, EncapsulatingNullability nullability) {
|
||||
return targets.fold<ResolvedType?>(null, (previous, element) {
|
||||
if (previous == null) return element;
|
||||
|
||||
final previousType = previous.type;
|
||||
final elementType = element!.type;
|
||||
final eitherNullable =
|
||||
previous.nullable == true || element.nullable == true;
|
||||
bool nullableTogether;
|
||||
switch (nullability) {
|
||||
case EncapsulatingNullability.nullIfAny:
|
||||
nullableTogether =
|
||||
previous.nullable == true || element.nullable == true;
|
||||
break;
|
||||
case EncapsulatingNullability.nullIfAll:
|
||||
nullableTogether =
|
||||
previous.nullable == true && element.nullable == true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (previousType == elementType || elementType == BasicType.nullType) {
|
||||
return previous.withNullable(eitherNullable);
|
||||
return previous.withNullable(nullableTogether);
|
||||
}
|
||||
if (previousType == BasicType.nullType) {
|
||||
return element.withNullable(nullableTogether);
|
||||
}
|
||||
if (previousType == BasicType.nullType) return element.withNullable(true);
|
||||
|
||||
bool isIntOrNumeric(BasicType? type) {
|
||||
return type == BasicType.int || type == BasicType.real;
|
||||
|
@ -168,7 +180,7 @@ class TypeGraph {
|
|||
|
||||
// encapsulate two different numeric types to real
|
||||
if (isIntOrNumeric(previousType) && isIntOrNumeric(elementType)) {
|
||||
return ResolvedType(type: BasicType.real, nullable: eitherNullable);
|
||||
return ResolvedType(type: BasicType.real, nullable: nullableTogether);
|
||||
}
|
||||
|
||||
// fallback to text if everything else fails
|
||||
|
|
|
@ -531,7 +531,8 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
|
|||
return null;
|
||||
case 'coalesce':
|
||||
case 'ifnull':
|
||||
session._addRelation(CopyEncapsulating(e, params));
|
||||
session._addRelation(CopyEncapsulating(
|
||||
e, params, null, EncapsulatingNullability.nullIfAll));
|
||||
for (final param in params) {
|
||||
session._addRelation(DefaultType(param, isNullable: true));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue