Skip to content

Avoid a redundant MOVE on some string interpolation setups#2324

Merged
vegorov-rbx merged 5 commits into
luau-lang:masterfrom
9382:string-interp
Apr 27, 2026
Merged

Avoid a redundant MOVE on some string interpolation setups#2324
vegorov-rbx merged 5 commits into
luau-lang:masterfrom
9382:string-interp

Conversation

@9382
Copy link
Copy Markdown
Contributor

@9382 9382 commented Apr 3, 2026

This fixes a discrepancy between string interpolation (local a = `{xyz}`) and the manually written equivilant version (local a = ("%*"):format(xyz)), where string interpolation would sometimes introduce a MOVE even when it didn't need to as the target register was marked as temporary

Bytecode difference

Code: local f = `{global}`

Before:

Function 0 (??):
    1: local f = `{global}`
LOADK R1 K0 ['%*']
GETGLOBAL R3 K1 ['global']
NAMECALL R1 R1 K2 ['format']
CALL R1 2 1
MOVE R0 R1
RETURN R0 0

After:

Function 0 (??):
    1: local f = `{global}`
LOADK R0 K0 ['%*']
GETGLOBAL R2 K1 ['global']
NAMECALL R0 R0 K2 ['format']
CALL R0 2 1
RETURN R0 0

This is also technically a performance improvement, though the difference is mostly unnoticeable in normal contexts (I'm seeing a 4-5% improvement on a 1e7 loop which is nothing but the optimisable string interp, so about as good as the case gets)

I've not written a new test case as InterpStringRegisterCleanup already covers the unoptimisable case

This keeps the generated bytecode of string interpolation in line with the result of writing the interpolation manually (e.g. `("%*"):format(xyz)`)

Also means string interpolation may sometimes be faster and use less bytecode, though the improvement is minimal in practice
Copy link
Copy Markdown
Contributor

@tommyscholly tommyscholly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, great catch on the redundant move. Just need the changes to be flagged.

Comment thread Compiler/src/Compiler.cpp
Comment thread tests/Compiler.test.cpp Outdated
@9382
Copy link
Copy Markdown
Contributor Author

9382 commented Apr 6, 2026

(Merged with master to resolve a conflict in Compiler, hopefully it isn't too much of a mess git-wise)

Comment thread Compiler/src/Compiler.cpp
Comment thread Compiler/src/Compiler.cpp Outdated
Copy link
Copy Markdown
Contributor

@SPY SPY left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@vegorov-rbx vegorov-rbx merged commit 26f346c into luau-lang:master Apr 27, 2026
7 checks passed
@vegorov-rbx
Copy link
Copy Markdown
Collaborator

Thank you!

aatxe added a commit that referenced this pull request May 8, 2026
Howdy there, folks! We've got another Luau release this week, mainly
focused on some pain points with type analysis!
  
**Note**: For folks who are using definition files for providing type
definitions for the runtime environment they're working in, we also
wanted to highlight that the `declare class` syntax is being entirely
cleaned up finally. We added syntax for `declare extern type` many
months ago, and with the [Luau classes
RFC](https://rfcs.luau.org/syntax-classes.html) accepted, it is
particularly prudent that we finalize the cleanup of `declare class`. If
you're still on the old syntax, please update to use `declare extern
type Foo with ...` to avoid interruption when a future release removes
`declare class` entirely.
  
  ## Language

* The `const` keyword now correctly appears in autocomplete suggestions
from Luau.

  ## Analysis

- Fixes false positive `OptionalValueAccess` errors when iterating over
a table with an optional indexer type. The VM's generalized iteration
already guarantees non-nil values in the loop body, and the type checker
now reflects that. Fixes
[#2236](#2236).
  ```luau
  --!strict
  type TypeA = { Value: any }
  local list = {} :: { [string]: TypeA? }

  for index, a in list do
a.Value = 1 -- No longer incorrectly reported as 'TypeA?' could be nil
  end
  ```

- Improves bidirectional type inference for unions of tables and
functions. Table literals that clearly match one branch of a union are
no longer falsely rejected:
  ```luau
  --!strict
  type FnRecord = { handler: (number) -> string, label: string? }
  type StrRecord = { handler: string, label: string? }
  type Record = FnRecord | StrRecord

-- Previously flagged as not a subtype of `Record`; now correctly
accepted as a FnRecord
  local r: Record = {
      handler = function(input)
          return tostring(input)
      end,
      label = "test",
  }
  ```

- Fixes a crash that could occur when `typeof` is used inside the type
arguments of an instantiated method call:
  ```luau
  local t = {}
  function t:f<T>() end
  local x = 42
  t:f<<typeof(x)>>() -- No longer crashes
  ```

- Fixes missing autocomplete suggestions for string singleton types when
the expected type is an intersection containing a string singleton (e.g.
`"Foo" & "Foo"` or `keyof<typeof(tbl)> & T`).

 ## Compiler

- Fixes a discrepancy where string interpolation would sometimes emit a
redundant `MOVE` instruction that an equivalent `string.format` call
would not, when the target register was already correct. (#2324 from
@9382, thanks!)
  
  ## Internal Contributors
  
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Annie Tang <annietang@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Thomas Schollenberger <tschollenberger@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Annie Tang <annietang@roblox.com>
Co-authored-by: Annie Tang <98965493+annieetang@users.noreply.github.com>
Co-authored-by: Ilya Rezvov <irezvov@roblox.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants