From 42ebe0808130e955926e685c8c6439bc8d05ea44 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Wed, 3 Jun 2026 12:12:19 -0700 Subject: [PATCH] [modular_aot] Fix simplification of string interpolation Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I6404c761f9849a50ee25c0387557c20c7b4d632c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508700 Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- pkg/cfg/lib/passes/simplification.dart | 13 +++++++++++- pkg/cfg/testcases/simplification.dart | 11 ++++++++++ pkg/cfg/testcases/simplification.dart.expect | 21 ++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pkg/cfg/lib/passes/simplification.dart b/pkg/cfg/lib/passes/simplification.dart index ac4b4e891f5..9c133e4f09a 100644 --- a/pkg/cfg/lib/passes/simplification.dart +++ b/pkg/cfg/lib/passes/simplification.dart @@ -256,6 +256,10 @@ final class Simplification extends Pass Instruction visitStringInterpolation(StringInterpolation instr) { final buf = _StringInterpolationBuffer(constantFolding); buf.addStringInterpolation(instr); + assert(buf.consumed.isEmpty || buf.optimized); + for (final i in buf.consumed) { + i.removeFromGraph(); + } if (buf.inputs.length == 1) { final input = buf.inputs.single; if (input is String) { @@ -633,6 +637,9 @@ class _StringInterpolationBuffer { // Contains either String or Definition. final List inputs = []; + // Consumed StringInterpolation instructions. + final List consumed = []; + bool optimized = false; _StringInterpolationBuffer(this.constantFolding); @@ -669,7 +676,11 @@ class _StringInterpolationBuffer { break; case StringInterpolation() when input.singleUser == instr: addStringInterpolation(input); - input.removeFromGraph(); + // Do not remove the consumed instruction from the graph immediately, + // as removal may change "has single user" property. + // As a result, instruction which is used multiple times would be both added + // as input and removed, leaving the graph in the inconsistent state. + consumed.add(input); optimized = true; break; default: diff --git a/pkg/cfg/testcases/simplification.dart b/pkg/cfg/testcases/simplification.dart index d793d4f91c2..cd7e7236bc9 100644 --- a/pkg/cfg/testcases/simplification.dart +++ b/pkg/cfg/testcases/simplification.dart @@ -119,6 +119,17 @@ void stringInterpolation(int x) { print('enclosing start... ${'some nested $s, x=$x'} ...end'); } +void stringInterpolation2(List x) { + var path = '${x[0]}${x[1]}'; + var right = 'prefix1:$path'; + var wrong = 'prefix2:$path'; + + print( + 'A very very looooooooooooooooooooooooooooong prefix: ' + "'$right', not '$wrong'.", + ); +} + void closureCall() { final x = (int arg) { print(arg); diff --git a/pkg/cfg/testcases/simplification.dart.expect b/pkg/cfg/testcases/simplification.dart.expect index 06391b1912f..9fc4e6f3697 100644 --- a/pkg/cfg/testcases/simplification.dart.expect +++ b/pkg/cfg/testcases/simplification.dart.expect @@ -219,6 +219,27 @@ B0 = EntryBlock() DirectCall print(v43) Return(v12) +--- stringInterpolation2 +B0 = EntryBlock() + v3 = Constant(0) + v6 = Constant(1) + Constant("prefix1:") + Constant("prefix2:") + Constant("A very very looooooooooooooooooooooooooooong prefix: ") + Constant("'") + Constant("', not '") + v23 = Constant("'.") + v26 = Constant(null) + v29 = Constant("A very very looooooooooooooooooooooooooooong prefix: 'prefix1:") + v30 = Constant("', not 'prefix2:") + v1 = Parameter(x) + v4 = InterfaceCall List.[](v1, v3) + v7 = InterfaceCall List.[](v1, v6) + v8 = StringInterpolation(v4, v7) + v28 = StringInterpolation(v29, v8, v30, v8, v23) + DirectCall print(v28) + Return(v26) + --- closureCall B0 = EntryBlock() v4 = Constant(42)