typir
Version:
General purpose type checking library
113 lines • 6.08 kB
JavaScript
/******************************************************************************
* Copyright 2025 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import { RuleRegistry } from '../../utils/rule-registration.js';
import { removeFromArray } from '../../utils/utils.js';
import { OverloadedFunctionsTypeInferenceRule } from './function-inference-overloaded.js';
import { isFunctionType } from './function-type.js';
import { FunctionCallArgumentsValidation } from './function-validation-calls.js';
/**
* Contains all the logic to manage all available functions,
* in particular, to support overloaded functions.
* In each type system, exactly one instance of this class is stored by the FunctionKind.
*/
export class AvailableFunctionsManager {
constructor(services, kind) {
/**
* function name => all overloaded functions (with additional information) with this name/key
* - The types could be collected with the TypeGraphListener, but the additional information like inference rules are not available.
* Therefore this map needs to be maintained here.
* - Main purpose is to support inference and validation for overloaded functions:
* Since overloaded functions are realized with one function type for each variant,
* the corresponding rules and logic need to involve multiple types,
* which makes it more complex and requires to manage them here and not in the single types.
*/
this.mapNameTypes = new Map();
this.services = services;
this.kind = kind;
this.services.infrastructure.Graph.addListener(this);
// this validation rule for checking arguments of function calls exists "for ever", since it validates all function types
this.validatorArgumentsCalls = this.createFunctionCallArgumentsValidation();
}
createFunctionCallArgumentsValidation() {
// since kind/map is required for the validation (but not visible to the outside), it is created here by the factory
return new FunctionCallArgumentsValidation(this.services, this);
}
createInferenceRuleForOverloads() {
// This inference rule don't need to be registered at the Inference service, since it manages the (de)registrations itself!
return new OverloadedFunctionsTypeInferenceRule(this.services, this.services.Inference);
}
getOverloads(functionName) {
return this.mapNameTypes.get(functionName);
}
getOrCreateOverloads(functionName) {
let result = this.mapNameTypes.get(functionName);
if (result === undefined) {
result = {
overloadedFunctions: [],
details: new RuleRegistry(this.services),
inferenceRule: this.createInferenceRuleForOverloads(),
sameOutputType: undefined,
};
this.mapNameTypes.set(functionName, result);
// the "global" validation for function calls needs to update its registration according to added/removed inference rules for calls of added/removed functions
result.details.addListener(this.validatorArgumentsCalls);
}
return result;
}
getAllOverloads() {
return this.mapNameTypes.entries();
}
addFunction(readyFunctionType, inferenceRulesForCalls) {
const overloaded = this.getOrCreateOverloads(readyFunctionType.functionName);
// remember the function type itself
overloaded.overloadedFunctions.push(readyFunctionType);
this.calculateSameOutputType(overloaded);
// register each inference rule for calls of the function
inferenceRulesForCalls.forEach(rule => overloaded.details.addRule({
functionType: readyFunctionType,
inferenceRuleForCalls: rule,
}, {
languageKey: rule.languageKey, // the language keys are directly encoded inside these special inference rules for function calls
boundToType: readyFunctionType, // these rules are specific for current function type/signature
}));
}
/* Get informed about deleted types in order to remove inference rules which are bound to them. */
onRemovedType(type, _key) {
if (isFunctionType(type)) {
const overloaded = this.getOverloads(type.functionName);
if (overloaded) {
// remove the current function
const removed = removeFromArray(type, overloaded.overloadedFunctions);
if (removed) {
this.calculateSameOutputType(overloaded);
}
// the rule registry removes this function type on its own => nothing to do here
// its inference rule is removed by the CompositeTypeInferenceRule => nothing to do here
}
}
}
calculateSameOutputType(overloaded) {
overloaded.sameOutputType = undefined;
for (let index = 0; index < overloaded.overloadedFunctions.length; index++) {
const current = overloaded.overloadedFunctions[index];
const outputTypeForFunctionCalls = this.kind.getOutputTypeForFunctionCalls(current); // output parameter for function calls
if (index === 0) {
overloaded.sameOutputType = outputTypeForFunctionCalls;
}
else {
if (overloaded.sameOutputType && outputTypeForFunctionCalls && this.services.Equality.areTypesEqual(overloaded.sameOutputType, outputTypeForFunctionCalls) === true) {
// the output types of all overloaded functions are the same for now
}
else {
// there is a difference
overloaded.sameOutputType = undefined;
break;
}
}
}
}
}
//# sourceMappingURL=function-overloading.js.map