Skip to content

Commit 620083a

Browse files
committed
refactoring: move const_to_pat code into its own submodule.
1 parent 4f7b922 commit 620083a

File tree

2 files changed

+217
-205
lines changed

2 files changed

+217
-205
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
use crate::const_eval::const_variant_index;
2+
3+
use rustc::hir;
4+
use rustc::lint;
5+
use rustc::mir::Field;
6+
use rustc::traits::{ObligationCause, PredicateObligation};
7+
use rustc::ty;
8+
9+
use rustc_index::vec::Idx;
10+
11+
use syntax::symbol::sym;
12+
use syntax_pos::Span;
13+
14+
use super::{FieldPat, Pat, PatCtxt, PatKind};
15+
16+
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
17+
/// Converts an evaluated constant to a pattern (if possible).
18+
/// This means aggregate values (like structs and enums) are converted
19+
/// to a pattern that matches the value (as if you'd compared via structural equality).
20+
pub(super) fn const_to_pat(
21+
&self,
22+
instance: ty::Instance<'tcx>,
23+
cv: &'tcx ty::Const<'tcx>,
24+
id: hir::HirId,
25+
span: Span,
26+
) -> Pat<'tcx> {
27+
// This method is just a warpper handling a validity check; the heavy lifting is
28+
// performed by the recursive const_to_pat_inner method, which is not meant to be
29+
// invoked except by this method.
30+
//
31+
// once indirect_structural_match is a full fledged error, this
32+
// level of indirection can be eliminated
33+
34+
debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
35+
debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
36+
37+
let mut saw_error = false;
38+
let inlined_const_as_pat = self.const_to_pat_inner(instance, cv, id, span, &mut saw_error);
39+
40+
if self.include_lint_checks && !saw_error {
41+
// If we were able to successfully convert the const to some pat, double-check
42+
// that the type of the const obeys `#[structural_match]` constraint.
43+
if let Some(non_sm_ty) = ty::search_for_structural_match_violation(self.tcx, cv.ty) {
44+
let msg = match non_sm_ty {
45+
ty::NonStructuralMatchTy::Adt(adt_def) => {
46+
let path = self.tcx.def_path_str(adt_def.did);
47+
format!(
48+
"to use a constant of type `{}` in a pattern, \
49+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
50+
path,
51+
path,
52+
)
53+
}
54+
ty::NonStructuralMatchTy::Param => {
55+
bug!("use of constant whose type is a parameter inside a pattern");
56+
}
57+
};
58+
59+
// before issuing lint, double-check there even *is* a
60+
// semantic PartialEq for us to dispatch to.
61+
//
62+
// (If there isn't, then we can safely issue a hard
63+
// error, because that's never worked, due to compiler
64+
// using PartialEq::eq in this scenario in the past.)
65+
66+
let ty_is_partial_eq: bool = {
67+
let partial_eq_trait_id = self.tcx.lang_items().eq_trait().unwrap();
68+
let obligation: PredicateObligation<'_> =
69+
self.tcx.predicate_for_trait_def(self.param_env,
70+
ObligationCause::misc(span, id),
71+
partial_eq_trait_id,
72+
0,
73+
cv.ty,
74+
&[]);
75+
self.tcx
76+
.infer_ctxt()
77+
.enter(|infcx| infcx.predicate_may_hold(&obligation))
78+
};
79+
80+
if !ty_is_partial_eq {
81+
// span_fatal avoids ICE from resolution of non-existent method (rare case).
82+
self.tcx.sess.span_fatal(span, &msg);
83+
} else {
84+
self.tcx.lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, &msg);
85+
}
86+
}
87+
}
88+
89+
inlined_const_as_pat
90+
}
91+
92+
/// Recursive helper for `const_to_pat`; invoke that (instead of calling this directly).
93+
fn const_to_pat_inner(
94+
&self,
95+
instance: ty::Instance<'tcx>,
96+
cv: &'tcx ty::Const<'tcx>,
97+
id: hir::HirId,
98+
span: Span,
99+
// This tracks if we signal some hard error for a given const
100+
// value, so that we will not subsequently issue an irrelevant
101+
// lint for the same const value.
102+
saw_const_match_error: &mut bool,
103+
) -> Pat<'tcx> {
104+
105+
let mut adt_subpattern = |i, variant_opt| {
106+
let field = Field::new(i);
107+
let val = crate::const_eval::const_field(
108+
self.tcx, self.param_env, variant_opt, field, cv
109+
);
110+
self.const_to_pat_inner(instance, val, id, span, saw_const_match_error)
111+
};
112+
let mut adt_subpatterns = |n, variant_opt| {
113+
(0..n).map(|i| {
114+
let field = Field::new(i);
115+
FieldPat {
116+
field,
117+
pattern: adt_subpattern(i, variant_opt),
118+
}
119+
}).collect::<Vec<_>>()
120+
};
121+
122+
123+
let kind = match cv.ty.kind {
124+
ty::Float(_) => {
125+
self.tcx.lint_hir(
126+
::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
127+
id,
128+
span,
129+
"floating-point types cannot be used in patterns",
130+
);
131+
PatKind::Constant {
132+
value: cv,
133+
}
134+
}
135+
ty::Adt(adt_def, _) if adt_def.is_union() => {
136+
// Matching on union fields is unsafe, we can't hide it in constants
137+
*saw_const_match_error = true;
138+
self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
139+
PatKind::Wild
140+
}
141+
// keep old code until future-compat upgraded to errors.
142+
ty::Adt(adt_def, _) if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
143+
let path = self.tcx.def_path_str(adt_def.did);
144+
let msg = format!(
145+
"to use a constant of type `{}` in a pattern, \
146+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
147+
path,
148+
path,
149+
);
150+
*saw_const_match_error = true;
151+
self.tcx.sess.span_err(span, &msg);
152+
PatKind::Wild
153+
}
154+
// keep old code until future-compat upgraded to errors.
155+
ty::Ref(_, ty::TyS { kind: ty::Adt(adt_def, _), .. }, _)
156+
if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
157+
// HACK(estebank): Side-step ICE #53708, but anything other than erroring here
158+
// would be wrong. Returnging `PatKind::Wild` is not technically correct.
159+
let path = self.tcx.def_path_str(adt_def.did);
160+
let msg = format!(
161+
"to use a constant of type `{}` in a pattern, \
162+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
163+
path,
164+
path,
165+
);
166+
*saw_const_match_error = true;
167+
self.tcx.sess.span_err(span, &msg);
168+
PatKind::Wild
169+
}
170+
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
171+
let variant_index = const_variant_index(self.tcx, self.param_env, cv);
172+
let subpatterns = adt_subpatterns(
173+
adt_def.variants[variant_index].fields.len(),
174+
Some(variant_index),
175+
);
176+
PatKind::Variant {
177+
adt_def,
178+
substs,
179+
variant_index,
180+
subpatterns,
181+
}
182+
}
183+
ty::Adt(adt_def, _) => {
184+
let struct_var = adt_def.non_enum_variant();
185+
PatKind::Leaf {
186+
subpatterns: adt_subpatterns(struct_var.fields.len(), None),
187+
}
188+
}
189+
ty::Tuple(fields) => {
190+
PatKind::Leaf {
191+
subpatterns: adt_subpatterns(fields.len(), None),
192+
}
193+
}
194+
ty::Array(_, n) => {
195+
PatKind::Array {
196+
prefix: (0..n.eval_usize(self.tcx, self.param_env))
197+
.map(|i| adt_subpattern(i as usize, None))
198+
.collect(),
199+
slice: None,
200+
suffix: Vec::new(),
201+
}
202+
}
203+
_ => {
204+
PatKind::Constant {
205+
value: cv,
206+
}
207+
}
208+
};
209+
210+
Pat {
211+
span,
212+
ty: cv.ty,
213+
kind: Box::new(kind),
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)