Skip to content

Commit

Permalink
Implemented the cycle introduction and transitivity check for convers…
Browse files Browse the repository at this point in the history
…ion.ts.

Throw error in case implicit conversion has introduced a cycle in the type graph.
  • Loading branch information
insafuhrmann committed Oct 14, 2024
1 parent cbdf797 commit 65aca38
Showing 1 changed file with 45 additions and 13 deletions.
58 changes: 45 additions & 13 deletions packages/typir/src/features/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,16 @@ export class DefaultTypeConversion implements TypeConversion {
edge.mode = mode;
}

// check, that the new edges did not introduce cycles
this.checkForCycles(mode);
}

protected checkForCycles(mode: ConversionModeForSpecification): void {
if (mode === 'IMPLICIT_EXPLICIT') {
this.checkForCyclesLogic(mode);
} else {
// all other modes allow cycles
/* check that the new edges did not introduce cycles
* if it did, the from node will be reachable via a cycle path
*/
const hasIntroducedCycle = this.existsEdgePath(from, from, mode);
if (hasIntroducedCycle) {
throw new Error('Adding the conversion from ' + from.identifier + ' to ' + to.identifier + ' has introduced a cycle ot the type graph.');
}
}
}
protected checkForCyclesLogic(_mode: ConversionModeForSpecification): void {
// TODO check for cycles and throw an Error in case of found cycles
}

protected isTransitive(mode: ConversionModeForSpecification): boolean {
// by default, only IMPLICIT is transitive!
Expand Down Expand Up @@ -180,11 +176,47 @@ export class DefaultTypeConversion implements TypeConversion {
return 'NONE';
}

protected isTransitivelyConvertable(_from: Type, _to: Type, _mode: ConversionModeForSpecification): boolean {
// TODO calculate transitive relationship
protected existsEdgePath(from: Type, to: Type, mode: ConversionModeForSpecification): boolean {
const visited: Set<Type> = new Set();
const stack: Type[] = [from];

while (stack.length > 0) {
const current = stack.pop()!;
visited.add(current);

const outgoingEdges = current.getOutgoingEdges<ConversionEdge>(ConversionEdge);
for (const edge of outgoingEdges) {
if (edge.mode === mode) {
if (edge.to === to) {
/* It was possible to reach our goal type using this path.
* Base case that also catches the case in which start and end are the same
* (is there a cycle?). Therefore it is allowed to have been "visited".
* True will only be returned if there is a real path (cycle) made up of edges
*/
return true;
}
if (!visited.has(edge.to)) {
/* The target node of this edge has not been visited before and is also not our goal node
* Add it to the stack and investigate this path later.
*/
stack.push(edge.to);
}
}
}
}

// Fall through means that we could not reach the goal type
return false;
}

protected isTransitivelyConvertable(from: Type, to: Type, mode: ConversionModeForSpecification): boolean {
if (from === to) {
return true;
} else {
return(this.existsEdgePath(from, to, mode));
}
}

isImplicitExplicitConvertible(from: Type, to: Type): boolean {
return this.getConversion(from, to) === 'IMPLICIT_EXPLICIT';
}
Expand Down

0 comments on commit 65aca38

Please sign in to comment.