aboutsummaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2024-01-05 23:29:21 +0000
committerMichael Williamson <mike@zwobble.org>2024-01-05 23:29:21 +0000
commit6e2fdd6cee5b79eb59d6eff857e42f36242688e2 (patch)
treed65fbd90c6e447d0e672eff3ecb6fa78d03b48e6 /src/test/java
parent21b95752a91ccb623ce14a7258b2473f7b193b44 (diff)
Split tests for solving type constraints
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java178
1 files changed, 113 insertions, 65 deletions
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java
index 33e47ff8..79b80179 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java
@@ -133,15 +133,11 @@ public class TypeCheckerIsSubTypeTests {
Sources.NULL,
TypeCheckerSubTyping.constraints(List.of(typeParam), TypeInfos.EMPTY)
);
- var result = constraints.solve(Sources.NULL);
assertThat(constraints.typeBounds().get(typeParam), allOf(
has("lower", x -> x.lower(), isSequence(equalTo(Types.INT))),
has("upper", x -> x.upper(), isSequence())
));
- assertThat(result, equalTo(TypeSubstitutionMap.of(
- Map.entry(typeParam, Types.INT)
- )));
}
@Test
@@ -157,15 +153,11 @@ public class TypeCheckerIsSubTypeTests {
Sources.NULL,
TypeCheckerSubTyping.constraints(List.of(typeParam), TypeInfos.EMPTY)
);
- var result = constraints.solve(Sources.NULL);
assertThat(constraints.typeBounds().get(typeParam), allOf(
has("lower", x -> x.lower(), isSequence()),
has("upper", x -> x.upper(), isSequence(equalTo(Types.INT)))
));
- assertThat(result, equalTo(TypeSubstitutionMap.of(
- Map.entry(typeParam, Types.BOTTOM)
- )));
}
@Test
@@ -386,11 +378,11 @@ public class TypeCheckerIsSubTypeTests {
Sources.NULL,
constraints
);
- var result = constraints.solve(Sources.NULL);
- assertThat(result, equalTo(TypeSubstitutionMap.of(
- Map.entry(TypeParam.forSumType(sumType, "T"), Types.STRING)
- )));
+ assertThat(constraints.typeBounds().get(TypeParam.forSumType(sumType, "T")), allOf(
+ has("lower", x -> x.lower(), isSequence(equalTo(Types.STRING))),
+ has("upper", x -> x.upper(), isSequence(equalTo(Types.STRING)))
+ ));
}
// == Constructed types ==
@@ -422,60 +414,11 @@ public class TypeCheckerIsSubTypeTests {
Sources.NULL,
constraints
);
- var result = constraints.solve(Sources.NULL);
-
- assertThat(result, equalTo(TypeSubstitutionMap.of(
- Map.entry(TypeParam.forSumType(sumType, "T"), Types.STRING)
- )));
- }
-
- @Test
- public void constructedTypesWithDifferentArgsCannotBothBeSubtypesOfConstructedTypeWithInvariantTypeParam() {
- var namespaceName = NamespaceName.of("Example", "Project");
- var sumType = Types.sum(namespaceName, "Sum");
- var structType = Types.struct(namespaceName, "Struct");
- var typeConstructor = Types.typeConstructor(
- List.of(TypeParam.forStructType(structType, "T")),
- structType
- );
- var typeInfos = TypeInfos.EMPTY;
-
- var constraints = TypeCheckerSubTyping.constraints(
- List.of(TypeParam.forSumType(sumType, "T")),
- typeInfos
- );
- constraints = TypeCheckerSubTyping.addSubtypeConstraint(
- Types.constructedStructType(
- typeConstructor,
- List.of(Types.STRING)
- ),
- Types.constructedStructType(
- typeConstructor,
- List.of(TypeParam.forSumType(sumType, "T"))
- ),
- Sources.NULL,
- constraints
- );
- constraints = TypeCheckerSubTyping.addSubtypeConstraint(
- Types.constructedStructType(
- typeConstructor,
- List.of(Types.INT)
- ),
- Types.constructedStructType(
- typeConstructor,
- List.of(TypeParam.forSumType(sumType, "T"))
- ),
- Sources.NULL,
- constraints
- );
- var c = constraints;
- var error = assertThrows(
- // TODO: more specific error
- IllegalStateException.class,
- () -> c.solve(Sources.NULL)
- );
- assertThat(error.getMessage(), equalTo("Incompatible type bounds"));
+ assertThat(constraints.typeBounds().get(TypeParam.forSumType(sumType, "T")), allOf(
+ has("lower", x -> x.lower(), isSequence(equalTo(Types.STRING))),
+ has("upper", x -> x.upper(), isSequence(equalTo(Types.STRING)))
+ ));
}
// == Function types ==
@@ -591,4 +534,109 @@ public class TypeCheckerIsSubTypeTests {
assertThat(isSubType(functionType2, functionType1, typeInfos), equalTo(false));
assertThat(isSubType(functionType1, functionType2, typeInfos), equalTo(false));
}
+
+ // == Constraint solving ==
+
+ @Test
+ public void whenTypeParamHasUpperBoundThenTypeParamIsResolvedToBottom() {
+ var typeParam = TypeParam.forStructType(
+ Types.struct(NamespaceName.of("Example"), "Struct"),
+ "T"
+ );
+
+ var constraints = TypeCheckerSubTyping.constraints(List.of(typeParam), TypeInfos.EMPTY)
+ .addUpperBound(typeParam, Types.INT);
+ var result = constraints.solve(Sources.NULL);
+
+ assertThat(result, equalTo(TypeSubstitutionMap.of(
+ Map.entry(typeParam, Types.BOTTOM)
+ )));
+ }
+
+ @Test
+ public void whenTypeParamHasSingleLowerBoundThenTypeParamIsResolvedToLowerBound() {
+ var typeParam = TypeParam.forStructType(
+ Types.struct(NamespaceName.of("Example"), "Struct"),
+ "T"
+ );
+
+ var constraints = TypeCheckerSubTyping.constraints(List.of(typeParam), TypeInfos.EMPTY)
+ .addLowerBound(typeParam, Types.INT);
+ var result = constraints.solve(Sources.NULL);
+
+ assertThat(result, equalTo(TypeSubstitutionMap.of(
+ Map.entry(typeParam, Types.INT)
+ )));
+ }
+
+ @Test
+ public void whenTypeParamHasMultipleLowerBoundsThenTypeParamIsResolvedToCommonSupertype() {
+ var typeParam = TypeParam.forStructType(
+ Types.struct(NamespaceName.of("Example"), "Struct"),
+ "T"
+ );
+
+ var constraints = TypeCheckerSubTyping.constraints(List.of(typeParam), TypeInfos.EMPTY)
+ .addLowerBound(typeParam, Types.INT)
+ .addLowerBound(typeParam, Types.STRING);
+ var result = constraints.solve(Sources.NULL);
+
+ assertThat(result, equalTo(TypeSubstitutionMap.of(
+ Map.entry(typeParam, Types.TOP)
+ )));
+ }
+
+ @Test
+ public void whenTypeParamHasCompatibleUpperAndLowerBoundsThenTypeParamIsResolvedToLowerBound() {
+ var typeParam = TypeParam.forStructType(
+ Types.struct(NamespaceName.of("Example"), "Struct"),
+ "T"
+ );
+ var namespaceName = NamespaceName.of("Example", "Project");
+ var sumType = Types.sum(namespaceName, "Sum");
+ var variantType1 = Types.struct(namespaceName, "Variant1");
+ var variantType2 = Types.struct(namespaceName, "Variant2");
+ var typeInfos = TypeInfos.EMPTY
+ .addSumTypeInfo(sumType, new SumTypeInfo(P.orderedSet(
+ variantType1,
+ variantType2
+ )));
+
+ var constraints = TypeCheckerSubTyping.constraints(List.of(typeParam), typeInfos)
+ .addLowerBound(typeParam, variantType1)
+ .addUpperBound(typeParam, sumType);
+ var result = constraints.solve(Sources.NULL);
+
+ assertThat(result, equalTo(TypeSubstitutionMap.of(
+ Map.entry(typeParam, variantType1)
+ )));
+ }
+
+ @Test
+ public void whenTypeParamHasConflictingUpperAndLowerBoundsThenErrorIsThrown() {
+ var typeParam = TypeParam.forStructType(
+ Types.struct(NamespaceName.of("Example"), "Struct"),
+ "T"
+ );
+ var namespaceName = NamespaceName.of("Example", "Project");
+ var sumType = Types.sum(namespaceName, "Sum");
+ var variantType1 = Types.struct(namespaceName, "Variant1");
+ var variantType2 = Types.struct(namespaceName, "Variant2");
+ var typeInfos = TypeInfos.EMPTY
+ .addSumTypeInfo(sumType, new SumTypeInfo(P.orderedSet(
+ variantType1,
+ variantType2
+ )));
+
+ var constraints = TypeCheckerSubTyping.constraints(List.of(typeParam), typeInfos)
+ .addLowerBound(typeParam, sumType)
+ .addUpperBound(typeParam, variantType1);
+ var error = assertThrows(
+ // TODO: more specific error
+ IllegalStateException.class,
+ () -> constraints.solve(Sources.NULL)
+ );
+
+ assertThat(error.getMessage(), equalTo("Incompatible type bounds"));
+ }
}