In Typst v0.13.1, $upright(…)$ always use the math font, and $"…"$ may use another font.
#show math.equation: set text(font: (
// To make the effect obvious, use a font that is absolutely unsuitable for math
(name: "Fira Code", covers: regex("x")),
"New Computer Modern Math",
))
$ x != upright(x) != "x" = #[x] $
The difference is in how they are laid out internally at the moment. upright(x) will stay “inside” math layout, whereas "x" will be laid out externally. In particular, that means "x" goes through the normal text layout code. You should probably be able to find a situation where this difference is actually visible, but I can’t think of one at the moment.
Regardless, the intention is that “…” would use the text font in the future. So I think you should still use upright(x) if x is a variable (or something), and only do "x" if the x is actually text.
I can’t come up with any visually distinguishable example either, but I’m able verify your claim with yaml.encode($…$).
As shown in the table below, "x" and #[x] are texts, but upright(x) is a styled symbol (it.func() does not equal to std.symbol, however).
#show math.equation: set text(font: (
// To make the effect obvious, use a font that is absolutely unsuitable for math
(name: "Libertinus Serif", covers: regex("[x]")),
"New Computer Modern Math",
))
#let debug(content) = raw(yaml.encode(content), lang: "yaml")
#set page(height: auto, width: auto)
#set raw(lang: "typm")
#table(
columns: 2,
inset: 1em,
align: (center + horizon, start),
`x`, debug($x$),
`upright(x)`, debug($upright(x)$),
`"x"`, debug($"x"$),
`#[x]`, debug($#[x]$),
)