Skip to content

Introduce pat_adjustments table #1

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 2 commits into from
Closed
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
35 changes: 34 additions & 1 deletion src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,24 @@ pub struct TypeckTables<'tcx> {

adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,

// Stores the actual binding mode for all instances of hir::BindingAnnotation.
/// Stores the actual binding mode for all instances of hir::BindingAnnotation.
pat_binding_modes: ItemLocalMap<BindingMode>,

/// Stores the types which were implicitly dereferenced in pattern binding modes
/// for later usage in HAIR lowering. For example,
///
/// ```
/// match &&Some(5i32) {
/// Some(n) => {},
/// _ => {},
/// }
/// ```
/// leads to a `vec![&&i32, &i32]`. Empty vectors are not stored.
///
/// See:
/// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions
pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>,

/// Borrows
pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,

Expand Down Expand Up @@ -390,6 +405,7 @@ impl<'tcx> TypeckTables<'tcx> {
node_substs: ItemLocalMap(),
adjustments: ItemLocalMap(),
pat_binding_modes: ItemLocalMap(),
pat_adjustments: ItemLocalMap(),
upvar_capture_map: FxHashMap(),
generator_sigs: ItemLocalMap(),
generator_interiors: ItemLocalMap(),
Expand Down Expand Up @@ -570,6 +586,21 @@ impl<'tcx> TypeckTables<'tcx> {
}
}

pub fn pat_adjustments(&self) -> LocalTableInContext<Vec<Ty<'tcx>>> {
LocalTableInContext {
local_id_root: self.local_id_root,
data: &self.pat_adjustments,
}
}

pub fn pat_adjustments_mut(&mut self)
-> LocalTableInContextMut<Vec<Ty<'tcx>>> {
LocalTableInContextMut {
local_id_root: self.local_id_root,
data: &mut self.pat_adjustments,
}
}

pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> {
self.upvar_capture_map[&upvar_id]
}
Expand Down Expand Up @@ -695,6 +726,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for Typeck
ref node_substs,
ref adjustments,
ref pat_binding_modes,
ref pat_adjustments,
ref upvar_capture_map,
ref closure_tys,
ref closure_kinds,
Expand All @@ -716,6 +748,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for Typeck
ich::hash_stable_itemlocalmap(hcx, hasher, node_substs);
ich::hash_stable_itemlocalmap(hcx, hasher, adjustments);
ich::hash_stable_itemlocalmap(hcx, hasher, pat_binding_modes);
ich::hash_stable_itemlocalmap(hcx, hasher, pat_adjustments);
ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| {
let ty::UpvarId {
var_id,
Expand Down
18 changes: 18 additions & 0 deletions src/librustc_const_eval/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,24 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}

pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
let unadjusted_pat = self.lower_pattern_unadjusted(pat);
self.tables
.pat_adjustments()
.get(pat.hir_id)
.unwrap_or(&vec![])
.iter()
.fold(unadjusted_pat, |pat, ref_ty| {
debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
Pattern {
span: pat.span,
ty: ref_ty,
kind: Box::new(PatternKind::Deref { subpattern: pat }),
}
},
)
}

pub fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
let mut ty = self.tables.node_id_to_type(pat.hir_id);

let kind = match pat.node {
Expand Down
136 changes: 108 additions & 28 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,92 @@ use syntax::ptr::P;
use syntax_pos::Span;

impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) {
self.check_pat_arg(pat, expected, false);
pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, def_bm: ty::BindingMode) {
self.check_pat_arg(pat, expected, def_bm, false);
}

/// The `is_arg` argument indicates whether this pattern is the
/// *outermost* pattern in an argument (e.g., in `fn foo(&x:
/// &u32)`, it is true for the `&x` pattern but not `x`). This is
/// used to tailor error reporting.
pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) {
#[allow(unused_assignments)]
pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, mut expected: Ty<'tcx>,
mut def_bm: ty::BindingMode, is_arg: bool) {
let tcx = self.tcx;

debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg);
debug!("check_pat(pat={:?},expected={:?},def_bm={:?},is_arg={})",
pat, expected, def_bm, is_arg);

let is_non_ref_pat = match pat.node {
PatKind::Struct(..) |
PatKind::TupleStruct(..) |
PatKind::Tuple(..) |
PatKind::Box(_) |
// PatKind::Lit(_) | // FIXME(tschottdorf): causes lots of errors
PatKind::Range(..) |
PatKind::Slice(..) => true,
PatKind::Path(..) => {
// FIXME(tschottdorf): to handle const refs, should be enough to
// check whether pat_ty starts with a TypeVariant::TyRef and if
// so, not apply const binding modes.
true
}
PatKind::Wild |
PatKind::Binding(..) |
PatKind::Ref(..) => false,
_ => false,
};
if is_non_ref_pat {
debug!("is_non_ref_pat");
let mut exp_ty = self.resolve_type_vars_with_obligations(&expected);

// Peel off as many `&` or `&mut` from the discriminant as possible. For example,
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
// the `Some(5)` which is not of type TyRef.
//
// For each ampersand peeled off, increment a counter and update the binding mode.
// See the examples in compile-pass/FIXME(tschottdorf).
let mut pat_adjustments = vec![];
expected = loop {
debug!("inspecting {:?} with type {:?}", exp_ty, exp_ty.sty);
match exp_ty.sty {
ty::TypeVariants::TyRef(_, ty::TypeAndMut{
ty: inner_ty, mutbl: inner_mutability,
}) => {
debug!("is a ref");
// Preserve the reference type. We'll need it later during HAIR lowering.
pat_adjustments.push(exp_ty);

// FIXME(tschottdorf): is it OK to keep the same flags?
exp_ty = inner_ty;
def_bm = match def_bm {
// If default binding mode is by value, make it `ref` or `ref mut`
// (depending on whether we observe `&` or `&mut`).
ty::BindByValue(_) =>
ty::BindByReference(inner_mutability),
// Once a `ref`, always a `ref`. This is because a `& &mut` can't mutate
// the underlying value.
ty::BindByReference(hir::Mutability::MutImmutable) =>
ty::BindByReference(hir::Mutability::MutImmutable),
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref`
// (on `&`).
ty::BindByReference(hir::Mutability::MutMutable) =>
ty::BindByReference(inner_mutability),
};
},
_ => break exp_ty,
}
};
if pat_adjustments.len() > 0 {
self.inh.tables.borrow_mut()
.pat_adjustments_mut()
.insert(pat.hir_id, pat_adjustments);
}
}

// Lose mutability now that we know binding mode and discriminant type.
let def_bm = def_bm;
let expected = expected;

let ty = match pat.node {
PatKind::Wild => {
Expand Down Expand Up @@ -114,10 +188,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
common_type
}
PatKind::Binding(ba, var_id, _, ref sub) => {
// Note the binding mode in the typeck tables. For now, what we store is always
// identical to what could be scraped from the HIR, but this will change with
// default binding modes (#42640).
let bm = ty::BindingMode::convert(ba);
let bm = if ba == hir::BindingAnnotation::Unannotated {
def_bm
} else {
ty::BindingMode::convert(ba)
};
self.inh
.tables
.borrow_mut()
Expand Down Expand Up @@ -155,19 +230,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}

if let Some(ref p) = *sub {
self.check_pat(&p, expected);
self.check_pat(&p, expected, def_bm);
}

typ
}
PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected)
self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected, def_bm)
}
PatKind::Path(ref qpath) => {
self.check_pat_path(pat, qpath, expected)
}
PatKind::Struct(ref qpath, ref fields, etc) => {
self.check_pat_struct(pat, qpath, fields, etc, expected)
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm)
}
PatKind::Tuple(ref elements, ddpos) => {
let mut expected_len = elements.len();
Expand All @@ -188,7 +263,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let pat_ty = tcx.mk_ty(ty::TyTuple(element_tys, false));
self.demand_eqtype(pat.span, expected, pat_ty);
for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
self.check_pat(elem, &element_tys[i]);
self.check_pat(elem, &element_tys[i], def_bm);
}
pat_ty
}
Expand All @@ -201,10 +276,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// think any errors can be introduced by using
// `demand::eqtype`.
self.demand_eqtype(pat.span, expected, uniq_ty);
self.check_pat(&inner, inner_ty);
self.check_pat(&inner, inner_ty, def_bm);
uniq_ty
} else {
self.check_pat(&inner, tcx.types.err);
self.check_pat(&inner, tcx.types.err, def_bm);
tcx.types.err
}
}
Expand Down Expand Up @@ -253,10 +328,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
};

self.check_pat(&inner, inner_ty);
self.check_pat(&inner, inner_ty, def_bm);
rptr_ty
} else {
self.check_pat(&inner, tcx.types.err);
self.check_pat(&inner, tcx.types.err, def_bm);
tcx.types.err
}
}
Expand Down Expand Up @@ -314,13 +389,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};

for elt in before {
self.check_pat(&elt, inner_ty);
self.check_pat(&elt, inner_ty, def_bm);
}
if let Some(ref slice) = *slice {
self.check_pat(&slice, slice_ty);
self.check_pat(&slice, slice_ty, def_bm);
}
for elt in after {
self.check_pat(&elt, inner_ty);
self.check_pat(&elt, inner_ty, def_bm);
}
expected_ty
}
Expand Down Expand Up @@ -495,7 +570,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut all_pats_diverge = Diverges::WarnedAlways;
for p in &arm.pats {
self.diverges.set(Diverges::Maybe);
self.check_pat(&p, discrim_ty);
self.check_pat(&p, discrim_ty,
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable));
all_pats_diverge &= self.diverges.get();
}

Expand Down Expand Up @@ -576,14 +652,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
qpath: &hir::QPath,
fields: &'gcx [Spanned<hir::FieldPat>],
etc: bool,
expected: Ty<'tcx>) -> Ty<'tcx>
expected: Ty<'tcx>,
def_bm: ty::BindingMode) -> Ty<'tcx>
{
// Resolve the path and check the definition for errors.
let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.id) {
variant_ty
} else {
for field in fields {
self.check_pat(&field.node.pat, self.tcx.types.err);
self.check_pat(&field.node.pat, self.tcx.types.err, def_bm);
}
return self.tcx.types.err;
};
Expand All @@ -592,7 +669,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.demand_eqtype(pat.span, expected, pat_ty);

// Type check subpatterns.
self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc);
self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc, def_bm);
pat_ty
}

Expand Down Expand Up @@ -637,12 +714,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
qpath: &hir::QPath,
subpats: &'gcx [P<hir::Pat>],
ddpos: Option<usize>,
expected: Ty<'tcx>) -> Ty<'tcx>
expected: Ty<'tcx>,
def_bm: ty::BindingMode) -> Ty<'tcx>
{
let tcx = self.tcx;
let on_error = || {
for pat in subpats {
self.check_pat(&pat, tcx.types.err);
self.check_pat(&pat, tcx.types.err, def_bm);
}
};
let report_unexpected_def = |def: Def| {
Expand Down Expand Up @@ -678,6 +756,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Replace constructor type with constructed type for tuple struct patterns.
let pat_ty = pat_ty.fn_sig(tcx).output();
let pat_ty = tcx.no_late_bound_regions(&pat_ty).expect("expected fn type");

self.demand_eqtype(pat.span, expected, pat_ty);

// Type check subpatterns.
Expand All @@ -689,7 +768,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
self.check_pat(&subpat, field_ty);
self.check_pat(&subpat, field_ty, def_bm);

self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span);
}
Expand All @@ -715,7 +794,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
variant: &'tcx ty::VariantDef,
fields: &'gcx [Spanned<hir::FieldPat>],
etc: bool) {
etc: bool,
def_bm: ty::BindingMode) {
let tcx = self.tcx;

let (substs, kind_name) = match adt_ty.sty {
Expand Down Expand Up @@ -772,7 +852,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
};

self.check_pat(&field.pat, field_ty);
self.check_pat(&field.pat, field_ty, def_bm);
}

// Report an error if incorrect number of the fields were specified.
Expand Down
5 changes: 3 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
// Add formal parameters.
for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) {
// Check the pattern.
fcx.check_pat_arg(&arg.pat, arg_ty, true);
fcx.check_pat_arg(&arg.pat, arg_ty,
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true);

// Check that argument is Sized.
// The check for a non-trivial pattern is a hack to avoid duplicate warnings
Expand Down Expand Up @@ -4159,7 +4160,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}

self.check_pat(&local.pat, t);
self.check_pat(&local.pat, t, ty::BindingMode::BindByValue(hir::Mutability::MutImmutable));
let pat_ty = self.node_ty(local.pat.hir_id);
if pat_ty.references_error() {
self.write_ty(local.hir_id, pat_ty);
Expand Down
Loading