diff --git a/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts b/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts index 6098d187a..ca445aeeb 100644 --- a/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts +++ b/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts @@ -89,6 +89,7 @@ import { } from '../partialEvaluation/model.js'; import { SafeDsPartialEvaluator } from '../partialEvaluation/safe-ds-partial-evaluator.js'; import { SafeDsServices } from '../safe-ds-module.js'; +import { SafeDsPurityComputer } from '../purity/safe-ds-purity-computer.js'; export const CODEGEN_PREFIX = '__gen_'; const BLOCK_LAMBDA_PREFIX = `${CODEGEN_PREFIX}block_lambda_`; @@ -104,11 +105,13 @@ export class SafeDsPythonGenerator { private readonly builtinAnnotations: SafeDsAnnotations; private readonly nodeMapper: SafeDsNodeMapper; private readonly partialEvaluator: SafeDsPartialEvaluator; + private readonly purityComputer: SafeDsPurityComputer; constructor(services: SafeDsServices) { this.builtinAnnotations = services.builtins.Annotations; this.nodeMapper = services.helpers.NodeMapper; this.partialEvaluator = services.evaluation.PartialEvaluator; + this.purityComputer = services.purity.PurityComputer; } generate(document: LangiumDocument, generateOptions: GenerateOptions): TextDocument[] { @@ -356,8 +359,7 @@ export class SafeDsPythonGenerator { } private generateBlock(block: SdsBlock, frame: GenerationInfoFrame): CompositeGeneratorNode { - // TODO filter withEffect - let statements = getStatements(block); + let statements = getStatements(block).filter((stmt) => this.hasStatementEffect(stmt)); if (statements.length === 0) { return traceToNode(block)('pass'); } @@ -366,6 +368,21 @@ export class SafeDsPythonGenerator { })!; } + private hasStatementEffect(statement: SdsStatement): boolean { + if (isSdsAssignment(statement)) { + const assignees = getAssignees(statement); + return ( + assignees.some((value) => !isSdsWildcard(value)) || + (statement.expression !== undefined && + this.purityComputer.expressionHasSideEffects(statement.expression)) + ); + } else if (isSdsExpressionStatement(statement)) { + return this.purityComputer.expressionHasSideEffects(statement.expression); + } + /* c8 ignore next */ + return false; + } + private generateStatement(statement: SdsStatement, frame: GenerationInfoFrame): CompositeGeneratorNode { if (isSdsAssignment(statement)) { return traceToNode(statement)(this.generateAssignment(statement, frame)); diff --git a/packages/safe-ds-lang/tests/resources/generation/declarations/parameter with python name/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/declarations/parameter with python name/input.sdstest index 864b5ef80..b16872756 100644 --- a/packages/safe-ds-lang/tests/resources/generation/declarations/parameter with python name/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/declarations/parameter with python name/input.sdstest @@ -1,7 +1,7 @@ package tests.generator.parameterWithPythonName -@Pure fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) -@Pure fun f2(param: (a: Int, b: Int, c: Int) -> ()) +@Impure([ImpurityReason.Other]) fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) +@Impure([ImpurityReason.Other]) fun f2(param: (a: Int, b: Int, c: Int) -> ()) segment test(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { f1((a: Int, b: Int, c: Int = 0) -> 1); diff --git a/packages/safe-ds-lang/tests/resources/generation/declarations/pipeline with python name/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/declarations/pipeline with python name/input.sdstest index 084980dfb..0b0ac9b6d 100644 --- a/packages/safe-ds-lang/tests/resources/generation/declarations/pipeline with python name/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/declarations/pipeline with python name/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.pipelineWithPythonName -@Pure fun f() +@Impure([ImpurityReason.Other]) fun f() @PythonName("test_pipeline") pipeline testPipeline { diff --git a/packages/safe-ds-lang/tests/resources/generation/declarations/segment with python name/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/declarations/segment with python name/input.sdstest index c8268be49..99dd0f1bd 100644 --- a/packages/safe-ds-lang/tests/resources/generation/declarations/segment with python name/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/declarations/segment with python name/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.segmentWithPythonName -@Pure fun f() +@Impure([ImpurityReason.Other]) fun f() @PythonName("test_segment") segment testSegment() { diff --git a/packages/safe-ds-lang/tests/resources/generation/declarations/two pipelines/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/declarations/two pipelines/input.sdstest index 4b5f27ba0..7f2541170 100644 --- a/packages/safe-ds-lang/tests/resources/generation/declarations/two pipelines/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/declarations/two pipelines/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.twoPipelines -@Pure fun f() +@Impure([ImpurityReason.Other]) fun f() pipeline test1 { f(); diff --git a/packages/safe-ds-lang/tests/resources/generation/declarations/two segments/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/declarations/two segments/input.sdstest index 2bfc5f01b..3b61d9942 100644 --- a/packages/safe-ds-lang/tests/resources/generation/declarations/two segments/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/declarations/two segments/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.twoSegments -@Pure fun f() +@Impure([ImpurityReason.Other]) fun f() segment test1(a: Int, b: Int = 0) { f(); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda result/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda result/input.sdstest index b13adc919..3cd1ae3bf 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda result/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda result/input.sdstest @@ -2,7 +2,7 @@ package tests.generator.blockLambdaResult @Pure fun g() -> a: Int -@Pure fun h(a: Int) +@Impure([ImpurityReason.Other]) fun h(a: Int) segment f1(l: (a: Int, b: Int) -> d: Int) { h(l(1, 2).d); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda/input.sdstest index 892566d16..95739d26a 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/block lambda/input.sdstest @@ -1,7 +1,7 @@ package tests.generator.blockLambda -@Pure fun f1(param: (a: Int, b: Int) -> r: Int) -@Pure fun f2(param: () -> ()) +@Impure([ImpurityReason.Other]) fun f1(param: (a: Int, b: Int) -> r: Int) +@Impure([ImpurityReason.Other]) fun f2(param: () -> ()) @Pure fun g() -> a: Int diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/call/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/call/input.sdstest index b6f8d2177..fad1d70f2 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/call/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/call/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.call -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g( param1: Int, @@ -12,15 +12,15 @@ package tests.generator.call @PythonName("param_2") param2: Int = 0 ) -> result: Boolean -@Pure +@Impure([ImpurityReason.Other]) @PythonCall("$param.i()") fun i(param: Any?) -@Pure +@Impure([ImpurityReason.Other]) @PythonCall("$param.j($param2)") fun j(param: Any?, param2: Any?) -@Pure +@Impure([ImpurityReason.Other]) @PythonCall("k($param2, $param)") fun k(param: Any?, param2: Any?) diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/constant/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/constant/input.sdstest index e97fc786d..14c636007 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/constant/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/constant/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.constant -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) pipeline test { f(1 < 2); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/enum variant call/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/enum variant call/input.sdstest index 30a67bc45..b7c8a3598 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/enum variant call/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/enum variant call/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.enumVariantCall -@Pure fun f(p: Any) +@Impure([ImpurityReason.Other]) fun f(p: Any) enum MyEnum { Variant1 diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/expression lambda/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/expression lambda/input.sdstest index 7ff05745e..9991d59a0 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/expression lambda/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/expression lambda/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.expressionLambda -@Pure fun f(param: (a: Int, b: Int) -> r: Int) +@Impure([ImpurityReason.Other]) fun f(param: (a: Int, b: Int) -> r: Int) pipeline test { f((a, b = 2) -> 1); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/indexed access/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/indexed access/input.sdstest index e9bff8f50..0dd0cef9a 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/indexed access/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/indexed access/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.indexedAccess -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) segment test(params: List) { f(params[0]); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/infix operation/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/infix operation/input.sdstest index 13b5f86be..cd554cc93 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/infix operation/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/infix operation/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.infixOperation -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g() -> result: Boolean diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/lists/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/lists/input.sdstest index a01b6e147..eee596243 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/lists/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/lists/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.lists -@Pure fun f(param: List) +@Impure([ImpurityReason.Other]) fun f(param: List) @Pure fun h() -> result: Int diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/literals/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/literals/input.sdstest index 402264bbc..33625ee46 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/literals/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/literals/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.literals -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) pipeline test { f(true); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest index abca2df8a..dc6734c8e 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest @@ -1,8 +1,8 @@ package tests.generator.maps -@Pure fun g1(param: Map) +@Impure([ImpurityReason.Other]) fun g1(param: Map) -@Pure fun g2(param: Map) +@Impure([ImpurityReason.Other]) fun g2(param: Map) @Pure fun h1() -> result: Float diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/member access/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/member access/input.sdstest index 496f540f4..9d5fc1339 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/member access/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/member access/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.memberAccess -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g() -> result: Boolean diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/parenthesized expression/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/parenthesized expression/input.sdstest index f83f51e62..06fceb84b 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/parenthesized expression/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/parenthesized expression/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.parenthesizedExpression -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g() -> result: Boolean diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/prefix operation/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/prefix operation/input.sdstest index ca2263a45..ff38d2a73 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/prefix operation/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/prefix operation/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.prefixOperation -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g() -> result: Boolean diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/reference/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/reference/input.sdstest index 92c6009ce..b248defce 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/reference/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/reference/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.reference -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure @PythonName("explain_model") diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/template string/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/template string/input.sdstest index ceedd8373..2e8645428 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/template string/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/template string/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.templateString -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) @Pure fun g() -> result: Int diff --git a/packages/safe-ds-lang/tests/resources/generation/imports/general/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/imports/general/input.sdstest index 82eec1390..e7176f729 100644 --- a/packages/safe-ds-lang/tests/resources/generation/imports/general/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/imports/general/input.sdstest @@ -5,7 +5,7 @@ from tests.generator.differentPackage import function2InDifferentPackage as g from tests.generator.withPythonModule import function1InCompilationUnitWithPythonModule from tests.generator.withPythonModule import function2InCompilationUnitWithPythonModule as h -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) pipeline test { f(segment1InSamePackage()); diff --git a/packages/safe-ds-lang/tests/resources/generation/imports/wildcard/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/imports/wildcard/input.sdstest index 483cf0233..4a0455519 100644 --- a/packages/safe-ds-lang/tests/resources/generation/imports/wildcard/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/imports/wildcard/input.sdstest @@ -3,7 +3,7 @@ package tests.generator.wildcard from tests.generator.differentPackageWildcard import * from tests.generator.withPythonModuleWildcard import * -@Pure fun f(param: Any?) +@Impure([ImpurityReason.Other]) fun f(param: Any?) pipeline test { f(function1InDifferentPackage()); diff --git a/packages/safe-ds-lang/tests/resources/generation/python module/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/python module/input.sdstest index ddfc25524..9d1cea8e8 100644 --- a/packages/safe-ds-lang/tests/resources/generation/python module/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/python module/input.sdstest @@ -2,7 +2,7 @@ package tests.generator.pythonModule -@Pure fun f() +@Impure([ImpurityReason.Other]) fun f() segment test() { f(); diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/assignment/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/statements/assignment/input.sdstest index f6eb364cb..98ae872cd 100644 --- a/packages/safe-ds-lang/tests/resources/generation/statements/assignment/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/statements/assignment/input.sdstest @@ -1,9 +1,9 @@ package tests.generator.assignment -@Pure fun f1(param: Any?) -@Pure fun f2(param: () -> r: Int?) +@Impure([ImpurityReason.Other]) fun f1(param: Any?) +@Impure([ImpurityReason.Other]) fun f2(param: () -> r: Int?) -@Pure fun g() -> (a: Int, b: Int, c: Int) +@Impure([ImpurityReason.Other]) fun g() -> (a: Int, b: Int, c: Int) pipeline testPipeline { _, _ ,_ = g(); diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/expression statement/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/statements/expression statement/input.sdstest index a2db47180..188ab8bb4 100644 --- a/packages/safe-ds-lang/tests/resources/generation/statements/expression statement/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/statements/expression statement/input.sdstest @@ -1,8 +1,8 @@ package tests.generator.expressionStatement -@Pure fun f(param: () -> ()) +@Impure([ImpurityReason.Other]) fun f(param: () -> ()) -@Pure fun g() -> result: Int +@Impure([ImpurityReason.Other]) fun g() -> result: Int pipeline testPipeline { g(); diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/input.sdstest similarity index 76% rename from packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/input.sdstest rename to packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/input.sdstest index a862d78fb..67d0bf46c 100644 --- a/packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/input.sdstest @@ -1,6 +1,6 @@ package tests.generator.statementWithoutEffect -fun f(param: () -> ()) +@Impure([ImpurityReason.Other]) fun f(param: () -> ()) pipeline testPipeline { 1; diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py similarity index 100% rename from packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py rename to packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py.map b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py.map new file mode 100644 index 000000000..65d34c1e3 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py.map @@ -0,0 +1 @@ +{"version":3,"sources":["input.sdstest"],"names":["testsegment","testpipeline","f"],"mappings":"AAAA;;AAcA,IAAQA,WAAW;IAAG;;;;AAVtB,IAASC,YAAY;IAIf;QAAG;IAALC,CAAC,CAAC","file":"gen_input.py"} \ No newline at end of file diff --git a/packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py b/packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py similarity index 100% rename from packages/safe-ds-lang/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py rename to packages/safe-ds-lang/tests/resources/generation/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py