profile
viewpoint
Eduard-Mihai Burtescu eddyb @LykenSol Bucharest, Romania they/them

eddyb/cprep 15

C/C++ preprocessor.

Centril/rfc-effects 9

Preparing an RFC on effect polymorphism in Rust with focus on 'const'

eddyb/glOOF 5

glOOF: an OpenGL implementation experiment

eddyb/aoc 2

Advent of Code solutions in Rust

eddyb/bootstrap 2

HTML, CSS, and JS toolkit from Twitter

eddyb/hematite 2

A simple Minecraft written in Rust with the Piston game engine

eddyb/1ml 0

1ML prototype interpreter

eddyb/cargo 0

The Rust package manager

issue commentrust-lang/rust

Presence of `std::simd` sometimes breaks type inference

Sorry, I forgot to look at the original example again. With this change, the error appears on stable:

- (None, Some(val)) | (Some(val), None) => source + val,
+ (None, Some(&val)) | (Some(&val), None) => source + val,

(or alternatively, dereferencing the val reference, i.e.: => source + *val)

So it's the same situation where (in this case) i32: Add<&$1> resolves to $1 = i32 because there's no other reference type you can add to an i32, on stable. The moment that changes, it goes out of the window.

<hr/>

Though the error is very misleading, because it points to the wrong part of the code that is affected - this example has enough information flowing through from source to target to infer the element types of both Vecs.

What seems to be breaking is the left.min(right) method call, which being the method call that it is, has to know the types at that moment - rewriting it to std::cmp::Ord::min(left, right) makes it compile again. So there's a diagnostic bug there, IMO.

This is a pretty "compact" leap - the addition in one match arm makes a method call in another resolve. You can see that swapping the match arms produces the same (confusing) error again, on stable.

EFanZh

comment created time in a day

issue commentrust-lang/rust

Presence of `std::simd` sometimes breaks type inference

Cross-posting another minimal example from #91378, which was bisected to #89167 as well.

This minimized code example:

If we allow a change of the trait involved (<sup>and ignore the weird () inference result - that's either an interaction with the ? operator or an unrelated bug</sup>), the full reduction looks like this:

fn main() {
    0usize + &Default::default();
}

Compiles on stable, ambiguous on nightly, due to the new impls allowing for multiple solutions to the usize: Add<&_> requirement.

The & is needed for it to compile at all, otherwise it was always ambiguous (or at least as far back as 1.0).

There's nothing that can be done here at the level of the operator overload impls themselves: a completely unknown type will not be narrowed now, whereas before it happened to be.

Maybe we shouldn't have let any of these examples compile before, but sadly I'm not aware of a heuristic that can tell "dangerous" cases of inference from "harmless" ones.

EFanZh

comment created time in a day

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentEmbarkStudios/rust-gpu

Generate bools as bools instead of u8

 pub enum Sampled {     No = 2, } -#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]+#[cfg(not(target_arch = "spirv"))]

but now is not usable on GPU at all.

Correct, Option<bool> breaks with this PR, because it expects bool to be an u8 - even if we forcefully disable the niche, it still turns the bool into u8 as part of a scalar pair (the same (is_some: bool, MaybeUninit<T>)-like scalar pair representation of Option<T> which has allowed for i in 0..n to work, so we can't just mess with it in general, yet).

khyperia

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentEmbarkStudios/rust-gpu

Generate bools as bools instead of u8

 fn has_two_decimal_digits(x: u32) -> bool { }  #[spirv(fragment)]-pub fn main(i: u32, o: &mut bool) {-    *o = has_two_decimal_digits(i);+pub fn main(i: u32, o: &mut u32) {+    *o = if has_two_decimal_digits(i) { 1 } else { 0 };

Hmm I wonder if as u32 works.

khyperia

comment created time in 2 days

PullRequestReviewEvent

Pull request review commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {             | ty::InstanceDef::CloneShim(..)             | ty::InstanceDef::Item(_) => {                 // We need MIR for this fn-                let body =+                let (body, instance) =                     match M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? {                         Some(body) => body,                         None => return Ok(()),                     }; -                // Check against the ABI of the MIR body we are calling (not the ABI of `instance`;-                // these can differ when `find_mir_or_eval_fn` does something clever like resolve-                // exported symbol names).-                let callee_def_id = body.source.def_id();-                let callee_abi = get_abi(self, self.tcx.type_of(callee_def_id));+                // Compute callee information using the `instance` returned by+                // `find_mir_or_eval_fn`.+                let callee_abi = get_abi(self, instance.ty(*self.tcx, self.param_env));+                // FIXME: for variadic support, do we have to somehow determine calle's extra_args?

You would use the exact same extra_args types for both caller and callee, as determined from the callsite. If you just want to pass the caller_fn_abi to this point, as the sole source of truth, you should be able to recover the types from caller_fn_abi.args[caller_fn_abi.fixed_count..].iter().map(|arg| arg.layout.ty).

RalfJung

comment created time in 4 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {          let get_abi = |this: &Self, instance_ty: Ty<'tcx>| match instance_ty.kind() {

You may still want to emit a warning for "Rust" vs "non-Rust" - even when compatible by happenstance, relying on it is probably a bad idea.

RalfJung

comment created time in 4 days

PullRequestReviewEvent

Pull request review commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {          let get_abi = |this: &Self, instance_ty: Ty<'tcx>| match instance_ty.kind() {

That sounds right - also, FWIW, most of the time, the "calling convention" won't even be anything other than "default for C on that platform", since the extern "Rust" and extern "C" differences are all encoded in how arguments/returns are passed around.

RalfJung

comment created time in 4 days

Pull request review commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {     }      fn check_argument_compat(-        rust_abi: bool,-        caller: TyAndLayout<'tcx>,-        callee: TyAndLayout<'tcx>,+        caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,+        callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,     ) -> bool {-        if caller.ty == callee.ty {-            // No question+        // Heuristic for type comparison.+        let layout_compat = || {+            if caller_abi.layout.ty == callee_abi.layout.ty {+                // No question+                return true;+            }+            // Compare layout+            match (caller_abi.layout.abi, callee_abi.layout.abi) {+                // Different valid ranges are okay (once we enforce validity,+                // that will take care to make it UB to leave the range, just+                // like for transmute).+                (abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {+                    caller.value == callee.value+                }+                (+                    abi::Abi::ScalarPair(caller1, caller2),+                    abi::Abi::ScalarPair(callee1, callee2),+                ) => caller1.value == callee1.value && caller2.value == callee2.value,+                // Be conservative+                _ => false,+            }+        };+        // Padding must be fully equal.+        let pad_compat = || {+            if caller_abi.pad != callee_abi.pad {+                trace!(+                    "check_argument_compat: incompatible pad: {:?} != {:?}",+                    caller_abi.pad,+                    callee_abi.pad+                );+                return false;+            }             return true;-        }-        if !rust_abi {-            // Don't risk anything-            return false;-        }-        // Compare layout-        match (caller.abi, callee.abi) {-            // Different valid ranges are okay (once we enforce validity,-            // that will take care to make it UB to leave the range, just-            // like for transmute).-            (abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => caller.value == callee.value,-            (abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {-                caller1.value == callee1.value && caller2.value == callee2.value+        };+        // For comparing the PassMode, we allow the attributes to differ+        // (e.g., it is okay for NonNull to differ between caller and callee).+        // FIXME: Are there attributes (`call::ArgAttributes`) that do need to be checked?

Most attributes are probably relevant, as they change where arguments are to be passed ("byval" is LLVM's way of indicating a stack copy), or some transformation done to the value, like the zero/sign-extensions.

Off the top of my head, something like fn(i8) and fn(u8) may not be compatible on some architectures, if, say, the body immediately casts the argument to i32, since the widening may actually happen on the caller's side (one of the more annoying ABI details I've come across - on CPUs, at least, GPUs are all of this and worse).

Though, to be fair, the layout_compat check above should disallow signedness changes.

I suppose we should have a higher-level version of attributes before lowering to LLVM - e.g. the NonNull should be generated from the validity ranges in the Layout abi field in rustc_codegen_llvm, and the zero/sign-extension could be dictated by Direct and Pair actually holding register sizes.

RalfJung

comment created time in 4 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOfHelpers<'tcx> for InterpC     } } +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'mir, 'tcx, M> {+    type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>;++    fn handle_fn_abi_err(+        &self,+        err: FnAbiError<'tcx>,+        _span: Span,+        _fn_abi_request: FnAbiRequest<'tcx>,+    ) -> InterpErrorInfo<'tcx> {+        match err {+            FnAbiError::Layout(err) => err_inval!(Layout(err)).into(),+            FnAbiError::AdjustForForeignAbi(err) => err_inval!(FnAbi(err)).into(),

You can turn this into a fatal error - this just means the current --target doesn't support that ABI, which should probably be a typeck error.

RalfJung

comment created time in 4 days

pull request commentrust-lang/rust

CTFE eval_fn_call: use FnAbi to determine argument skipping and compatibility

(The fact that every backend has to separately implement support for these arguments seems suboptimal -- looks like this might have been better implemented on the MIR level.)

We can't, because whether to pass the argument or not can vary across a trait's impls, so from the caller's perspective it's undecided until monomorphization.

RalfJung

comment created time in 4 days

delete branch LykenSol/rust-gpu

delete branch : layout-hide-niche

delete time in 6 days

push eventLykenSol/rust-gpu

Eduard-Mihai Burtescu

commit sha fe1696611b273cc9ba70aecdf498237240120ee1

abi: demonstrate overriding the behavior of the `layout_of` query.

view details

push time in 7 days

PR opened EmbarkStudios/rust-gpu

abi: demonstrate overriding the behavior of the `layout_of` query.

In particular, hiding the niche of a type (i.e. replacing the largest_niche field of Layout with None), would be relatively easy with this PR (only the predicate needs to be added).

This has come up as potentially useful, and no matter what we need to tweak wrt how layouts are computed, having the scaffolding around will be helpful (frankly, the whole clone_layout thing could've been entirely avoided upstream and then this PR would be 5 times smaller - but this is faster than waiting for a fix upstream).

+76 -1

0 comment

1 changed file

pr created time in 7 days

create barnchLykenSol/rust-gpu

branch : layout-hide-niche

created branch time in 7 days

delete branch LykenSol/rust-gpu

delete branch : infer-access-chain-slice

delete time in 10 days

issue commentEmbarkStudios/rust-gpu

Fails to compile `core::mem::swap` that is scceeded previously

In #716 we updated to a version that supports mem::replace again, thanks to https://github.com/rust-lang/rust/pull/87827, but mem::swap indeed did not get fixed the same way - if someone wants to try and land upstream something like that PR but for mem::swap, feel free to.

I gave up on it because of how rare mem::swap is compared to mem::replace (which gets used everywhere in core, e.g. Option::take(self) is mem::replace(self, None) and range iterators also depend on it).

There's a more general fix at https://github.com/rust-lang/rust/pull/86699 but it requires a bunch of unstable feature-gating stuff I haven't gone back to for.

hatoo

comment created time in 10 days

push eventLykenSol/rust-gpu

Eduard-Mihai Burtescu

commit sha 8b651a8ecf00b357f9d726bead81e3e498b8e2cf

asm: support `OpCompositeExtract` inference for `OpTypeStruct` fields.

view details

push time in 12 days

Pull request review commentEmbarkStudios/rust-gpu

Remove unused dependencies in rust-gpu

 use-compiled-tools = ["spirv-builder/use-compiled-tools"] ash = "0.33" ash-window = "0.7" winit = { git = "https://github.com/rust-windowing/winit", rev = "1b3b82a3c1369c5248a2e6a251230ba6c615e918" }-serde = { version = "1.0", features = ["derive"] }-serde_json = "1.0" structopt = "0.3.20" cfg-if = "1.0.0" shared = { path = "../../shaders/shared" } spirv-builder = { path = "../../../crates/spirv-builder", default-features = false } -# TODO: Remove this once no longer needed, only needed to make cargo-deny happy for some reason.-# https://rustsec.org/advisories/RUSTSEC-2021-0119-nix = "0.20.2"-

What makes this possible? I don't understand what else changed to allow removing this.

bnjbvr

comment created time in 12 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentEmbarkStudios/rust-gpu

Improve the calculation of location sizes for arrays and structs (revives #513)

 impl<'tcx> CodegenCx<'tcx> {             }         }     }++    fn location_size_of_type(&self, ty: Word) -> u32 {

Can this be renamed to location_count_of_type or location_slots_per_type? "size_of" could get confused with the size in bytes, which AFAICT is not relevant here.

expenses

comment created time in 12 days

more