Skip to content
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

Making enums more specific in src/librustc/mir/interpret/error.rs #4

Closed
4 tasks done
TheDarkula opened this issue Aug 19, 2018 · 43 comments · Fixed by rust-lang/rust#69839
Closed
4 tasks done

Comments

@TheDarkula
Copy link

TheDarkula commented Aug 19, 2018

While chatting with @oli-obk, we came up with this schema for separating the EvalErrorKind enum into two parts.
What's everyone's thoughts on this?


@oli-obk
Copy link
Contributor

oli-obk commented Aug 19, 2018

The reason we talked about this was to have a clear separation between the variants that produce panics when occurring inside a promoted, and the variants that produce an abort (or a compiler-error pending unconst RFCs and such)

@oli-obk
Copy link
Contributor

oli-obk commented Aug 19, 2018

cc @RalfJung @eddyb you might have opinions on this. We could alternatively just have a method that decides which variants report what, but it seems more fragile wrt future additions

@eddyb
Copy link
Member

eddyb commented Aug 20, 2018

I like the enum split.

@RalfJung
Copy link
Member

Related miri issue: rust-lang/miri#417

So if we are touching errors at all, I would like to also separate "operation not implemented" from "your program just caused UB". So I imagine a top-level enum with three variants (unimplemented, panic, UB), and then sub-enums (Unimplemented can likely directly be a string like it is now).

Furthermore, I'd like to reevaluate the use of having so many enum variants. Many of them miss information that would be very useful to have when an error occurs, e.g. ReadBytesAsPointer does not say which bytes. Is it really worth having so many different structural errors, or should we give up an use strings more? We already use strings for some things.

Also, EvalErrorPanic::Panic should (optionally?) take a panic string. miri can actually get that string even when formatting is involved.

@eddyb
Copy link
Member

eddyb commented Aug 22, 2018

@RalfJung we need to be really careful with strings: if we don't intern them, we could "leak" memory from things that may not be an user-facing error.
That is, anything that could fail because some generic parameters are not fully specified yet, should be as cheap as possible, because we'll certainly generate tons of them.

@RalfJung
Copy link
Member

I do not fully understand. What would be an example of a problem?

Maybe instead of using strings we can use the failure crate, which makes creating error enums with nice formatting much nicer? :D

@eddyb
Copy link
Member

eddyb commented Aug 22, 2018

@RalfJung The "problem" just increased memory usage, although I doubt it would be significant. Also, does the failure crate give you a hashable enum with no extra dynamic allocations?
If so, it's probably okay to use (I now wonder if we could do this for diagnostics in general).

@oli-obk
Copy link
Contributor

oli-obk commented Aug 22, 2018

Also, EvalErrorPanic::Panic should (optionally?) take a panic string. miri can actually get that string even when formatting is involved.

Already done in rust-lang/rust#52011

@RalfJung
Copy link
Member

Another inconsistency: We have dedicated error variants for invalid bool/char that are used when such an invalid value occurs in a normal operation. But for strings, we use a ValidationError.

@TheDarkula
Copy link
Author

TheDarkula commented Sep 3, 2018

@RalfJung for your "top-level" design, were you thinking something like this:

#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum EvalErrorKind<'tcx, O> {
    Panic(EvalErrorPanic<'tcx, O>),
    UndefinedBehaviour(EvalErrorUndefinedBehaviour<'tcx, O>),
    Unimplemented(EvalErrorUnimplemented<'tcx, O>),
}

With each variant being the nested corresponding enum?

@oli-obk
Copy link
Contributor

oli-obk commented Sep 3, 2018

Is it really worth having so many different structural errors, or should we give up an use strings more? We already use strings for some things.

Since we catch some of these errors and silently do something else, we should not be allocating (and especially not formatting) strings.

Any truly terminal error variant could be merged into a string error variant though. Maybe really just ValidationError.

@RalfJung
Copy link
Member

RalfJung commented Sep 17, 2018

@TheDarkula yes, that looks great! I hope only the EvalErrorPanic will even need the O though -- AssertMessage should be just an EvalErrorPanic, I think.

Since we catch some of these errors and silently do something else, we should not be allocating (and especially not formatting) strings.

Yeah I guess we'll have to see in each of these variants where strings are appropriate. I think for EvalErrorUnimplemented there is no reason for anything but a string.


Now, one thing I am not sure where to put is when an endless loop was detected. That's not UB and not unimplemented, and not really a panic either. What do you think?

@TheDarkula
Copy link
Author

Now, one thing I am not sure where to put is when an endless loop was detected. That's not UB and not unimplemented, and not really a panic either. What do you think?
Do you just want a fourth sub-enum like:

#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum EvalErrorKind<'tcx, O> {
    Panic(EvalErrorPanic<'tcx, O>),
    UndefinedBehaviour(EvalErrorUndefinedBehaviour<'tcx, O>),
    Unimplemented(EvalErrorUnimplemented<'tcx, O>),
    InfiniteLoop(String),
}

@RalfJung
Copy link
Member

That would be the other option.

@TheDarkula
Copy link
Author

What other idea(s) did you have in mind?

@RalfJung
Copy link
Member

Putting it into one of the other three.^^

@TheDarkula
Copy link
Author

Ah. I thought it was a distinct enough condition to be separate. Which one do you think it would fit best into?

@RalfJung
Copy link
Member

Well I don't know, that's the point. ;)

@eddyb
Copy link
Member

eddyb commented Sep 18, 2018

Isn't infinite loop a resource exhaustion problem? Where are the other resource limits enforced?

@RalfJung
Copy link
Member

I suppose that would make a good 4th category: Resource exhaustion. We also have a stack size limit and, uh, we used to have a memory limit but it seems that was removed.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 18, 2018

The stack size limit is a poor man's infinite recursion detector. we could just as well do the infinite recursion check the same way we do the infinite loop check, the same arguments to a function will always yield the same result if the memory is the same otherwise. In the miri engine that is. miri itself has mutable statics and env vars which complicate these things

@saleemjaffer
Copy link

The reason we talked about this was to have a clear separation between the variants that produce panics when occurring inside a promoted, and the variants that produce an abort (or a compiler-error pending unconst RFCs and such)

@oli-obk Could you explain this a bit?

@RalfJung
Copy link
Member

(FWIW the very related rust-lang/miri#417 made its way very high up my TODO list, I planned to tackle it on the week-end.)

But I actually also wonder what @oli-obk meant there.^^

@oli-obk
Copy link
Contributor

oli-obk commented Jul 18, 2019

Well, anythin like a panic, index out of bound or arithmetic overflow is allowed inside promoteds, as that's just a panic that can continue. If we encounter something unpromotable at evaluation time, we screwed up, but should not produce UB.

I believe we have cleaned up promotion well enough now that this isn't a problem anymore, so... Ignore that comment ;)

Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this issue Jul 23, 2019
… r=oli-obk

more specific errors in src/librustc/mir/interpret/error.rs

Implements [this](rust-lang/const-eval#4)
@RalfJung
Copy link
Member

@saleemjaffer when you make a PR for an issue (like rust-lang/rust#60951), please mention that in the issue. (The backlinks GH auto-inserts do not help as they do not trigger email notifications.)

It is pure luck that I didn't have time last weekend to start working on the error messages, which would have had bad conflicts with your PR.

@saleemjaffer
Copy link

@RalfJung Yeah, will take care of this.

@RalfJung
Copy link
Member

RalfJung commented Jul 24, 2019

@saleemjaffer you mean you want to continue working on this issue?

That's great! However, some of it isn't just plain refactoring, it is about correctly classifying UB vs "unsupported"-style errors. And we'll probably have to review every single error site in librustc/mir/interpret and librustc_mir/interpret to make sure we do not missclassify anything.

So I think it would be worth talking here about how you intend to split InterpError into several sub-enums, before you spend a lot of time doing that.

(Or maybe I misinterpreted what you said. I have no idea what the "this" is you want to take care of.)

@RalfJung
Copy link
Member

Cc rust-lang/rust#62927

@saleemjaffer
Copy link

saleemjaffer commented Jul 24, 2019

By "this" I meant

@saleemjaffer when you make a PR for an issue (like rust-lang/rust#60951), please mention that in the issue. (The backlinks GH auto-inserts do not help as they do not trigger email notifications.)

I was also working on the same PanicMessage thing :D

But I can still continue to work on "UB vs "unsupported"-style errors".

@saleemjaffer
Copy link

@RalfJung

when you make a PR for an issue (like rust-lang/rust#60951), please mention that in the issue. (The backlinks GH auto-inserts do not help as they do not trigger email notifications.)

This should definitely go into the contributing guide in the rustdoc! What do you think?

@RalfJung
Copy link
Member

RalfJung commented Jul 24, 2019

But I can still continue to work on "UB vs "unsupported"-style errors".

Sure, if you want we can do this with mentoring. :)

My proposal for the enum would be

pub enum InterpError<'tcx, O> {
    /// The program panicked.
    Panic(PanicMessage<'tcx, O>),
    /// The program caused undefined behavior.
    UndefinedBehaviour(UndefinedBehaviourMessage<'tcx>),
    /// The program did something the interpreter does not support (some of these *might* be UB but the interpreter is not sure).
    Unsupported(UnsupportedMessage<'tcx>),
    /// The program was invalid (ill-typed, not sufficiently monomorphized, ...).
    InvalidProgram(InvalidProgramMessage<'tcx>),
    /// The program exhausted the interpreter's resources (stack/heap too big, execution takes too long, ..).
    ResourceExhaustion(ResourceExhaustionMessage<'tcx>),
}

Here, ResourceExhaustion is for the infinite-loop error (so ResourceExhaustionMessage is likely an alias for String), and InvalidProgram is for TooGeneric, ReferencedConstant and (if that is even still used anywhere) TypeckError. @oli-obk does that seem reasonable?

The interesting ones would be UndefinedBehaviourMessage and UnimplementedMessage, which share what is currently in InterpError. However, we likely do not need all those variants; having a variant is only necessary if (a) the error is ever caught (then we want to create it without having to allocate), or (b) if the error is raised in multiple places (and we want to keep the error message consistent). The other cases can just become a generic String variant (like the current Unimplemented). But remember that Miri also uses this error type!

OTOH, we do not have to arrive at the perfect final state in one step. If in doubt, an error should be considered "unsupported" rather than "UB"; then we can slowly go over the "unsupported" errors and figured out which of those are really UB. Oh and please base your work on top of rust-lang/rust#62927, or wait until that lands.

This should definitely go into the contributing guide in the rustdoc! What do you think?

That seems like a good idea!

@RalfJung
Copy link
Member

Note that I am not entirely sure about that Message suffix. Error would make more sense for most of them, but PanicMessage already exists and PanicError is weird.

Centril added a commit to Centril/rust that referenced this issue Jul 24, 2019
@saleemjaffer
Copy link

saleemjaffer commented Jul 25, 2019

@RalfJung @oli-obk I have started to work refactoring InterpError here

@eddyb
Copy link
Member

eddyb commented Jul 25, 2019

Couple ideas about the suffix:

  • PanicDetails, UndefinedBehaviorDetails
  • PanicInfo, UndefinedBehaviorInfo

@RalfJung
Copy link
Member

I think I like info. It is also fairly short.

@oli-obk @saleemjaffer what do you think?

@oli-obk
Copy link
Contributor

oli-obk commented Jul 25, 2019

*Info sgtm

@saleemjaffer
Copy link

@RalfJung @oli-obk @eddyb I have moved some enum variants to where I felt they are appropriate. Haven't implemented anything though. Probably you guys can give your inputs here

Centril added a commit to Centril/rust that referenced this issue Aug 2, 2019
… r=RalfJung

Changing the structure of `mir::interpret::InterpError`

Implements [this](rust-lang/const-eval#4 (comment))
@RalfJung
Copy link
Member

RalfJung commented Aug 3, 2019

One interesting kind of error that comes up in a few places is programs that use lang items or intrinsics the wrong way. What do we want to do for those? We could ICE, call then InvalidProgram, or call them Unsupported. I don't really have a strong preference as long as we are consistent about it.

So far we marked some of them as "ABI violation", which is confusing because I first thought it would be about our ABI checks on function calls. In CTFE, calling a non-const fn is "unsupported" right now, as is doing something that still needs an RFC. Maybe the former should be "invalid" as well? But by original intention for "invalid" was something that at least stable Rust can never cause except when programs get run that have not passed typeck, or things are not monomorphized, like const_prop sometimes does IIRC -- and one day stable Rust might be allowed to transmute function pointers around in CTFE and then it can trigger the "called non-const function" error.

@RalfJung
Copy link
Member

RalfJung commented Aug 3, 2019

Another question, @oli-obk, about this match here: can / should we make it exhaustive for InvalidProgram? The one variant it currently does not handle is ReferencedConstant. Should it handle that variant? If not, should that variant maybe move elsewhere?

@oli-obk
Copy link
Contributor

oli-obk commented Aug 3, 2019

Lang item or intrinsic abi or api misuse ICEs the compiler, so go right ahead with doing the same

ReferencedConstant could be moved elsewhere, but it's unclear whether all of these variants should be silent. There's some work in rust-lang/rust#63152 but it's entirely unclear to me how yo fix all that nicely

@RalfJung
Copy link
Member

RalfJung commented Aug 3, 2019

Lang item or intrinsic abi or api misuse ICEs the compiler, so go right ahead with doing the same

All right.

ReferencedConstant could be moved elsewhere, but it's unclear whether all of these variants should be silent. There's some work in rust-lang/rust#63152 but it's entirely unclear to me how yo fix all that nicely

I am not sure which "elsewhere" would fit though.
But I suppose for now I will wait how that issue gets resolved.

@RalfJung
Copy link
Member

Actually, now that Miri supports panics properly, the "panic" variants of these errors do not actually make any sense any more. A panic not an "interpreter error condition". So I think we should get rid of it.

But see rust-lang/rust#66874 (comment) for why const_prop makes that non-trivial.

Manishearth added a commit to Manishearth/rust that referenced this issue Mar 17, 2020
Miri error reform

Some time ago we started moving Miri errors into a few distinct categories, but we never classified all the old errors. That's what this PR does.

~~This is on top of rust-lang#69762; [relative diff](RalfJung/rust@validity-errors...RalfJung:miri-error-cleanup).~~

r? @oli-obk

Fixes rust-lang/const-eval#4
Centril added a commit to Centril/rust that referenced this issue Mar 18, 2020
Miri error reform

Some time ago we started moving Miri errors into a few distinct categories, but we never classified all the old errors. That's what this PR does.

~~This is on top of rust-lang#69762; [relative diff](RalfJung/rust@validity-errors...RalfJung:miri-error-cleanup).~~

r? @oli-obk

Fixes rust-lang/const-eval#4
Centril added a commit to Centril/rust that referenced this issue Mar 18, 2020
Miri error reform

Some time ago we started moving Miri errors into a few distinct categories, but we never classified all the old errors. That's what this PR does.

~~This is on top of rust-lang#69762; [relative diff](RalfJung/rust@validity-errors...RalfJung:miri-error-cleanup).~~

r? @oli-obk

Fixes rust-lang/const-eval#4
Centril added a commit to Centril/rust that referenced this issue Mar 18, 2020
Miri error reform

Some time ago we started moving Miri errors into a few distinct categories, but we never classified all the old errors. That's what this PR does.

~~This is on top of rust-lang#69762; [relative diff](RalfJung/rust@validity-errors...RalfJung:miri-error-cleanup).~~

r? @oli-obk

Fixes rust-lang/const-eval#4
@RalfJung
Copy link
Member

I think this can be closed now, the boxes are all checked and I am mostly happy with our error enum now. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants