Skip to content

Commit

Permalink
fix: subtype check for literal and union types (#1288)
Browse files Browse the repository at this point in the history
### Summary of Changes

Previously, the type checker incorrectly showed an error in the
following program at the call marked with the arrow:

```
package bug

@pure fun bug1(p: union<Int, String>)
@pure fun bug2() -> r: literal<1, "">

pipeline bug3 {
    val a = bug2();
    bug1(a); // <----
}
```

This was caused because the quantifiers when comparing literal and union
types were in the wrong order:

- We checked whether there was an option in the union type that all
constants of the literal type could be assigned to.
- Now we check whether, for each constant of the literal type, there is
an option in the union type they could be assigned to.
  • Loading branch information
lars-reimann authored Dec 28, 2024
1 parent 3880bfe commit 911881c
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ export class SafeDsTypeChecker {
// Handle union types
if (type instanceof UnionType) {
return type.types.every((it) => this.isSubtypeOf(it, other, options));
} else if (other instanceof UnionType) {
} else if (other instanceof UnionType && !(type instanceof LiteralType)) {
// For literal types the quantifiers would be the wrong way around
return other.types.some((it) => this.isSubtypeOf(type, it, options));
}

Expand Down Expand Up @@ -264,17 +265,21 @@ export class SafeDsTypeChecker {
return true;
}

return type.constants.every((constant) => this.constantIsSubtypeOfClassType(constant, other, options));
return type.constants.every((constant) => this.constantIsSubtypeOf(constant, other, options));
} else if (other instanceof LiteralType) {
return type.constants.every((constant) =>
other.constants.some((otherConstant) => constant.equals(otherConstant)),
);
} else if (other instanceof UnionType) {
return type.constants.every((constant) =>
other.types.some((it) => this.constantIsSubtypeOf(constant, it, options)),
);
} else {
return false;
}
}

private constantIsSubtypeOfClassType(constant: Constant, other: ClassType, options: TypeCheckOptions): boolean {
private constantIsSubtypeOf(constant: Constant, other: Type, options: TypeCheckOptions): boolean {
const classType = this.typeComputer().computeClassTypeForConstant(constant);
return this.isSubtypeOf(classType, other, options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,11 @@ const basic = async (): Promise<IsSubOrSupertypeOfTest[]> => {
type2: factory.createUnionType(coreTypes.String),
expected: false,
},
{
type1: factory.createLiteralType(new IntConstant(1n), new StringConstant('')),
type2: factory.createUnionType(coreTypes.Int, coreTypes.String),
expected: true,
},
// Literal type to other
{
type1: factory.createLiteralType(), // Empty literal type
Expand Down

0 comments on commit 911881c

Please sign in to comment.