Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: type checker service #722

Merged
merged 9 commits into from
Nov 4, 2023
Prev Previous commit
Next Next commit
feat: type checker for enum & enum variant types
  • Loading branch information
lars-reimann committed Nov 4, 2023
commit 8eeecb13d829c3a9ca55e6f6174aef763bd93bb0
34 changes: 13 additions & 21 deletions packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getContainerOfType } from 'langium';
import type { SafeDsClasses } from '../builtins/safe-ds-classes.js';
import { isSdsEnum, SdsDeclaration } from '../generated/ast.js';
import {
BooleanConstant,
Expand All @@ -25,10 +26,12 @@ import { SafeDsClassHierarchy } from './safe-ds-class-hierarchy.js';
import { SafeDsCoreTypes } from './safe-ds-core-types.js';

export class SafeDsTypeChecker {
private readonly builtinClasses: SafeDsClasses;
private readonly classHierarchy: SafeDsClassHierarchy;
private readonly coreTypes: SafeDsCoreTypes;

constructor(services: SafeDsServices) {
this.builtinClasses = services.builtins.Classes;
this.classHierarchy = services.types.ClassHierarchy;
this.coreTypes = services.types.CoreTypes;
}
Expand Down Expand Up @@ -119,41 +122,30 @@ export class SafeDsTypeChecker {
return false;
}

if (other instanceof EnumType) {
if (other instanceof ClassType) {
return other.declaration === this.builtinClasses.Any;
} else if (other instanceof EnumType) {
return type.declaration === other.declaration;
} else {
return false;
}

// return when (val unwrappedOther = unwrapVariadicType(other)) {
// is ClassType -> {
// (!this.isNullable || unwrappedOther.isNullable) &&
// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any
// }
// else -> false
// }

return type.equals(other);
}

private enumVariantTypeIsAssignableTo(type: EnumVariantType, other: Type): boolean {
if (type.isNullable && !other.isNullable) {
return false;
}

if (other instanceof EnumType) {
if (other instanceof ClassType) {
return other.declaration === this.builtinClasses.Any;
} else if (other instanceof EnumType) {
const containingEnum = getContainerOfType(type.declaration, isSdsEnum);
return containingEnum === other.declaration;
} else if (other instanceof EnumVariantType) {
return type.declaration === other.declaration;
} else {
return false;
}
// return when (val unwrappedOther = unwrapVariadicType(other)) {
// is ClassType -> {
// (!this.isNullable || unwrappedOther.isNullable) &&
// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any
// }
// else -> false
// }

return type.equals(other);
}

private literalTypeIsAssignableTo(type: LiteralType, other: Type): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ const code = `
Variant1
Variant2
}
enum Enum2 {
Variant3
}
enum Enum2
`;
const module = await getNodeOfType(services, code, isSdsModule);
const func = getModuleMembers(module).find(isSdsFunction)!;
Expand All @@ -55,10 +53,8 @@ const enumType2 = typeComputer.computeType(enum2);
const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray();
const enumVariant1 = enumVariants[0];
const enumVariant2 = enumVariants[1];
const enumVariant3 = enumVariants[2];
const enumVariantType1 = typeComputer.computeType(enumVariant1);
const enumVariantType2 = typeComputer.computeType(enumVariant2);
const enumVariantType3 = typeComputer.computeType(enumVariant3);

describe('SafeDsTypeChecker', async () => {
const testCases: IsAssignableToTest[] = [
Expand Down Expand Up @@ -112,10 +108,145 @@ describe('SafeDsTypeChecker', async () => {
type2: enumType1,
expected: false,
},
// Enum type to X
// TODO
// Enum variant type to X
// TODO
// Enum type to class type
{
type1: enumType1,
type2: classType1,
expected: false,
},
{
type1: enumType1,
type2: coreTypes.Any,
expected: true,
},
{
type1: enumType1.updateNullability(true),
type2: coreTypes.Any,
expected: false,
},
{
type1: enumType1.updateNullability(true),
type2: coreTypes.AnyOrNull,
expected: true,
},
// Enum type to enum type
{
type1: enumType1,
type2: enumType1,
expected: true,
},
{
type1: enumType1,
type2: enumType2,
expected: false,
},
{
type1: enumType1.updateNullability(true),
type2: enumType1,
expected: false,
},
{
type1: enumType1.updateNullability(true),
type2: enumType1.updateNullability(true),
expected: true,
},
// Enum type to union type
{
type1: enumType1,
type2: new UnionType(enumType1),
expected: true,
},
{
type1: enumType1,
type2: new UnionType(enumType2),
expected: false,
},
// Enum type to other
{
type1: enumType1,
type2: new LiteralType(),
expected: false,
},
// Enum variant type to class type
{
type1: enumVariantType1,
type2: classType1,
expected: false,
},
{
type1: enumVariantType1,
type2: coreTypes.Any,
expected: true,
},
{
type1: enumVariantType1.updateNullability(true),
type2: coreTypes.Any,
expected: false,
},
{
type1: enumVariantType1.updateNullability(true),
type2: coreTypes.AnyOrNull,
expected: true,
},
// Enum variant type to enum type
{
type1: enumVariantType1,
type2: enumType1,
expected: true,
},
{
type1: enumVariantType1,
type2: enumType2,
expected: false,
},
{
type1: enumVariantType1.updateNullability(true),
type2: enumType1,
expected: false,
},
{
type1: enumVariantType1.updateNullability(true),
type2: enumType1.updateNullability(true),
expected: true,
},
// Enum variant type to enum variant type
{
type1: enumVariantType1,
type2: enumVariantType1,
expected: true,
},
{
type1: enumVariantType1,
type2: enumVariantType2,
expected: false,
},
{
type1: enumVariantType1.updateNullability(true),
type2: enumVariantType1,
expected: false,
},
{
type1: enumVariantType1.updateNullability(true),
type2: enumVariantType1.updateNullability(true),
expected: true,
},
// Enum variant type to union type
{
type1: enumVariantType1,
type2: new UnionType(enumType1),
expected: true,
},
{
type1: enumVariantType1,
type2: new UnionType(enumType2),
expected: false,
},
// Enum variant type to other
{
type1: enumVariantType1,
type2: new LiteralType(),
expected: false,
},
// Literal type to class type
{
type1: new LiteralType(),
Expand Down