From 5613f3417aa766041fe1a91d27861f08d0facba9 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 10 Aug 2026 17:39:13 +0100 Subject: Fix creation of arbitrary constructed struct values --- .../compiler/analysis/ArbitraryValueAnalysis.java | 121 +++++++++++++++++++++ .../generators/javatypes/JavaTypesGenerator.java | 81 +++++++------- 2 files changed, 163 insertions(+), 39 deletions(-) (limited to 'src/main') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/analysis/ArbitraryValueAnalysis.java b/src/main/java/org/zwobble/hobgoblin/compiler/analysis/ArbitraryValueAnalysis.java index 39d9e3f..b803b3d 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/analysis/ArbitraryValueAnalysis.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/analysis/ArbitraryValueAnalysis.java @@ -9,8 +9,129 @@ import org.zwobble.hobgoblin.compiler.types.*; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.IntStream; public class ArbitraryValueAnalysis { + public sealed interface ArbitraryValue { + void collectTypeParams(Set typeParams); + + record NativeFunctionCall(SimpleNativeType type) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + } + } + + record ConstructedStructBuilder( + ConstructedStructType type, + List args + ) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + for (var arg : args) { + arg.collectTypeParams(typeParams); + } + } + } + + record SimpleStructBuilder(SimpleStructType type) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + } + } + + record EnumFirstVariant(EnumType type) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + } + } + + record TypeParamThunk(TypeParam typeParam) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + typeParams.add(typeParam); + } + } + + record Box(ArbitraryValue value) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + value.collectTypeParams(typeParams); + } + } + + record Shared(ArbitraryValue value) implements ArbitraryValue { + @Override + public void collectTypeParams(Set typeParams) { + value.collectTypeParams(typeParams); + } + } + } + + public static ArbitraryValue arbitraryValue(Type type, TypesInfo typesInfo) { + // TODO: unify logic for constructed types? + return switch (type) { + case ConstructedNativeType constructedNativeType -> { + if (constructedNativeType.constructor().equals(NativeTypes.BOX)) { + yield new ArbitraryValue.Box(arbitraryValue(constructedNativeType.args().getFirst(), typesInfo)); + } + + if (constructedNativeType.constructor().equals(NativeTypes.SHARED)) { + yield new ArbitraryValue.Shared(arbitraryValue(constructedNativeType.args().getFirst(), typesInfo)); + } + + yield new ArbitraryValue.NativeFunctionCall(constructedNativeType.constructor().genericType()); + } + + case ConstructedStructType constructedStructType -> { + var fields = typesInfo.fieldsOf(constructedStructType.constructor().genericType()) + // TODO: better error + .orElseThrow(); + var usedTypeParams = new HashSet(); + for (var field : fields) { + arbitraryValue(field.type(), typesInfo).collectTypeParams(usedTypeParams); + } + + var allTypeParams = constructedStructType.constructor().params(); + var args = IntStream.range(0, constructedStructType.args().size()) + .filter(paramIndex -> usedTypeParams.contains(allTypeParams.get(paramIndex))) + .mapToObj(paramIndex -> arbitraryValue(constructedStructType.args().get(paramIndex), typesInfo)) + .toList(); + + yield new ArbitraryValue.ConstructedStructBuilder( + constructedStructType, + args + ); + } + + case EnumType enumType -> + new ArbitraryValue.EnumFirstVariant(enumType); + + case SimpleNativeType simpleNativeType -> + new ArbitraryValue.NativeFunctionCall(simpleNativeType); + + case SimpleStructType simpleStructType -> + new ArbitraryValue.SimpleStructBuilder(simpleStructType); + + case SumType sumType -> { + var firstVariant = selectArbitraryVariant(sumType, typesInfo); + yield arbitraryValue(firstVariant, typesInfo); + } + + case TypeLevelValueType _ -> + throw new UnsupportedOperationException("TODO"); + + case TypeParam typeParam -> + new ArbitraryValue.TypeParamThunk(typeParam); + }; + } + + private static void collectUsedTypeParams( + ConstructedStructType structType, + Set usedTypeParams + ) { + + } + public static Type selectArbitraryVariant(SumType sumType, TypesInfo typesInfo) { var sumTypeInfo = typesInfo.sumTypeInfo(sumType); if (sumTypeInfo.variants().isEmpty()) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java index 93047d3..1fe01c9 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java @@ -1,5 +1,6 @@ package org.zwobble.hobgoblin.compiler.output.generators.javatypes; +import org.zwobble.hobgoblin.compiler.analysis.ArbitraryValueAnalysis; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.ast.typed.*; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; @@ -16,9 +17,11 @@ import org.zwobble.json5.reader.Json5ObjectReader; import java.io.IOException; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; -import static org.zwobble.hobgoblin.compiler.analysis.ArbitraryValueAnalysis.selectArbitraryVariant; import static org.zwobble.hobgoblin.compiler.util.Casing.lowerCamelCaseToUpperCamelCase; public class JavaTypesGenerator implements Generator { @@ -188,7 +191,16 @@ public class JavaTypesGenerator implements Generator { var arbitraryValueExpression = new JavaNewExpression( builderJavaTypeRef, structDefinition.fields().orElse(List.of()).stream() - .map(field -> arbitraryValue(field.type(), typeParamsUsedInArbitraryValue, context)) + .map(field -> { + var arbitraryValue = ArbitraryValueAnalysis.arbitraryValue( + field.type().value(), + context.typesInfo + ); + + arbitraryValue.collectTypeParams(typeParamsUsedInArbitraryValue); + + return abstractArbitraryValueToJava(arbitraryValue, context); + }) .toList() ); @@ -632,36 +644,24 @@ public class JavaTypesGenerator implements Generator { return JavaTypeRef.inner(javaPackageName, generateTypeName(name), BUILDER_TYPE_NAME); } - private JavaExpression arbitraryValue( - TypedTypeLevelExpressionNode type, - Set typeParamsUsedInArbitraryValue, + private JavaExpression abstractArbitraryValueToJava( + ArbitraryValueAnalysis.ArbitraryValue arbitraryValue, Context context ) { - return arbitraryValue(type.value(), typeParamsUsedInArbitraryValue, context); - } - - private JavaExpression arbitraryValue( - Type type, - Set typeParamsUsedInArbitraryValue, - Context context - ) { - // TODO: unify logic for constructed types. - return switch (this.javaGenerator.collapseType(type)) { - case ConstructedNativeType constructedType -> { - yield arbitraryValue(constructedType.constructor().genericType(), typeParamsUsedInArbitraryValue, context); - } - - case ConstructedStructType constructedType -> { + return switch (arbitraryValue) { + case ArbitraryValueAnalysis.ArbitraryValue.ConstructedStructBuilder constructedStructBuilder -> { yield new JavaMethodCall( new JavaStaticMethodCall( - generateTypeRef(constructedType.constructor().genericType(), context), - List.of(), + generateTypeRef(constructedStructBuilder.type().constructor().genericType(), context), + constructedStructBuilder.type().args().stream() + .map(typeArg -> this.javaGenerator.generateTypeRef(typeArg)) + .toList(), ARBITRARY_METHOD_NAME, - constructedType.args().stream() - .map(typeArg -> new JavaLambdaExpression( + constructedStructBuilder.args().stream() + .map(arg -> new JavaLambdaExpression( List.of(), new JavaBlock(List.of( - new JavaReturn(arbitraryValue(typeArg, typeParamsUsedInArbitraryValue, context)) + new JavaReturn(abstractArbitraryValueToJava(arg, context)) )) )) .toList() @@ -671,14 +671,18 @@ public class JavaTypesGenerator implements Generator { ); } - case EnumType enumType -> { + case ArbitraryValueAnalysis.ArbitraryValue.EnumFirstVariant enumFirstVariant -> { + var enumType = enumFirstVariant.type(); + yield new JavaStaticFieldAccess( generateTypeRef(enumType, context), generateEnumConstantName(context.enumVariants(enumType).getFirst().name()) ); } - case SimpleNativeType simpleNativeType -> { + case ArbitraryValueAnalysis.ArbitraryValue.NativeFunctionCall nativeFunctionCall -> { + var simpleNativeType = nativeFunctionCall.type(); + yield nativeTypeConfig(simpleNativeType) .flatMap(nativeTypeConfig -> nativeTypeConfig.arbitraryValue()) .orElseGet(() -> { @@ -693,7 +697,9 @@ public class JavaTypesGenerator implements Generator { }); } - case SimpleStructType structType -> { + case ArbitraryValueAnalysis.ArbitraryValue.SimpleStructBuilder simpleStructBuilder -> { + var structType = simpleStructBuilder.type(); + yield new JavaMethodCall( new JavaStaticMethodCall( generateTypeRef(structType, context), @@ -706,23 +712,20 @@ public class JavaTypesGenerator implements Generator { ); } - case SumType sumType -> { - var firstVariant = selectArbitraryVariant(sumType, context.typesInfo); - yield arbitraryValue(firstVariant, typeParamsUsedInArbitraryValue, context); - } - - case TypeLevelValueType typeLevelValueType -> { - throw new UnsupportedOperationException("TODO"); - } + case ArbitraryValueAnalysis.ArbitraryValue.TypeParamThunk typeParamThunk -> { + var typeParam = typeParamThunk.typeParam(); - case TypeParam typeParam -> { - typeParamsUsedInArbitraryValue.add(typeParam); yield new JavaMethodCall( new JavaRef(arbitraryValueSupplier(typeParam)), JavaIdentifier.of("get"), List.of() ); } + case ArbitraryValueAnalysis.ArbitraryValue.Box box -> + abstractArbitraryValueToJava(box.value(), context); + + case ArbitraryValueAnalysis.ArbitraryValue.Shared shared -> + abstractArbitraryValueToJava(shared.value(), context); }; } -- cgit v1.2.3