Skip to content

Commit

Permalink
fix: do not memoize calls containing lambdas calling segments (#944)
Browse files Browse the repository at this point in the history
- do not memoize calls, if lambdas are referencing segment code to
ensure correctness

---------

Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
Co-authored-by: Lars Reimann <mail@larsreimann.com>
  • Loading branch information
3 people authored Mar 7, 2024
1 parent 9923074 commit 114fee6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AstUtils, LangiumDocument, TreeStreamImpl, URI } from 'langium';
import { AstNode, AstUtils, LangiumDocument, TreeStreamImpl, URI } from 'langium';
import {
CompositeGeneratorNode,
expandToNode,
Expand All @@ -16,6 +16,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
import { groupBy, isEmpty } from '../../helpers/collections.js';
import { SafeDsAnnotations } from '../builtins/safe-ds-annotations.js';
import {
isSdsAbstractCall,
isSdsAbstractResult,
isSdsAssignment,
isSdsBlockLambda,
Expand Down Expand Up @@ -968,7 +969,36 @@ export class SafeDsPythonGenerator {
private isMemoizableCall(expression: SdsCall): boolean {
const impurityReasons = this.purityComputer.getImpurityReasonsForExpression(expression);
// If the file is not known, the call is not memoizable
return !impurityReasons.some((reason) => !(reason instanceof FileRead) || reason.path === undefined);
return (
!impurityReasons.some((reason) => !(reason instanceof FileRead) || reason.path === undefined) &&
!this.doesCallContainLambdaReferencingSegment(expression)
);
}

private doesCallContainLambdaReferencingSegment(expression: SdsCall): boolean {
return getArguments(expression).some((arg) => {
if (isSdsExpressionLambda(arg.value)) {
return this.containsSegmentCall(arg.value.result);
} else if (isSdsBlockLambda(arg.value)) {
return this.containsSegmentCall(arg.value.body);
} else {
/* c8 ignore next 2 */
return false;
}
});
}

private containsSegmentCall(node: AstNode | undefined): boolean {
if (!node) {
/* c8 ignore next 2 */
return false;
}
return AstUtils.streamAst(node)
.filter(isSdsAbstractCall)
.some((call) => {
const callable = this.nodeMapper.callToCallable(call);
return isSdsSegment(callable);
});
}

private generateMemoizedCall(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
def __gen_null_safe_call(receiver: Any, callable: Callable[[], __gen_T]) -> __gen_T | None:
return callable() if receiver is not None else None

# Segments ---------------------------------------------------------------------

def segment_a(a):
__gen_yield_result = (a) * (2)
return __gen_yield_result

# Pipelines --------------------------------------------------------------------

def test():
Expand All @@ -32,3 +38,20 @@ def test():
__gen_null_safe_call(j, lambda: 'abc'.j(123))
__gen_null_safe_call(k, lambda: k(456, 1.23))
f(safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.readFile", readFile, [], [safeds_runner.file_mtime('a.txt')]))
f(l(lambda a: segment_a(a)))
f(l(lambda a: (3) * (segment_a(a))))
f(l(lambda a: safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.m", m, [(3) * (segment_a(a))], [])))
f(l(lambda a: (3) * (safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.m", m, [safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.m", m, [(3) * (segment_a(a))], [])], []))))
def __gen_block_lambda_0(a):
__gen_block_lambda_result_result = segment_a(a)
return __gen_block_lambda_result_result
f(l(__gen_block_lambda_0))
def __gen_block_lambda_1(a):
__gen_block_lambda_result_result = safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.m", m, [segment_a(a)], [])
return __gen_block_lambda_result_result
f(l(__gen_block_lambda_1))
f(safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.l", l, [lambda a: (3) * (a)], []))
def __gen_block_lambda_2(a):
__gen_block_lambda_result_result = (3) * (safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.m", m, [a], []))
return __gen_block_lambda_result_result
f(safeds_runner.memoized_call("tests.generator.callWithRunnerIntegration.l", l, [__gen_block_lambda_2], []))

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ fun j(param: Any?, param2: Any?)
@PythonCall("k($param2, $param)")
fun k(param: Any?, param2: Any?)

@Pure
fun l(param: (a: Int) -> result: Int) -> result: Int

@Pure
fun m(param: Int) -> result: Int

@Impure([ImpurityReason.FileReadFromConstantPath("a.txt")])
fun readFile() -> content: String

Expand All @@ -47,4 +53,16 @@ pipeline test {
k?(1.23, 456);

f(readFile());
f(l((a) -> segment_a(a)));
f(l((a) -> 3 * segment_a(a)));
f(l((a) -> m(3 * segment_a(a))));
f(l((a) -> 3 * m(m(3 * segment_a(a)))));
f(l((a) {yield result = segment_a(a); }));
f(l((a) {yield result = m(segment_a(a)); }));
f(l((a) -> 3 * a));
f(l((a) {yield result = 3 * m(a); }));
}

segment segment_a(a: Int) -> result: Int {
yield result = a * 2;
}

0 comments on commit 114fee6

Please sign in to comment.