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

fix: assignability of static type to callable type #725

Merged
merged 1 commit into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getContainerOfType } from 'langium';
import type { SafeDsClasses } from '../builtins/safe-ds-classes.js';
import { isSdsEnum, SdsDeclaration } from '../generated/ast.js';
import { isSdsEnum, type SdsAbstractResult, SdsDeclaration } from '../generated/ast.js';
import { getParameters } from '../helpers/nodeProperties.js';
import {
BooleanConstant,
Constant,
Expand All @@ -16,6 +17,7 @@ import {
EnumType,
EnumVariantType,
LiteralType,
NamedTupleEntry,
NamedTupleType,
StaticType,
Type,
Expand All @@ -24,16 +26,19 @@ import {
} from './model.js';
import { SafeDsClassHierarchy } from './safe-ds-class-hierarchy.js';
import { SafeDsCoreTypes } from './safe-ds-core-types.js';
import type { SafeDsTypeComputer } from './safe-ds-type-computer.js';

export class SafeDsTypeChecker {
private readonly builtinClasses: SafeDsClasses;
private readonly classHierarchy: SafeDsClassHierarchy;
private readonly coreTypes: SafeDsCoreTypes;
private readonly typeComputer: () => SafeDsTypeComputer;

constructor(services: SafeDsServices) {
this.builtinClasses = services.builtins.Classes;
this.classHierarchy = services.types.ClassHierarchy;
this.coreTypes = services.types.CoreTypes;
this.typeComputer = () => services.types.TypeComputer;
}

/**
Expand Down Expand Up @@ -218,8 +223,48 @@ export class SafeDsTypeChecker {
}
}

private staticTypeIsAssignableTo(type: Type, other: Type): boolean {
return type.equals(other);
private staticTypeIsAssignableTo(type: StaticType, other: Type): boolean {
if (other instanceof CallableType) {
return this.isAssignableTo(this.associatedCallableTypeForStaticType(type), other);
} else {
return type.equals(other);
}
}

private associatedCallableTypeForStaticType(type: StaticType): Type {
const instanceType = type.instanceType;
if (instanceType instanceof ClassType) {
const declaration = instanceType.declaration;
if (!declaration.parameterList) {
return UnknownType;
}

const parameterEntries = new NamedTupleType(
...getParameters(declaration).map(
(it) => new NamedTupleEntry(it, it.name, this.typeComputer().computeType(it)),
),
);
const resultEntries = new NamedTupleType(
new NamedTupleEntry<SdsAbstractResult>(undefined, 'instance', instanceType),
);

return new CallableType(declaration, parameterEntries, resultEntries);
} else if (instanceType instanceof EnumVariantType) {
const declaration = instanceType.declaration;

const parameterEntries = new NamedTupleType(
...getParameters(declaration).map(
(it) => new NamedTupleEntry(it, it.name, this.typeComputer().computeType(it)),
),
);
const resultEntries = new NamedTupleType(
new NamedTupleEntry<SdsAbstractResult>(undefined, 'instance', instanceType),
);

return new CallableType(declaration, parameterEntries, resultEntries);
} else {
return UnknownType;
}
}

private unionTypeIsAssignableTo(type: UnionType, other: Type): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
} from '../../../src/language/partialEvaluation/model.js';
import {
ClassType,
EnumType,
EnumVariantType,
LiteralType,
NamedTupleEntry,
NamedTupleType,
Expand All @@ -45,13 +47,15 @@ const code = `
fun func8() -> (s: Int)
fun func9() -> (r: Any)
fun func10() -> (r: String)
fun func11() -> (r: Class1)
fun func12() -> (r: Enum1)

class Class1
class Class2 sub Class1
class Class1(p: Int)
class Class2() sub Class1
class Class3

enum Enum1 {
Variant1
Variant1(p: Int)
Variant2
}
enum Enum2
Expand All @@ -68,6 +72,8 @@ const callableType7 = typeComputer.computeType(functions[6]);
const callableType8 = typeComputer.computeType(functions[7]);
const callableType9 = typeComputer.computeType(functions[8]);
const callableType10 = typeComputer.computeType(functions[9]);
const callableType11 = typeComputer.computeType(functions[10]);
const callableType12 = typeComputer.computeType(functions[11]);

const classes = getModuleMembers(module).filter(isSdsClass);
const class1 = classes[0];
Expand All @@ -80,14 +86,14 @@ const classType3 = typeComputer.computeType(class3) as ClassType;
const enums = getModuleMembers(module).filter(isSdsEnum);
const enum1 = enums[0];
const enum2 = enums[1];
const enumType1 = typeComputer.computeType(enum1);
const enumType2 = typeComputer.computeType(enum2);
const enumType1 = typeComputer.computeType(enum1) as EnumType;
const enumType2 = typeComputer.computeType(enum2) as EnumType;

const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray();
const enumVariant1 = enumVariants[0];
const enumVariant2 = enumVariants[1];
const enumVariantType1 = typeComputer.computeType(enumVariant1);
const enumVariantType2 = typeComputer.computeType(enumVariant2);
const enumVariantType1 = typeComputer.computeType(enumVariant1) as EnumVariantType;
const enumVariantType2 = typeComputer.computeType(enumVariant2) as EnumVariantType;

describe('SafeDsTypeChecker', async () => {
const testCases: IsAssignableToTest[] = [
Expand Down Expand Up @@ -521,6 +527,47 @@ describe('SafeDsTypeChecker', async () => {
type2: enumType1,
expected: false,
},
// Static type to callable type
{
type1: new StaticType(classType1),
type2: callableType1,
expected: false,
},
{
type1: new StaticType(classType1),
type2: callableType3,
expected: true,
},
{
type1: new StaticType(classType2),
type2: callableType11,
expected: true,
},
{
type1: new StaticType(classType3),
type2: callableType1,
expected: false,
},
{
type1: new StaticType(enumType1),
type2: callableType1,
expected: false,
},
{
type1: new StaticType(enumVariantType1),
type2: callableType1,
expected: false,
},
{
type1: new StaticType(enumVariantType1),
type2: callableType3,
expected: true,
},
{
type1: new StaticType(enumVariantType2),
type2: callableType12,
expected: true,
},
// Static type to static type
{
type1: new StaticType(classType1),
Expand Down