Change:
The says It does not give any details of when it might not be possible to perform the alignment. My reading of that is that the user must always be prepared for failure to happen. In accordance with that, a while ago I adjusted the (the preferred way to use ) to say In practice, returning happens when (whatever exactly this means, this is taken from the implementation) -- and when running in Miri, which will always return . Historically, Miri did this because it had no notion of the "integer address" of an allocation, so there literally was no way to offset a pointer to get a particular alignment. This is changing now, Miri is getting the support for this. So maybe we should note some conditions under which will definitely succeed. A motivation for this is , which implicitly made the assumption that aligning with will always succeed. On the other hand, the current contract for lets Miri do more reliable alignment checking. This is off-by-default but can be enabled with : when checking whether some pointer that points to offset inside an allocation with alignment , we have the option to consider only and and not the integer value of . This allows us to reliably detect alignment problems in code such as: If we were to take the actual integer value of into account, the program might get "lucky" and actually run successfully in Miri because happens to be even. In contrast, by not doing this, Miri can offer a flag where the bug in the program above is definitely caught. With this flag, the user can be sure that none of the accesses in the program are aligned "by chance". However, this also means that when this flag is set, the following code will not pass: Miri cannot know that you actually did your homework and checked the integer address. This program is basically indistinguishable from the bad program above. Currently there does not seem to be much code that operates like the last example above -- code will instead use , which will (when run in Miri with symbolic alignment checking) make the middle part empty, and thus the "higher-aligned" accesses just don't happen. This means the vast majority of code works fine in the better-alignment-checking mode. If we force to not fail like expects, then suddenly will return non-empty middle parts in Miri as well, and will basically be useless. There will be false positives when any method using is called, which includes a few fundamental methods in libcore. So, here's the trade-off: either Miri has a mode that can reliably detect alignment problems, or guarantees success under certain conditions. I don't think we can have both. Which one do we pick? (The particular PR that triggered this might be fixable to work with the contract Miri needs, I don't know. But this discussion will probably come up again.) Cc
Turns out that fixing to work fine with Miri does not even change the generated assembly; looks like LLVM can just optimize away the extra check for . That takes the time pressure of this discussion, that PR can land without having to reach any conclusion here. The discussion still seems to be worth having though!
Another reason we might want to keep the spec as-is is to be able to eventually make a . CTFE does not, cannot and should not pick an actual "base address" for an allocation, so there is no way for any byte in a 1-aligned allocation (say, a ) to have an alignment higher than 1. After all, depending on the actual base address, any of these bytes could be just 1-aligned. Sure, some of them will have higher alignment, but we cannot know which -- and deciding which during CTFE would have to be made consistent with the base address this data gets later at run-time.
This essentially checks that it is indeed possible to align , which might not be the case for some specific values of , and . For example , and . Or, alternatively, that a solution for the linear congruence equation exists.
What is in your text? For me it was the alignment of the allocation but when considering the physical machine that doesn't seem to make sense? I guess you mean the parameter? I see. If your pointer points to an odd address and you can only move even-sized steps, you cannot ever get an even address.
Its the parameter, yes. of the comment elaborates.
The changes made to the documentation of alignoffset, and in particular the present stance that is not very pragmatic. The need to get an aligned reference inside a Vec<u8is a very mundane and common one. Eg: . The way that bumpalo seems to be working around this limitation is just to cast to a usize, do the rounding there, and then cast back to a pointer. See: . Is round tripping a pointer through a usize really what we want to say is the best practice? That seems to me kind of bizarre. If some operation like aligning a u8 is fundamentally unsound, why would converting to usize, aligning, and converting back be any more sound? Does altering code that would normally deal with u8 so that it deals with usize instead lose many of the benefits that Miri is meant to provide? Perhaps code dealing with u8 does have a soundness problem which Miri would catch, but then the code is altered to use usize instead and Miri can no longer catch the problem.
Quick remark (will come back to the rest of your comment when I have more time): Note that this is not a change. Already the says Guaranteeing more than that would be a change.
It is not fundamentally unsound. Where are you getting that idea from? The issue above explains in great detail what the issue is with guaranteeing more for ; "unsound" is not a word that is used there. Do you have a concrete question about what the problem is, something I should clarify? I do not understand the question. What bumpalo does will indeed not work properly in Miri right now, and when Miri gains support for such code it will come at the expense of test coverage. For code like bumpalo that really needs to successfully get a higher-aligned pointer out of a lower-aligned allocation, that's fine, there is nothing else we can do -- but most code using is not of that kind, and it would be sad to lose Miri test coverage for all that code.
Hello, I'm using in one project and I find myself in the need of being . I never contributed to the rust language before, but is there something I can do to help ?
what exactly do you need from it at const-time? The full functionality of is impossible to implement in , since it is impossible to determine the actual integer address that an allocation will be placed at.
I define symbols in a linker script, then get them in my rust code as external symbols. And want to use them in a static objects. In In rust: So this works. But I need my struct to contain a 4k page aligned value, so I tried the following: But this is not allowed because the cast to usize would allow things that would lead to non constant results, which I understand. But I was expecting that this will be allowed as it will always produce the same output:
This is impossible. Const evaluation is done by the compiler, while the final address is determined by the linker, which runs later. The compiler doesn't know the address; all it can do is emit a relocation to ask the linker to insert the address in a specified place in the code or data. A relocation can request adding or subtracting an arbitrary offset to the address, but not more complicated math than that. That said, you can do the math in the linker script. Something like should work.
Ok yeah, it seems obvious when you explain it. Dunno why I didn't realized it myself. Thanks for for linker align tip, I got the same idea last night when I went to sleep. That's what I will do. Ty.
Now that Miri's , would it be possible to strengthen 's documented guarantees accordingly? Or are there still reasons we want to allow it to fail in all cases?
There's still the question of, at some point, allowing to be called during CTFE -- which will only be possible with the weak semantics.
For reference, the main reason I'm interested in having stronger guarantees is that it would enable the use of without platform- or implementation-specific assumptions. For example: The “standard” way of doing this is to cast the pointer to a and use the lower bits, which works in practice but, as far as I know, relies on assumptions about pointer-to-integer conversions that aren't guaranteed by the language. With stronger guarantees, even if they applied only to non-CTFE contexts, the example above could be guaranteed not to panic.
The implementation of is relying on those same guarantees... but I guess that is a libstd implementation detail and none of your concern. ;) I'd say we should probably make those guarantees, except I have no idea what happens on platforms like CHERI that have hardware ptr tagging -- but then, there are many open questions for Rust on those platforms. Btw, is there a reason you are not using and on pointers? That would avoid having to make inbounds assumptions (in particular, it would make your scheme work even for pointers to zero-sized objects).
Indeed, and while searching around, I came across someone who noted that the uses a tagged pointer with the standard method of converting to a and using the lower bits. Making additional guarantees about pointer-to-integer conversions would definitely work for this purpose, but I figured they might be more controversial than strengthening 's guarantees. There are theoretically platforms, as you note, that wouldn't allow reusing the lower bits of an aligned pointer, but would work with the implementation. Supporting those platforms, of course, would require changes to libstd. Ah, good point—that would indeed be better. As silly as it sounds, I hadn't actually realized that and aren't (I should've read the documentation more carefully!).
I must say, as someone who has to align pointers from time to time, strikes me as a very strange API. To start with, why does it express failure as a sentinel value instead of returning ? Perhaps as a micro-optimization, but I don't see why a sentinel would be any faster. (If it returned an , in a typical case I'd expect the optimizer to inline and then be able to show that the discriminant is always .) Beyond that, at first I didn't even understand the use case for an alignment operation that can fail. After reading up on I see what it is: algorithms that can run on arbitrary aligned buffers, but have a fast path for well-aligned buffers, and try to extract the well-aligned subset of the input buffer to run the fast path on. Fair enough, such algorithms can often tolerate spurious failure – but even then, some might rely on the assumption that the prefix and suffix each have a size smaller than the alignment. Regardless, there are many other use cases for that can't tolerate failure. One common one is to allocate an aligned buffer within a non-aligned slice of unused memory, like does. Some in-memory and file formats have a similar principle: they contain a series of entries which are implicitly padded to multiples of N bytes. For example, if N is 8 and you saw a 5 byte entry, you would have to skip an additional 3 bytes to find the next entry. If you use for this and it fails, you won't know where to find the next entry. Admittedly, even in C, these formats are usually implemented by checking the alignment of integer sizes or offsets, not pointers. But not always: is an example of Rust code using to compute the correct offset in an in-memory format. And even when it comes to code that does operate on integers… while is only provided for pointers, I found of code that casts a size value to a for the sole purpose of calling on it! Another use case is testing in order to check whether is already aligned – sometimes , though more often . <details<summary(Pointless sidenote about C)</summaryIt was nonintuitive to me that people would use <codealignoffset</codefor this purpose. I'm more used to C where there's no standard helper function equivalent to <codealignoffset</code, so you have to write out the math explicitly. To compute the bytes needed to round up, i.e. what <codealignoffset</codeis, you write <code-foo & (align - 1)</code, whereas to compute the bytes needed to round down, you write <codefoo & (align - 1)</code. If you want to check whether something is already aligned, either will work, but the latter is less nonintuitive (what is negating a pointer supposed to accomplish?) and one character shorter, so you would usually pick that: <code(foo & (align - 1)) == 0</code. Of course, there's nothing wrong with using <codealignoffset</code. If it's behind a helper function, intuitiveness doesn't matter, and the performance is likely identical since the optimizer will likely optimize away the negation. That said, I'm surprised that we have <codealignoffset</code, but don't have a complementary helper function to compute the number of bytes needed to round down. </detailsHonestly, I originally thought this didn't matter very much either way, but researching this comment has changed my mind. Each of the five links above points to real-world code that uses and assumes it will never fail, despite what the docs say. Some of the examples have an assert that it doesn't return , while others will just silently misbehave in that case. The examples weren't exactly hard to find: I used to search for uses of , and it seemed like most of them assumed it wouldn't fail. (There were also surprisingly many uses overall, to me; I had thought it was a little-used function.) Heck, even seems to be misused more often than not: . Given that, I am strongly in favor of strengthening 's semantics to guarantee what people already expect of it, even though it means giving up on compatibility. If we want a -compatible version, it can be as a new function. The new function could have a longer name that somehow indicates it can spuriously fail, though I'm not sure how to word this. And it could have a proper return type, so that if people do misuse it, at least it will only result in panics when they try to unwrap it, not silent miscalculation.
It might be worth pointing out C++'s , just as a matter of seeing what other languages do. It also rounds up and also has a weird API (although differently so), but the most important difference is that it isn't allowed to spuriously fail.
just to be sure, those uses you found that assume that cannot fail, they use it with a pointer type of size 1? For the more general case of arbitrary pointer type and arbitrary alignment, it is not always possible to align the pointer properly, even disregarding const-compatibility. IOW, the awkwardness of the API (sentinel value instead of ) is not solved by ditching const-compatibility.
The advantage of the sentinel value is that the normal usage on normal platforms has one less branch, as you don't need to match on an option first and then do an inbound check. Instead the inbound check alone suffices
Oops, I missed this when you originally posted it. I re-checked my examples and they do all use it with pointers. That seems to be by far the most common use case. Perhaps worth noting: for any type whose size equals its alignment, will never fail for a well-aligned input pointer. However, the set of types for which size equals alignment is platform-dependent. I tend to assume it's true for all basic integer types, but it's false for on Windows 32-bit x86.
Is there actually any fundamental reason cannot work sanely in CTFE so long as ptr2int doesn't exist (which aiui is not expected to change)? The algorithm I'm thinking of is for to just increase the alignment for the allocation of to be (if is larger than the current alignment). Then CTFE has all of the information needed to compute : the offset mod of the allocation base (now guaranteed to be 0), and the offset of from the allocation base ( is stable const). So long as the compiler is guaranteed to be able to recognize every CTFE alignment check (by the lack of ptr2int), it can claim that the allocation is aligned for every checked value, and then if the allocation is promoted to a static, it can require that alignment for the static. This does mean that it would still fail during const eval (in some way, panic, linker error, return failure) for "too large" alignments that still fit in a usize, but that really doesn't sound like a problem tbh. (And slice::alignto/assimd would never fail)
I think it would be very surprising if calling actually affected the alignment of an allocation. Also, I don't think this can actually work: the const to which points might be in another crate whose bitcode has already been generated.
I mean, less surprising than the current definition (and more definitely, results in less obnoxious downstream APIs). The fact that the allocation might be defined in another crate is technically not a problem at the moment with the current definition of that I hate (that they're substituted at every use and any dedup anywhere is entirely best-effort, and you cannot even refer to any statics at all), though it's certainly a bit more ugly as a potential source of .(ro)data size, and the restriction preventing access to Freeze statics is otherwise unnecessary iirc. In practice it could certainly be avoided by increasing the default align of statics to their size up to an arch-specific maximum, though that doesn't prevent the general case problem if we permit const references to any actual .
I strongly disagree. Currently we just have an API that returns an "unknown" result. Your proposal means non-local side-effects of what should be a read-only operation. I can already see people trying to debug why Rust puts such huge amounts of padding into their binary, just because some other crate internally somewhere used with a high value. Any crate with access to a given const can now suddenly change the way that const is compiled! I think it very much is a problem at least with the current implementation in rustc. With , the allocation containing is part of the original crate and will not be duplicated (it has an and that is used consistently by all downstream crates). It is impossible for downstream crates to alter this allocation. yet, downstream crates might call on . The fact that, just looking at this crate, we cannot tell the alignment constraint that will be generated for that , is both IMA a big red flag semantically, and also completely incompatible with the rustc architecture. (Not that rustc architecture should necessarily drive our design. But it just shows that such effects of downstream crates on upstream data is so out-of-the-ordinary that rustc was designed in a way that it cannot even be achieved.)
We have APIs (alignto/assimd) that return aggressively useless results that make everyone who sees them go wtf. And once they understand, it's still annoying and warps the code to not be able to do simple things that would require the head/tail to be less than the lane count. And you can't force the head to be empty by forcing the alignment elsewhere (and just use these functions as a safe and correct transmute). Yes, this is not ideal, but let's be realistic here; people don't randomly call these APIs, generally don't call them in const in the first place (the higher-level APIs aren't even , despite having the screwed up design that comes from alignoffset), and don't generally call them with large values in the first place (much less values larger than the size). Isn't that precisely the one case where dedup failure can be detected (and comes up via associated consts being generic)? It still does make it far more ugly, though the silly align=size heuristic is always more likely to remove the issue in practice.
These APIs are not useful for your use case, because they are not meant for it. I created that adds targetted functions for your use cases. My preference is still to have precise functions instead of a universal function, similar to how the proposal for strict provenance argues against as a tool that does 50 different things, depending on the runtime input values.
You mean isn't intended to be useful for SIMD algorithms (and isn't intended to be useful for them without portable-simd)? The extra "force alignment other ways" part sure, though requiring separate APIs for that is silly.
We can bounce arguments back and forth, but I don't understand why align_to is useless, considering it has libstd usages that don't even need SIMD ops but instead use integer types on the middle slice for measurable speedups. Why do you think having separate APIs is silly? You are throwing away the start and end slice on every call, woundn't you rather just have a function that gives you what you actually want?
is perfectly useful; claiming it produces "aggressively useless results" is useless hyperbole. It returns three slices that join to the intended slice. It's not even a so I don't see the relation to your const-related suggestions. Calling with the intent of ignoring the first and last component is clearly an API misuse, as is strongly suggested by both the docs and the type of that function. However, since is not a , it would be totally possible to change its contract to always require the middle part to be maximal. wg-const-eval isn't even involved in that discussion; you'll have to take that up with t-libs-api (probably by writing an ).
The entire reason it has issues is because of 's API, so it is clearly relevant if 's API can be fixed (for which "external crates s" is a vaguely reasonable counterargument even if it's somewhat workaroundable). And when people are ignoring assimd/alignto to make their own versions because of the warning text (probably before the update to alignto's warning text, to be fair) I think it's worth calling them useless. I agree that if alignto/assimd aren't going to go const that they should just be fixed via docs and it's probably worth leaving alone .
I would suggest mostly ignoring for this discussion. It's under the tracking issue, so isn't particularly close to stabilization at all, and can still be changed however we want -- including potentially removing it entirely.
No, it just means that this function does not cover those people's needs. It says nothing about the usefulness of the function for its actually intended usecase. We are not looking to turn into a kitchen sink that can cover everything; if there are needs it doesn't cover, the best answer is new APIs that cover them. When you claim "useless" you are claiming "nobody can have a use for this function". That claim is obviously false, and also non-productive. The public API of is no no way tied to its internal implementation via . If you can convince t-libs-api that should have a different API contract, we'll change the implementation to make that work -- that is not and has never been the blocking issue. We could even change 's contract to say that at runtime, the offset computation is guaranteed to not fail "spuriously". This has been talked about for years, but nobody seriously tried to actually make such a change. That would require a proper motivation and explanation of usecases, and why changing to be suitable for such cases is a better option than adding a new function. So if you want to truly move this discussion forward, I suggest you prepare such a document. I don't think there is much to discuss in this issue here in the meantime.
When people reimplement the exact same function only without the unnecessary caveat, saying "does not cover those people's needs" like it's a special case is silly. And claiming that the public API limitations that are taken straight from its internal implementation and have no other excuse are not related is also wtf. I agree that it sounds like this is not the place to actually solve it.
Given the semantic and architectural issues with "have change the alignment of a compile-time allocation", I can only repeat what I have been saying for years: the most promising way to change is to declare that at runtime, it will always produce the best possible result, but at compiletime it can fail. This situation is blocked on someone who cares sufficiently about this problem to write up that proposal with motivating examples and argue why those examples are not better handled with a different API. So far I haven't seen the proponents of this change seriously engage with the idea of alternative APIs. A consequence of this proposal is that either, will never be stably const, or we have const functions that behave in ways that are never permitted at runtime. I think we will have the latter anyway with an eventual safe consteval_select, so I don't think that is in itself a big deal.

Discussion:
/// `align`. /// /// If it is not possible to align the pointer, the implementation returns <del> /// `usize::MAX`. It is permissible for the implementation to *always* /// return `usize::MAX`. Only your algorithm's performance can depend /// on getting a usable offset here, not its correctness. </del> <ins> /// `usize::MAX`. </ins> /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be /// used with the `wrapping_add` method.

/// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// <ins> /// When this is called during compile-time evaluation (which is unstable), the implementation /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the /// actual alignment of pointers is not known yet during compile-time, so an offset with /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet /// known, so the execution has to be correct for either choice. It is therefore impossible to /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual /// for unstable APIs.) /// </ins> /// # Panics /// /// The function panics if `align` is not a power-of-two.

/// maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle <del> /// slice of a new type, and the suffix slice. How exactly the slice is split up is not /// specified; the middle part may be smaller than necessary. However, if this fails to return a /// maximal middle part, that is because code is running in a context where performance does not /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running /// in a default (debug or release) execution *will* return a maximal middle part. </del> <ins> /// slice of a new type, and the suffix slice. The middle part will be as big as possible under /// the given alignment constraint and element size. </ins> /// /// This method has no purpose when either input element `T` or output element `U` are /// zero-sized and will return the original slice without splitting anything.

/// types is maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle <del> /// slice of a new type, and the suffix slice. How exactly the slice is split up is not /// specified; the middle part may be smaller than necessary. However, if this fails to return a /// maximal middle part, that is because code is running in a context where performance does not /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running /// in a default (debug or release) execution *will* return a maximal middle part. </del> <ins> /// slice of a new type, and the suffix slice. The middle part will be as big as possible under /// the given alignment constraint and element size. </ins> /// /// This method has no purpose when either input element `T` or output element `U` are /// zero-sized and will return the original slice without splitting anything.