diff options
| author | Michael Williamson <mike@zwobble.org> | 2024-01-05 23:29:21 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2024-01-05 23:29:21 +0000 |
| commit | 6e2fdd6cee5b79eb59d6eff857e42f36242688e2 (patch) | |
| tree | d65fbd90c6e447d0e672eff3ecb6fa78d03b48e6 /src/test/java | |
| parent | 21b95752a91ccb623ce14a7258b2473f7b193b44 (diff) | |
Split tests for solving type constraints
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIsSubTypeTests.java | 178 |
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")); + } } |
