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

Cyclic type definitions #33

Merged
merged 19 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a2bb2ee
first working version of type definitions in "wrong" order for classe…
JohannesMeierSE Nov 12, 2024
bf1eb78
listener for new inference rules, fixed several bugs
JohannesMeierSE Nov 12, 2024
36708f0
fixed important bug
JohannesMeierSE Nov 18, 2024
c1f02ec
refactorings, improved comments
JohannesMeierSE Nov 18, 2024
0c933fa
validation (instead of exception) for super-sub-class cycles
JohannesMeierSE Nov 19, 2024
ddda324
propagate cyclic types to the direct children
JohannesMeierSE Nov 19, 2024
6c29f57
propagate types to ignore recursively to indirect dependencies as well
JohannesMeierSE Nov 19, 2024
40bea25
added more expected error messages into existing test cases
JohannesMeierSE Nov 19, 2024
d4b1cde
simplified code, improved comments, more test cases, renamings, some …
JohannesMeierSE Nov 20, 2024
005c37f
fixed registration of inference rules of classes, new deconstruct log…
JohannesMeierSE Nov 20, 2024
072de08
more test cases for cyclic classes
JohannesMeierSE Nov 20, 2024
704d81c
small improvements for the lifecycle of types, renamings
JohannesMeierSE Nov 21, 2024
a07614b
realized cyclic types with Functions involved, slightly reworked the …
JohannesMeierSE Nov 21, 2024
14b21d5
fix for unique method validation, more test cases
JohannesMeierSE Nov 22, 2024
db66fba
more comments, renamings
JohannesMeierSE Nov 22, 2024
9f1b703
refactorings, more test cases and comments according to the review
JohannesMeierSE Nov 26, 2024
2a6c18f
added name as identifier for nominally typed classes earlier, refacto…
JohannesMeierSE Nov 27, 2024
3b471e2
improved existing test cases
JohannesMeierSE Nov 27, 2024
bc43521
renaming, refactoring, more comments
JohannesMeierSE Nov 27, 2024
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
35 changes: 18 additions & 17 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, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, ParameterDetails, PrimitiveKind, TopKind, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation } from 'typir';
import { ClassKind, CreateFieldDetails, CreateFunctionTypeDetails, CreateParameterDetails, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, PrimitiveKind, TopKind, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation, createNoSuperClassCyclesValidation } 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 @@ -190,42 +190,41 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator {

// check for unique function declarations
this.typir.validation.collector.addValidationRuleWithBeforeAndAfter(new UniqueFunctionValidation(this.typir, isFunctionDeclaration));

// check for unique class declarations
this.typir.validation.collector.addValidationRuleWithBeforeAndAfter(new UniqueClassValidation(this.typir, isClass));
const uniqueClassValidator = new UniqueClassValidation(this.typir, isClass);
// check for unique method declarations
this.typir.validation.collector.addValidationRuleWithBeforeAndAfter(new UniqueMethodValidation(this.typir,
(node) => isMethodMember(node), // MethodMembers could have other $containers?
(method, _type) => method.$container));
(method, _type) => method.$container,
uniqueClassValidator,
));
this.typir.validation.collector.addValidationRuleWithBeforeAndAfter(uniqueClassValidator); // TODO this order is important, solve it in a different way!
// check for cycles in super-sub-type relationships
this.typir.validation.collector.addValidationRule(createNoSuperClassCyclesValidation(isClass));
}

onNewAstNode(node: AstNode): void {
// define types which are declared by the users of LOX => investigate the current AST

// function types: they have to be updated after each change of the Langium document, since they are derived from FunctionDeclarations!
if (isFunctionDeclaration(node)) {
this.functionKind.getOrCreateFunctionType(createFunctionDetails(node)); // this logic is reused for methods of classes, since the LOX grammar defines them very similar
this.functionKind.createFunctionType(createFunctionDetails(node)); // this logic is reused for methods of classes, since the LOX grammar defines them very similar
}

// TODO support lambda (type references)!

/**
* TODO Delayed:
* - (classType: Type) => Type(for output)
* - WANN werden sie aufgelöst? bei erster Verwendung?
* - WO wird das verwaltet? im Kind? im Type? im TypeGraph?
*/

// class types (nominal typing):
if (isClass(node)) {
const className = node.name;
const classType = this.classKind.getOrCreateClassType({
const classType = this.classKind.createClassType({
className,
superClasses: node.superClass?.ref, // note that type inference is used here; TODO delayed
superClasses: node.superClass?.ref, // note that type inference is used here
fields: node.members
.filter(isFieldMember) // only Fields, no Methods
.map(f => <CreateFieldDetails>{
name: f.name,
type: f.type, // note that type inference is used here; TODO delayed
type: f.type, // note that type inference is used here
}),
methods: node.members
.filter(isMethodMember) // only Methods, no Fields
Expand All @@ -245,13 +244,15 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator {
},
// inference rule for accessing fields
inferenceRuleForFieldAccess: (domainElement: unknown) => isMemberCall(domainElement) && isFieldMember(domainElement.element?.ref) && domainElement.element!.ref.$container === node
? domainElement.element!.ref.name : 'N/A', // as an alternative, use 'InferenceRuleNotApplicable' instead, what should we recommend?
? domainElement.element!.ref.name : InferenceRuleNotApplicable,
});

// 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.getOrCreateTopClassType({}), 'IMPLICIT_EXPLICIT');
this.typir.conversion.markAsConvertible(this.primitiveKind.getPrimitiveType({ primitiveName: 'nil' })!, classType, 'IMPLICIT_EXPLICIT');
classType.addListener(type => {
this.typir.conversion.markAsConvertible(this.primitiveKind.getPrimitiveType({ primitiveName: 'nil' })!, type, 'IMPLICIT_EXPLICIT');
});
JohannesMeierSE marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Expand All @@ -261,7 +262,7 @@ function createFunctionDetails(node: FunctionDeclaration | MethodMember): Create
return {
functionName: callableName,
outputParameter: { name: NO_PARAMETER_NAME, type: node.returnType },
inputParameters: node.parameters.map(p => (<ParameterDetails>{ name: p.name, type: p.type })),
inputParameters: node.parameters.map(p => (<CreateParameterDetails>{ name: p.name, type: p.type })),
// inference rule for function declaration:
inferenceRuleForDeclaration: (domainElement: unknown) => domainElement === node, // only the current function/method declaration matches!
/** inference rule for funtion/method calls:
Expand Down
Loading