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

Fix ICE with unsized type in const pattern #87065

Merged
merged 1 commit into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,17 +490,29 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
// convert the dereferenced constant to a pattern that is the sub-pattern of the
// deref pattern.
_ => {
let old = self.behind_reference.replace(true);
// In case there are structural-match violations somewhere in this subpattern,
// we fall back to a const pattern. If we do not do this, we may end up with
// a !structural-match constant that is not of reference type, which makes it
// very hard to invoke `PartialEq::eq` on it as a fallback.
let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
Ok(subpattern) => PatKind::Deref { subpattern },
Err(_) => PatKind::Constant { value: cv },
};
self.behind_reference.set(old);
val
if !pointee_ty.is_sized(tcx.at(span), param_env) {
Copy link
Contributor

Choose a reason for hiding this comment

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

that certainly avoids the ICE, but we could also make such patterns legal. I think we should. @rust-lang/wg-const-eval what do you think about these?

Copy link
Member

Choose a reason for hiding this comment

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

This always does span_err or delay_span_bug... how does this make anything legal?

Copy link
Contributor

@oli-obk oli-obk Jul 11, 2021

Choose a reason for hiding this comment

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

I said "we could ...", I'm fine doing this PR, it changes an ICE to an error, but why should the code in the new test not be permitted? It seems perfectly alright to me

Copy link
Member

Choose a reason for hiding this comment

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

I'm not familiar enough with pattern matching code to comment on this.

But I'd prefer we port that code to valtrees first, then I will be a lot more confident that what that code does makes sense in my mental model of rustc. :)

Copy link

Choose a reason for hiding this comment

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

@oli-obk What would be a good way to direct attention to this? I would find it incredibly useful to be able to use newtypes around str as const patterns.

Copy link
Contributor

Choose a reason for hiding this comment

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

Valtrees are being worked on right now, once we have them, this becomes trivial to do and we can ask T-lang whether they think this should br supported, too

Copy link

Choose a reason for hiding this comment

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

Cool thanks

// `tcx.deref_const()` below will ICE with an unsized type
// (except slices, which are handled in a separate arm above).
let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
// In case there are structural-match violations somewhere in this subpattern,
// we fall back to a const pattern. If we do not do this, we may end up with
// a !structural-match constant that is not of reference type, which makes it
// very hard to invoke `PartialEq::eq` on it as a fallback.
let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
Ok(subpattern) => PatKind::Deref { subpattern },
Err(_) => PatKind::Constant { value: cv },
};
self.behind_reference.set(old);
val
}
}
},
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
Expand Down
34 changes: 34 additions & 0 deletions src/test/ui/consts/issue-87046.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Regression test for the ICE described in #87046.

#![crate_type="lib"]
#![allow(unreachable_patterns)]
#![feature(const_fn_union)]

#[derive(PartialEq, Eq)]
#[repr(transparent)]
pub struct Username(str);

pub const ROOT_USER: &Username = Username::from_str("root");

impl Username {
pub const fn from_str(raw: &str) -> &Self {
union Transmute<'a> {
raw: &'a str,
typed: &'a Username,
}

unsafe { Transmute { raw }.typed }
}

pub const fn as_str(&self) -> &str {
&self.0
}

pub fn is_root(&self) -> bool {
match self {
ROOT_USER => true,
//~^ ERROR: cannot use unsized non-slice type `Username` in constant patterns
_ => false,
}
}
}
8 changes: 8 additions & 0 deletions src/test/ui/consts/issue-87046.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: cannot use unsized non-slice type `Username` in constant patterns
--> $DIR/issue-87046.rs:29:13
|
LL | ROOT_USER => true,
| ^^^^^^^^^

error: aborting due to previous error