Skip to content

Commit

Permalink
fixes according to the review
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannesMeierSE committed Nov 4, 2024
1 parent 21aa91c commit 18a83e2
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 70 deletions.
8 changes: 4 additions & 4 deletions examples/lox/src/language/type-system/lox-type-checking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { AstNode, AstUtils, Module, assertUnreachable } from 'langium';
import { LangiumSharedServices } from 'langium/lsp';
import { ClassKind, CreateFieldDetails, CreateFunctionTypeDetails, FUNCTION_MISSING_NAME, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, OperatorManager, ParameterDetails, PrimitiveKind, TopKind, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation } from 'typir';
import { ClassKind, CreateFieldDetails, CreateFunctionTypeDetails, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, ParameterDetails, PrimitiveKind, TopKind, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation } from 'typir';
import { AbstractLangiumTypeCreator, LangiumServicesForTypirBinding, PartialTypirLangiumServices } from 'typir-langium';
import { ValidationMessageDetails } from '../../../../../packages/typir/lib/features/validation.js';
import { BinaryExpression, FunctionDeclaration, MemberCall, MethodMember, TypeReference, UnaryExpression, isBinaryExpression, isBooleanLiteral, isClass, isClassMember, isFieldMember, isForStatement, isFunctionDeclaration, isIfStatement, isMemberCall, isMethodMember, isNilLiteral, isNumberLiteral, isParameter, isPrintStatement, isReturnStatement, isStringLiteral, isTypeReference, isUnaryExpression, isVariableDeclaration, isWhileStatement } from '../generated/ast.js';
Expand Down Expand Up @@ -248,9 +248,9 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator {
? domainElement.element!.ref.name : 'N/A', // as an alternative, use 'InferenceRuleNotApplicable' instead, what should we recommend?
});

// TODO conversion 'nil' to classes ('anyClass')!
// TODO conversion 'nil' to classes ('TopClass')!
// any class !== all classes; here we want to say, that 'nil' is assignable to each concrete Class type!
// this.typir.conversion.markAsConvertible(typeNil, this.classKind.getOrCreateAnyClassType({}), 'IMPLICIT_EXPLICIT');
// this.typir.conversion.markAsConvertible(typeNil, this.classKind.getOrCreateTopClassType({}), 'IMPLICIT_EXPLICIT');
this.typir.conversion.markAsConvertible(this.primitiveKind.getPrimitiveType({ primitiveName: 'nil' })!, classType, 'IMPLICIT_EXPLICIT');
}
}
Expand All @@ -260,7 +260,7 @@ function createFunctionDetails(node: FunctionDeclaration | MethodMember): Create
const callableName = node.name;
return {
functionName: callableName,
outputParameter: { name: FUNCTION_MISSING_NAME, type: node.returnType },
outputParameter: { name: NO_PARAMETER_NAME, type: node.returnType },
inputParameters: node.parameters.map(p => (<ParameterDetails>{ name: p.name, type: p.type })),
// inference rule for function declaration:
inferenceRuleForDeclaration: (domainElement: unknown) => domainElement === node, // only the current function/method declaration matches!
Expand Down
3 changes: 2 additions & 1 deletion examples/lox/test/lox-type-checking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ describe('Test internal validation of Typir for cycles in the class inheritance
});

describe('LOX', () => {
test.fails('complete with difficult order of classes', async () => await validate(`
// this test case will work after having the support for cyclic type definitions, since it will solve also issues with topological order of type definitions
test.todo('complete with difficult order of classes', async () => await validate(`
class SuperClass {
a: number
}
Expand Down
4 changes: 2 additions & 2 deletions examples/ox/src/language/ox-type-checking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { AstNode, AstUtils, Module, assertUnreachable } from 'langium';
import { LangiumSharedServices } from 'langium/lsp';
import { FUNCTION_MISSING_NAME, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, OperatorManager, ParameterDetails, PrimitiveKind, TypirServices, UniqueFunctionValidation } from 'typir';
import { FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, ParameterDetails, PrimitiveKind, TypirServices, UniqueFunctionValidation } from 'typir';
import { AbstractLangiumTypeCreator, LangiumServicesForTypirBinding, PartialTypirLangiumServices } from 'typir-langium';
import { ValidationMessageDetails } from '../../../../packages/typir/lib/features/validation.js';
import { BinaryExpression, MemberCall, UnaryExpression, isAssignmentStatement, isBinaryExpression, isBooleanLiteral, isForStatement, isFunctionDeclaration, isIfStatement, isMemberCall, isNumberLiteral, isParameter, isReturnStatement, isTypeReference, isUnaryExpression, isVariableDeclaration, isWhileStatement } from './generated/ast.js';
Expand Down Expand Up @@ -174,7 +174,7 @@ export class OxTypeCreator extends AbstractLangiumTypeCreator {
this.functionKind.getOrCreateFunctionType({
functionName,
// note that the following two lines internally use type inference here in order to map language types to Typir types
outputParameter: { name: FUNCTION_MISSING_NAME, type: domainElement.returnType },
outputParameter: { name: NO_PARAMETER_NAME, type: domainElement.returnType },
inputParameters: domainElement.parameters.map(p => (<ParameterDetails>{ name: p.name, type: p.type })),
// inference rule for function declaration:
inferenceRuleForDeclaration: (node: unknown) => node === domainElement, // only the current function declaration matches!
Expand Down
3 changes: 2 additions & 1 deletion examples/ox/test/ox-type-checking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ describe('Explicitly test type checking for OX', () => {
`, 2); // both functions should be marked as "duplicate"
});

test.fails('function: the same function name twice (even in different files) is not allowed in Typir', async () => {
// TODO this test case needs to be investigated in more detail
test.todo('function: the same function name twice (even in different files) is not allowed in Typir', async () => {
await validate('fun myFunction() : boolean { return true; }', 0);
await validate('fun myFunction() : boolean { return false; }', 2); // now, both functions should be marked as "duplicate"
});
Expand Down
4 changes: 2 additions & 2 deletions packages/typir/src/features/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
******************************************************************************/

import { Type } from '../graph/type-node.js';
import { FUNCTION_MISSING_NAME, FunctionKind, FunctionKindName, isFunctionKind } from '../kinds/function-kind.js';
import { FunctionKind, FunctionKindName, isFunctionKind, NO_PARAMETER_NAME } from '../kinds/function-kind.js';
import { TypirServices } from '../typir.js';
import { NameTypePair, Types } from '../utils/utils-definitions.js';
import { toArray } from '../utils/utils.js';
Expand Down Expand Up @@ -156,7 +156,7 @@ export class DefaultOperatorManager implements OperatorManager {
// create the operator as type of kind 'function'
const newOperatorType = functionKind.createFunctionType({
functionName: typeDetails.name,
outputParameter: { name: FUNCTION_MISSING_NAME, type: typeDetails.outputType },
outputParameter: { name: NO_PARAMETER_NAME, type: typeDetails.outputType },
inputParameters: typeDetails.inputParameter,
inferenceRuleForDeclaration: undefined, // operators have no declaration in the code => no inference rule for the operator declaration!
inferenceRuleForCalls: typeDetails.inferenceRule // but infer the operator when the operator is called!
Expand Down
79 changes: 39 additions & 40 deletions packages/typir/src/kinds/class-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,14 +520,14 @@ export class ClassKind implements Kind {
return isFunctionKind(kind) ? kind : new FunctionKind(this.services);
}

getOrCreateAnyClassType(typeDetails: AnyClassTypeDetails): AnyClassType {
return this.getAnyClassKind().getOrCreateAnyClassType(typeDetails);
getOrCreateTopClassType(typeDetails: TopClassTypeDetails): TopClassType {
return this.getTopClassKind().getOrCreateTopClassType(typeDetails);
}

getAnyClassKind(): AnyClassKind {
// ensure, that Typir uses the predefined 'function' kind
const kind = this.services.kinds.get(AnyClassKindName);
return isAnyClassKind(kind) ? kind : new AnyClassKind(this.services);
getTopClassKind(): TopClassKind {
// ensure, that Typir uses the predefined 'TopClass' kind
const kind = this.services.kinds.get(TopClassKindName);
return isTopClassKind(kind) ? kind : new TopClassKind(this.services);
}

}
Expand Down Expand Up @@ -683,11 +683,10 @@ export class UniqueMethodValidation<T> implements ValidationRuleWithBeforeAfter
}


// TODO for the review: which name is better? AnyClassType vs TopClassType?
export class AnyClassType extends Type {
override readonly kind: AnyClassKind;
export class TopClassType extends Type {
override readonly kind: TopClassKind;

constructor(kind: AnyClassKind, identifier: string) {
constructor(kind: TopClassKind, identifier: string) {
super(identifier);
this.kind = kind;
}
Expand All @@ -701,7 +700,7 @@ export class AnyClassType extends Type {
}

override analyzeTypeEqualityProblems(otherType: Type): TypirProblem[] {
if (isAnyClassType(otherType)) {
if (isTopClassType(otherType)) {
return [];
} else {
return [<TypeEqualityProblem>{
Expand All @@ -714,8 +713,8 @@ export class AnyClassType extends Type {
}

override analyzeIsSubTypeOf(superType: Type): TypirProblem[] {
if (isAnyClassType(superType)) {
// special case by definition: AnyClassType is sub-type of AnyClassType
if (isTopClassType(superType)) {
// special case by definition: TopClassType is sub-type of TopClassType
return [];
} else {
return [<SubTypeProblem>{
Expand All @@ -728,7 +727,7 @@ export class AnyClassType extends Type {
}

override analyzeIsSuperTypeOf(subType: Type): TypirProblem[] {
// an AnyClassType is the super type of all ClassTypes!
// an TopClassType is the super type of all ClassTypes!
if (isClassType(subType)) {
return [];
} else {
Expand All @@ -743,64 +742,64 @@ export class AnyClassType extends Type {

}

export function isAnyClassType(type: unknown): type is AnyClassType {
return isType(type) && isAnyClassKind(type.kind);
export function isTopClassType(type: unknown): type is TopClassType {
return isType(type) && isTopClassKind(type.kind);
}


export interface AnyClassTypeDetails {
inferenceRules?: InferAnyClassType | InferAnyClassType[]
export interface TopClassTypeDetails {
inferenceRules?: InferTopClassType | InferTopClassType[]
}

export type InferAnyClassType = (domainElement: unknown) => boolean;
export type InferTopClassType = (domainElement: unknown) => boolean;

export interface AnyClassKindOptions {
export interface TopClassKindOptions {
name: string;
}

export const AnyClassKindName = 'AnyClassKind';
export const TopClassKindName = 'TopClassKind';

export class AnyClassKind implements Kind {
readonly $name: 'AnyClassKind';
export class TopClassKind implements Kind {
readonly $name: 'TopClassKind';
readonly services: TypirServices;
readonly options: AnyClassKindOptions;
protected instance: AnyClassType | undefined;
readonly options: TopClassKindOptions;
protected instance: TopClassType | undefined;

constructor(services: TypirServices, options?: Partial<AnyClassKindOptions>) {
this.$name = AnyClassKindName;
constructor(services: TypirServices, options?: Partial<TopClassKindOptions>) {
this.$name = TopClassKindName;
this.services = services;
this.services.kinds.register(this);
this.options = {
// the default values:
name: 'AnyClass',
name: 'TopClass',
// the actually overriden values:
...options
};
}

getAnyClassType(typeDetails: AnyClassTypeDetails): AnyClassType | undefined {
getTopClassType(typeDetails: TopClassTypeDetails): TopClassType | undefined {
const key = this.calculateIdentifier(typeDetails);
return this.services.graph.getType(key) as AnyClassType;
return this.services.graph.getType(key) as TopClassType;
}

getOrCreateAnyClassType(typeDetails: AnyClassTypeDetails): AnyClassType {
const topType = this.getAnyClassType(typeDetails);
getOrCreateTopClassType(typeDetails: TopClassTypeDetails): TopClassType {
const topType = this.getTopClassType(typeDetails);
if (topType) {
this.registerInferenceRules(typeDetails, topType);
return topType;
}
return this.createAnyClassType(typeDetails);
return this.createTopClassType(typeDetails);
}

createAnyClassType(typeDetails: AnyClassTypeDetails): AnyClassType {
assertTrue(this.getAnyClassType(typeDetails) === undefined);
createTopClassType(typeDetails: TopClassTypeDetails): TopClassType {
assertTrue(this.getTopClassType(typeDetails) === undefined);

// create the top type (singleton)
if (this.instance) {
// note, that the given inference rules are ignored in this case!
return this.instance;
}
const topType = new AnyClassType(this, this.calculateIdentifier(typeDetails));
const topType = new TopClassType(this, this.calculateIdentifier(typeDetails));
this.instance = topType;
this.services.graph.addNode(topType);

Expand All @@ -810,7 +809,7 @@ export class AnyClassKind implements Kind {
}

/** Register all inference rules for primitives within a single generic inference rule (in order to keep the number of "global" inference rules small). */
protected registerInferenceRules(typeDetails: AnyClassTypeDetails, topType: AnyClassType) {
protected registerInferenceRules(typeDetails: TopClassTypeDetails, topType: TopClassType) {
const rules = toArray(typeDetails.inferenceRules);
if (rules.length >= 1) {
this.services.inference.addInferenceRule((domainElement, _typir) => {
Expand All @@ -824,12 +823,12 @@ export class AnyClassKind implements Kind {
}
}

calculateIdentifier(_typeDetails: AnyClassTypeDetails): string {
calculateIdentifier(_typeDetails: TopClassTypeDetails): string {
return this.options.name;
}

}

export function isAnyClassKind(kind: unknown): kind is AnyClassKind {
return isKind(kind) && kind.$name === AnyClassKindName;
export function isTopClassKind(kind: unknown): kind is TopClassKind {
return isKind(kind) && kind.$name === TopClassKindName;
}
Loading

0 comments on commit 18a83e2

Please sign in to comment.