package org.zwobble.hobgoblin.compiler.analysis; import org.junit.jupiter.api.Test; import org.zwobble.hobgoblin.compiler.analysis.errors.SumTypeHasNoVariantsError; import org.zwobble.hobgoblin.compiler.analysis.errors.TypeIsInfiniteError; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.sources.NullSource; import org.zwobble.hobgoblin.compiler.typechecker.TypesInfoInMemory; import org.zwobble.hobgoblin.compiler.types.*; import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.zwobble.hobgoblin.compiler.analysis.ArbitraryValueAnalysis.selectArbitraryVariant; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.equalTo; public class ArbitraryValueAnalysisTests { @Test public void whenTypeHasNoVariantsThenErrorIsThrown() { var sumType = new SumType(NamespaceName.of(), "X"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType(sumType, List.of(), List.of(), NullSource.INSTANCE); var error = assertThrows( SumTypeHasNoVariantsError.class, () -> selectArbitraryVariant(sumType, typesInfo) ); assertThat(error.type(), equalTo(sumType)); } @Test public void whenTypeIsInfiniteThenErrorIsThrown() { var sumType = new SumType(NamespaceName.of(), "X"); var variantType = new SimpleStructType(NamespaceName.of(), "Y"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, variantType, variantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(variantType, Optional.of(List.of( new Field("x", sumType, NullSource.INSTANCE) ))); var error = assertThrows( TypeIsInfiniteError.class, () -> selectArbitraryVariant(sumType, typesInfo) ); assertThat(error.type(), equalTo(sumType)); } @Test public void firstNonRecursivelyConstructibleVariantIsChosen() { var sumType = new SumType(NamespaceName.of(), "X"); var recursiveVariantType = new SimpleStructType(NamespaceName.of(), "Y"); var nonRecursiveVariantType = new SimpleStructType(NamespaceName.of(), "Z"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, recursiveVariantType, recursiveVariantType), new SumVariant(1, nonRecursiveVariantType, nonRecursiveVariantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(recursiveVariantType, Optional.of(List.of( new Field("x", sumType, NullSource.INSTANCE) ))); typesInfo.defineStructType(nonRecursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.INT_64, NullSource.INSTANCE) ))); var variant = selectArbitraryVariant(sumType, typesInfo); assertThat(variant, equalTo(nonRecursiveVariantType)); } @Test public void boxIsRecursivelyConstructibleIffInnerTypeIsRecursivelyConstructible() { var sumType = new SumType(NamespaceName.of(), "X"); var recursiveVariantType = new SimpleStructType(NamespaceName.of(), "Y"); var nonRecursiveVariantType = new SimpleStructType(NamespaceName.of(), "Z"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, recursiveVariantType, recursiveVariantType), new SumVariant(1, nonRecursiveVariantType, nonRecursiveVariantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(recursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.box(sumType), NullSource.INSTANCE) ))); typesInfo.defineStructType(nonRecursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.box(NativeTypes.INT_64), NullSource.INSTANCE) ))); var variant = selectArbitraryVariant(sumType, typesInfo); assertThat(variant, equalTo(nonRecursiveVariantType)); } @Test public void listIsAlwaysNonRecursivelyConstructible() { var sumType = new SumType(NamespaceName.of(), "X"); var recursiveVariantType = new SimpleStructType(NamespaceName.of(), "Y"); var nonRecursiveVariantType = new SimpleStructType(NamespaceName.of(), "Z"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, recursiveVariantType, recursiveVariantType), new SumVariant(1, nonRecursiveVariantType, nonRecursiveVariantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(recursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.list(sumType), NullSource.INSTANCE) ))); typesInfo.defineStructType(nonRecursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.list(NativeTypes.INT_64), NullSource.INSTANCE) ))); var variant = selectArbitraryVariant(sumType, typesInfo); assertThat(variant, equalTo(recursiveVariantType)); } @Test public void optionIsAlwaysNonRecursivelyConstructible() { var sumType = new SumType(NamespaceName.of(), "X"); var recursiveVariantType = new SimpleStructType(NamespaceName.of(), "Y"); var nonRecursiveVariantType = new SimpleStructType(NamespaceName.of(), "Z"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, recursiveVariantType, recursiveVariantType), new SumVariant(1, nonRecursiveVariantType, nonRecursiveVariantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(recursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.option(sumType), NullSource.INSTANCE) ))); typesInfo.defineStructType(nonRecursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.option(NativeTypes.INT_64), NullSource.INSTANCE) ))); var variant = selectArbitraryVariant(sumType, typesInfo); assertThat(variant, equalTo(recursiveVariantType)); } @Test public void sharedIsRecursivelyConstructibleIffInnerTypeIsRecursivelyConstructible() { var sumType = new SumType(NamespaceName.of(), "X"); var recursiveVariantType = new SimpleStructType(NamespaceName.of(), "Y"); var nonRecursiveVariantType = new SimpleStructType(NamespaceName.of(), "Z"); var typesInfo = TypesInfoInMemory.empty(); typesInfo.defineSumType( sumType, List.of( new SumVariant(0, recursiveVariantType, recursiveVariantType), new SumVariant(1, nonRecursiveVariantType, nonRecursiveVariantType) ), List.of(), NullSource.INSTANCE ); typesInfo.defineStructType(recursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.shared(sumType), NullSource.INSTANCE) ))); typesInfo.defineStructType(nonRecursiveVariantType, Optional.of(List.of( new Field("x", NativeTypes.shared(NativeTypes.INT_64), NullSource.INSTANCE) ))); var variant = selectArbitraryVariant(sumType, typesInfo); assertThat(variant, equalTo(nonRecursiveVariantType)); } }