diff options
Diffstat (limited to 'src/main')
5 files changed, 112 insertions, 15 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/SubtypeIsMissingFieldError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/SubtypeIsMissingFieldError.java new file mode 100644 index 0000000..ce59593 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/SubtypeIsMissingFieldError.java @@ -0,0 +1,38 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.errors.SourceError; +import org.zwobble.hobgoblin.compiler.sources.Source; +import org.zwobble.hobgoblin.compiler.types.Type; + +public class SubtypeIsMissingFieldError extends SourceError { + private final Type supertype; + private final Type subtype; + private final String fieldName; + + public SubtypeIsMissingFieldError(Type supertype, Type subtype, String fieldName, Source source) { + super( + String.format( + "%s is a subtype of %s, but is missing field %s", + subtype.describe(), + supertype.describe(), + fieldName + ), + source + ); + this.supertype = supertype; + this.subtype = subtype; + this.fieldName = fieldName; + } + + public Type supertype() { + return supertype; + } + + public Type subtype() { + return subtype; + } + + public String fieldName() { + return fieldName; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java index c291aaf..c6f8721 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.typechecker; import org.zwobble.hobgoblin.compiler.ast.typed.*; import org.zwobble.hobgoblin.compiler.ast.untyped.*; +import org.zwobble.hobgoblin.compiler.sources.NullSource; import org.zwobble.hobgoblin.compiler.sources.Source; import org.zwobble.hobgoblin.compiler.types.*; @@ -183,6 +184,17 @@ public class TypeChecker { context.defineStructType(structType, typeCheckedFieldDefinitions.fields()); + for (var sumType : context.variantOf(structType)) { + // TODO: better error if sum type fields aren't defined + var sumTypeFields = context.fieldsOf(sumType).orElseThrow(); + typeCheckVariantType( + sumType, + sumTypeFields, + structType, + typeCheckedFieldDefinitions.fields + ); + } + return new TypedStructDefinitionNode( structType, typeCheckedFieldDefinitions.typedFieldNodes(), @@ -233,8 +245,7 @@ public class TypeChecker { var sumType = (SumType) lookupMetaType(untyped.name(), untyped.source(), context); var typedVariants = new ArrayList<TypedSumVariantDefinitionNode>(); - // TODO: should be StructType? - var variantTypes = new ArrayList<Type>(); + var variantTypes = new ArrayList<StructType>(); for (var untypedVariant : untyped.variants()) { var variantType = typeCheckMetaType(untypedVariant.type(), context); var typedVariant = new TypedSumVariantDefinitionNode( @@ -242,12 +253,25 @@ public class TypeChecker { untyped.source() ); typedVariants.add(typedVariant); - variantTypes.add(variantType.value()); + // TODO: handle not struct type + variantTypes.add((StructType) variantType.value()); } var typeCheckedFieldDefinitions = typeCheckFieldDefinitions(untyped.fields(), context); - context.defineSumType(sumType, variantTypes); + context.defineSumType(sumType, variantTypes, typeCheckedFieldDefinitions.fields); + + for (var variantType : variantTypes) { + var variantTypeFields = context.fieldsOf(variantType); + if (variantTypeFields.isPresent()) { + typeCheckVariantType( + sumType, + typeCheckedFieldDefinitions.fields, + variantType, + variantTypeFields.get() + ); + } + } return new TypedSumDefinitionNode( sumType, @@ -274,6 +298,24 @@ public class TypeChecker { } } + private static void typeCheckVariantType( + SumType sumType, + List<Field> sumTypeFields, + StructType variantType, + List<Field> variantTypeFields + ) { + for (var sumTypeField : sumTypeFields) { + var variantTypeField = variantTypeFields.stream() + .filter(field -> field.name().equals(sumTypeField.name())) + .findFirst(); + + if (variantTypeField.isEmpty()) { + // TODO: set source appropriately. + throw new SubtypeIsMissingFieldError(sumType, variantType, sumTypeField.name(), NullSource.INSTANCE); + } + } + } + static TypedTypeLevelExpressionNode<? extends TypeLevelValue> typeCheckTypeLevelExpression( UntypedTypeLevelExpressionNode untyped, TypeCheckerNamespaceContext context diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java index 3e7665c..ca2d731 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java @@ -8,7 +8,7 @@ import java.util.*; public class TypeCheckerGlobalContext { private final Map<String, Type> nativeTypes = new HashMap<>(); private final Map<EnumType, List<EnumVariant>> enumVariants = new HashMap<>(); - private final Map<StructType, List<Field>> structFieldsOf = new HashMap<>(); + private final Map<Type, List<Field>> fieldsOf = new HashMap<>(); private final Map<NamespaceName, Fields> namespaceFieldsOf = new HashMap<>(); private final ManyToMany<SumType, Type> sumVariants = new ManyToMany<>(); @@ -22,7 +22,7 @@ public class TypeCheckerGlobalContext { public TypesInfo toTypesInfo() { return new TypesInfo( this.enumVariants, - this.structFieldsOf, + this.fieldsOf, this.sumVariants ); } @@ -40,7 +40,7 @@ public class TypeCheckerGlobalContext { namespaceName, this.nativeTypes, this.enumVariants, - this.structFieldsOf, + this.fieldsOf, this.sumVariants ); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java index 906cdfa..0df7e16 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java @@ -13,7 +13,7 @@ public class TypeCheckerNamespaceContext { NamespaceName namespaceName, Map<String, Type> nativeTypes, Map<EnumType, List<EnumVariant>> enumVariants, - Map<StructType, List<Field>> fieldsOf, + Map<Type, List<Field>> fieldsOf, ManyToMany<SumType, Type> variants ) { return new TypeCheckerNamespaceContext( @@ -28,14 +28,14 @@ public class TypeCheckerNamespaceContext { private final NamespaceName namespaceName; private final Map<String, Type> variables; private final Map<EnumType, List<EnumVariant>> enumVariants; - private final Map<StructType, List<Field>> fieldsOf; + private final Map<Type, List<Field>> fieldsOf; private final ManyToMany<SumType, Type> sumVariants; private TypeCheckerNamespaceContext( NamespaceName namespaceName, Map<String, Type> variables, Map<EnumType, List<EnumVariant>> enumVariants, - Map<StructType, List<Field>> fieldsOf, + Map<Type, List<Field>> fieldsOf, ManyToMany<SumType, Type> sumVariants ) { this.namespaceName = namespaceName; @@ -66,9 +66,26 @@ public class TypeCheckerNamespaceContext { this.fieldsOf.put(structType, fields); } - public void defineSumType(SumType sumType, List<Type> variantTypes) { + public Optional<List<Field>> fieldsOf(StructType type) { + return Optional.ofNullable(this.fieldsOf.get(type)); + } + + public void defineSumType( + SumType sumType, + List<StructType> variantTypes, + List<Field> fields + ) { for (var variantType : variantTypes) { this.sumVariants.add(sumType, variantType); } + this.fieldsOf.put(sumType, fields); + } + + public List<SumType> variantOf(StructType variantType) { + return this.sumVariants.rightToLeft(variantType); + } + + public Optional<List<Field>> fieldsOf(SumType type) { + return Optional.ofNullable(this.fieldsOf.get(type)); } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java index 0fb4f81..63d21b6 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java @@ -9,16 +9,16 @@ import java.util.Optional; public class TypesInfo { private final Map<EnumType, List<EnumVariant>> enumVariants; - private final Map<StructType, List<Field>> structFieldsOf; + private final Map<Type, List<Field>> fieldsOf; private final ManyToMany<SumType, Type> sumVariants; public TypesInfo( Map<EnumType, List<EnumVariant>> enumVariants, - Map<StructType, List<Field>> structFieldsOf, + Map<Type, List<Field>> fieldsOf, ManyToMany<SumType, Type> sumVariants ) { this.enumVariants = enumVariants; - this.structFieldsOf = structFieldsOf; + this.fieldsOf = fieldsOf; this.sumVariants = sumVariants; } @@ -28,7 +28,7 @@ public class TypesInfo { public List<Field> fieldsOf(StructType structType) { // TODO: handle error better - return Optional.ofNullable(this.structFieldsOf.get(structType)).orElseThrow(); + return Optional.ofNullable(this.fieldsOf.get(structType)).orElseThrow(); } public List<SumType> variantOf(StructType variantType) { |
