---
title: "Lang/RfL meeting 2024-10-09"
tags: ["T-lang", "design-meeting", "minutes"]
date: 2024-10-09
discussion: https://rust-lang.zulipchat.com/#narrow/stream/410673-t-lang.2Fmeetings/topic/RfL.20meeting.202024-10-09
url: https://hackmd.io/AAEd5yPxRJOJKE-ZtP2yOw
---
# Lang/RfL meeting 2024-10-09
[Tracking issue](https://github.com/rust-lang/rust-project-goals/issues/116)
## Off script agenda
Gary Guo would like to go through the design doc for asm-goto.
Alice has a very incomplete target modifiers RFC.
NM: Name for derive smart pointer in FCP as `CoercePointee`.
Benno: I created a use-cases document for field projections.
Zulip topic: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Field.20Projections
Document: https://hackmd.io/@y86-dev/SkSB48hCR
## Target modifiers
Alice: Had thoughts about build-std. Do we really need to block stabilization of these sorts of flags on building the standard library?
NM: I didn't realize they were blocked
Alice: Target modifiers require a new std
NM: Sure, so you can't productively use it, but why does that prevent stabilizing the flag? Seems like an unnecessary dependency. There is precedent, e.g., [when we stabilized `#![no_std]`](https://rust-lang.github.io/rfcs/1184-stabilize-no_std.html) I recall people making the argument that we should stabilize this, even though it can't be used off nightly in practice, to move us forward.
GG: In practice people are building libcore/liballoc and libs team makes decisions based on that (e.g., avoiding feature flags).
AR: Libs team has been avoiding breaking these users....can we just *say* you can compile libcore with rustc and be done with it?
NM: I feel like "maybe" but I'd still rather we not block on that.
MO: Some folks have wanted to add some sort of build-std flag in `rustc` itself (not Cargo).
NM: I strongly suspect that will be the way we do this. Some flag that says "give the default flags you need to build core", even if that's currently the empty set, so that we can evolve it.
## Gary Guo's doc on asm-goto
https://hackmd.io/@nbdd0121/BJlVrepa0 (https://hackmd.io/FFw6vk6cRFSBLQRh1Jf04Q?both for source)
(Background on `asm goto` in C: https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/how-to-use-inline-assembly-language-in-c-code.html#goto-labels)
Niko's takeaways from doc
* Definitely should remove the safe/unsafe
* Directly supporting values out of asm-goto feels elegant, even if low priority
* "This adds complexity to a rarely (in terms of code appearance frequency not runtime frequency) used asm feature" -- how much complexity? I'm curious to hear from Amanieu on this.
* Extending const to CTFE constants seems like a no-brainer...is it backwards compatible?
* Const arguments would be useful for SIMD as well; I love the idea of it but it's a fairly large-ish change
Alice:
* Const accepting pointers seems like a good way to replace 'i' arguments.
Notes on conversation:
AR: from Rust perspective, it looks like this basically means "if a page fault happens, you jump to the default label, but otherwise you drop through the end of the block".
GG: yes, the only thing that affects the design is whether the block is safe or not. If we want to say that `unsafe` is narrowly scoped then we want to make the block safe. I think the argument has been that the block should be safe by default.
TC: Does this tie into a larger language discussion over "goto-labeled blocks"? This was happening over on Zulip recently, I didn't follow the whole discussion, something about labeled block arguments, computed goto...feels like the way that this works in C is reliant on the fact that you can jump via goto in C *anyway*. We don't have that in Rust so I'm struggling to see how we make asm-goto work without the more general goto?
GG: asm-goto is not unstructured control flow in Rust. In C it goes to a label. But in Rust it goes to a block, control is transferred to that block, which is scoped.
```rust
'out: {
'fault: {
unsafe {
asm!(
"/* do some op, jump to {fault} on fail */",
fault = label {
break 'fault;
}
);
}
break 'out;
}
// Fault handling code.
}
```
NM: Seems like even if we did want to add labels in the future, it'd be backwards compatible to support it...
```rust
'out: {
'fault: {
unsafe {
asm!(
"/* do some op, jump to {fault} on fail */",
fault = break 'fault,
);
}
break 'out;
}
// Fault handling code.
}
// Always comes here eventually
```
AR: Right, it seems like you could just say "you can give an inline block or a label".
GG: In this case the fallthrough block has no use.
NM: Currently fallthrough block is not a concept, right?
NM: So, allowing safe code is really perfect here:
```rust
unsafe {
asm!(
"/* do some op, jump to {fault} on fail */",
fault = label {
// safe fault handling code here
}
);
}
```
### Breaking out from loop in asm-implemented computation
Example:
```rust
asm!(
"/* ... */ beq, ..., {end} /* ... */",
end = label {
return ... // or break
},
)
```
Could be mildly shorter but "meh".
### Static branch
Modeling "results" from the `asm!` expression:
```rust
'outer: {
unsafe {
asm!(/* use label */, label { break 'outer true; });
false
}
}
```
...used like...
```rust
if arch_static_branch!(KEY, BRANCH) {
/* */
}
```
...could be nicer if we have had...
```rust
unsafe {
asm!(
"/* do some op, maybe jump to {0}, maybe not */",
label { true },
fallthrough { false },
)
}
```
GG: Easy to implement for the type checker. Already have to do unification because blocks can diverge and what not. We are currently unifying them to unit.
NM: This appeals to me because it feels Rust-y to have asm blocks yield values.
GG: Kind of weird to not permit result values for the more common case of e.g. writing to a register.
NM: :shrug: True? Still nice.
### `i` constraint
GG: This is technically a distinct thing, not specifiying to asm-goto.
In C:
```c
int x;
static inline void foo(int *ptr, int value) {
int y;
asm volatile ("/* ... */"::"i"(100)); // OKAY
asm volatile ("/* ... */"::"i"(1+1)); // OKAY
asm volatile ("/* ... */"::"i"(&x)); // OKAY
asm volatile ("/* ... */"::"i"(&y)); // NOT OKAY
asm volatile ("/* ... */"::"i"(ptr)); // OKAY if caller uses a constant `ptr`.
asm volatile ("/* ... */"::"i"(value)); // OKAY if caller uses a constant `value`.
}
```
Rust permits `const` like so:
```rust
static X: (i32, i32) = 1;
#[inline]
unsafe fn foo<const N: i32>(ptr: *const i32, value: i32) {
let y: i32 = 2;
asm!("/* ... */", const 100); // OKAY
asm!("/* ... */", const 1+1); // OKAY
asm!("/* ... */", const &X); // NOT OKAY
asm!("/* ... */", const &X.1); // NOT OKAY
asm!("/* ... */", sym X); // but this is OKAY
asm!("/* ... */", const &y); // NOT OKAY
asm!("/* ... */", const ptr); // NOT OKAY
asm!("/* ... */", const value); // NOT OKAY
asm!("/* ... */", const N); // but this is OKAY
}
```
But it doesn't work with pointers. There's a workaround to use `sym` which inserts the address of the global. Inserts a symbol name as textual replacement.
Prevents you from writing a function, must use macros.
If we could accept "pointers" with const...could work around this.
GG: There was some discussion. People wanted to say that `const FOO: &str` would substitute the string into the assembly, but I think it should be an address.
AR: So there are these other points, ref to str, where you might want the actual string value...
```asm
mov "foo", x
```
Currently, if you have
```rust
asm!("mov rax, {}", const 100);
```
LLVM gets
```asm
mov rax, 100
```
there's suggestion to make
```rust
asm!("mov rax, {}", const "foo");
```
LLVM gets
```asm
mov rax, foo
```
NM: that seems like a distinct keyword
AR: maybe we should have interpolate or something
```rust
asm!("mov rax, {}", interpolate "foo");
```
GG: alternative would be to insert the address of the pointee. Some concern about how to get that to work for global-asm. Currently works for numerical numbers only. For non-global though can generate similar to `i` constraints.
NM: Is it a problem to just document that it can't be used in global-asm?
GG: Not particularly, it'd just be nice for them to be the same.
NM: in Rust, "foo" is a wide-pointer.......what should that do?
GG: Prob only want thin pointers. Just accept integers or thin pointers.
NM: Basically any pointer to something whose metadata is unit.
NM: Alice the things you want to insert, what type are they?
AR: I want to insert a pointer to a field of a struct. Specifically not at the beginning. So `&X.my_field`.
## THE DREAM: constant function arguments
What we'd be able to do with better `const` is to use `const` generic arguments...
```rust
static X: (i32, i32) = 1;
#[inline]
unsafe fn foo<const N: i32>(ptr: *const i32, value: i32) {
let y: i32 = 2;
asm!("/* ... */", const 100); // OKAY
asm!("/* ... */", const 1+1); // OKAY
asm!("/* ... */", const &X); // NOT OKAY
asm!("/* ... */", const &X.1); // NOT OKAY
asm!("/* ... */", sym X); // but this is OKAY
asm!("/* ... */", const &y); // NOT OKAY
asm!("/* ... */", const ptr); // NOT OKAY
asm!("/* ... */", const value); // NOT OKAY
asm!("/* ... */", const N); // but this is OKAY
}
```
```rust
unsafe fn foo(const ptr: *const i32, const value: i32) {
asm!("/* ... */", const ptr);
asm!("/* ... */", const value);
}
```