Skip to content

[TEST] Don't allow constant eval to access generator layouts #115359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

Zoxc
Copy link
Contributor

@Zoxc Zoxc commented Aug 29, 2023

This emits an error if generator layouts are observed by the frontend via constant evaluation. This ensures that the frontend does not depend on optimizations done later in MIR. This is a breaking change as generator layouts can be observed via the async feature combined with constant evaluation.

r? @oli-obk

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 29, 2023
@rustbot
Copy link
Collaborator

rustbot commented Aug 29, 2023

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

@rustbot
Copy link
Collaborator

rustbot commented Aug 29, 2023

The Miri subtree was changed

cc @rust-lang/miri

@@ -357,6 +357,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,

const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error

// We don't allow access to generator layout at compile time.
const ACCESS_GENERATOR_LAYOUT: bool = false;
Copy link
Member

Choose a reason for hiding this comment

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

Please remove the default value here, and move it to the compile_time_machine macro below. After all this is also shared with Miri, so we don't set defaults here based on "being compile-time".


// Ensure we don't need the layout of any generators if applicable.
// This prevents typeck from depending on MIR optimizations.
self.validate_generator_layout_access(ty)?;
Copy link
Member

Choose a reason for hiding this comment

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

Why is this the one place where the layout is checked? That seems odd, aren't there tons of ways the layout could become relevant?


/// This function checks if the given `ty`'s layout depends on generators.
#[inline(always)]
pub fn validate_generator_layout_access(&self, ty: Ty<'tcx>) -> InterpResult<'tcx> {
Copy link
Member

Choose a reason for hiding this comment

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

"validity.rs" is for checking that a value is valid. Please don't put unrelated functions here.

@@ -404,6 +404,7 @@ pub enum ValidationErrorKind<'tcx> {
InvalidBool { value: String },
InvalidChar { value: String },
InvalidFnPtr { value: String },
GeneratorLayoutAccess { ty: Ty<'tcx>, generators: &'tcx List<Ty<'tcx>> },
Copy link
Member

Choose a reason for hiding this comment

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

"ValidationErrorKind" is for when a value doesn't satisfy its validity invariant. This is unrelated, please don't put that here.

Sound more like an InvalidProgram error to me. Or ideally the error variant is only added in const_eval::error, so we structurally know it will not occur in Miri.

.allocation_spans
.borrow_mut()
.insert(alloc_id, (span, None));
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
Copy link
Member

Choose a reason for hiding this comment

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

Please don't format these files, they'll have to be reformatted when syncing changes back to Miri. (Miri has a different rustfmt.toml.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why doesn't rustfmt use those settings? :/

Copy link
Member

@RalfJung RalfJung Aug 31, 2023

Choose a reason for hiding this comment

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

I think it's up to RA to tell it the config file to use (rustfmt isn't invoked on a file, it is fed data via stdin and returned via stdout). Not sure if there's an open RA bug for this.

EDIT: rust-lang/rust-analyzer#15540

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like RA changes the current directory to match the file for rustfmt. I guess that doesn't work out.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 30, 2023

This is a breaking change as generator layouts can be observed via the async feature combined with constant evaluation.

do you have an example snippet that compiles on stable but doesn't compile anymore?

@Zoxc
Copy link
Contributor Author

Zoxc commented Aug 30, 2023

As @RalfJung pointed out, this isn't the only place layout is used. It would have apply more generally to all allocations, effectively banning async and generator blocks from const eval, at least while allocations are byte based. That's not a particularly great outcome.

@oli-obk I'll relay your example back to you:

const X: usize = {
    let x = Thing(|x: u128| async move {x});
    x.size()
};

struct Thing<T>(T);

impl<U, T: Fn(u128) -> U> Thing<T> {
    const fn size(&self) -> usize {
        std::mem::size_of::<U>()
    }
}

@oli-obk
Copy link
Contributor

oli-obk commented Aug 30, 2023

Oh whoops. I remember this.

Yea, this seems very annoying to prevent. I forget where the original discussion was. Why did we consider avoiding getting generator layouts during const eval? Avoiding some cycle errors?

@Zoxc
Copy link
Contributor Author

Zoxc commented Aug 30, 2023

Here's the discussion on Zulip.

The problem is that having const eval depend on generator layouts places constraints on optimization. We can't have an out of process LTO optimization of generator layouts for example. Just partially optimizing generator layouts within the query system gets quite hairy. I suspect there's some interesting interactions with monomorphization too.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 30, 2023

Thanks! I reread the thread. Splitting Reveal::All into Optimizations and Codegen indeed seems like the only solution to do this properly. We can probably get away with letting const eval always use Codegen for its Allocations, but forward its real Reveal otherwise. layout_of with Reveal::Optimizations or UserFacing would then error in case it tries to compute the layout of a generator

@RalfJung
Copy link
Member

Inevitably at some point people will ask to execute generators at compiletime. What do we do then?

I don't think doing LTO on pre-lowering generators is a realistic option.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 1, 2023

The moment offset_of works on stable in const eval, I don't see how we could possibly hide the actual size of generators. Without offset_of, we can always do some shenanigans where the size of a generator is treated as something opaque just like pointers. That would be a lot of complexity, but it would be possible. Since this will break down once we have offset_of, there's no real point to it though.

the new Reveal mode would resolve the issue raised in the zulip, as all it would do is make optimizations before the generator lowering pass stop being able to rely on the size of generators (without causing errors, just by returning something akin to TooGeneric, which all optimizations must already silently handle)

@RalfJung
Copy link
Member

RalfJung commented Sep 1, 2023

https://github.com/Gilnaa/memoffset already works in const-eval when there is no interior mutability.

@bors
Copy link
Collaborator

bors commented Sep 13, 2023

☔ The latest upstream changes (presumably #115797) made this pull request unmergeable. Please resolve the merge conflicts.

@apiraino
Copy link
Contributor

apiraino commented Oct 5, 2023

hey, checking progress. I'm unsure about the status of this PR (besides a few outstanding comments and a rebase). @Zoxc do you have any update? Are you specifically waiting on a feedback? Thanks!

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 5, 2023
@Zoxc
Copy link
Contributor Author

Zoxc commented Oct 7, 2023

@oli-obk The problem with that approach is that we end up having that reveal option on anything that could use generator sizes, including type_of and type checking.

I don't think the method in this PR is a suitable solution however, so I'm going to close it.

@Zoxc Zoxc closed this Oct 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants