Skip to content

Rollup of 6 pull requests #112774

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

Merged
merged 12 commits into from
Jun 19, 2023
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
7 changes: 3 additions & 4 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Fudge the receiver, so we can do new inference on it.
let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
let method = self
.lookup_method(
.lookup_method_for_diagnostic(
possible_rcvr_ty,
segment,
DUMMY_SP,
call_expr,
binding,
args,
)
.ok()?;
// Unify the method signature with our incompatible arg, to
Expand Down Expand Up @@ -435,14 +434,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
let Ok(method) =
self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
self.lookup_method_for_diagnostic(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
else {
continue;
};

let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
let ideal_method = self
.lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
.lookup_method_for_diagnostic(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
.ok()
.and_then(|method| {
let _ = self.at(&ObligationCause::dummy(), self.param_env)
Expand Down
26 changes: 23 additions & 3 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct ConfirmContext<'a, 'tcx> {
span: Span,
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
skip_record_for_diagnostics: bool,
}

impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> {
Expand Down Expand Up @@ -59,6 +60,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
confirm_cx.confirm(unadjusted_self_ty, pick, segment)
}

pub fn confirm_method_for_diagnostic(
&self,
span: Span,
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
unadjusted_self_ty: Ty<'tcx>,
pick: &probe::Pick<'tcx>,
segment: &hir::PathSegment<'_>,
) -> ConfirmResult<'tcx> {
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
confirm_cx.skip_record_for_diagnostics = true;
confirm_cx.confirm(unadjusted_self_ty, pick, segment)
}
}

impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
Expand All @@ -68,7 +83,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
) -> ConfirmContext<'a, 'tcx> {
ConfirmContext { fcx, span, self_expr, call_expr }
ConfirmContext { fcx, span, self_expr, call_expr, skip_record_for_diagnostics: false }
}

fn confirm(
Expand Down Expand Up @@ -219,7 +234,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
self.register_predicates(autoderef.into_obligations());

// Write out the final adjustments.
self.apply_adjustments(self.self_expr, adjustments);
if !self.skip_record_for_diagnostics {
self.apply_adjustments(self.self_expr, adjustments);
}

target
}
Expand Down Expand Up @@ -453,7 +470,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
});

debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation);
self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);

if !self.skip_record_for_diagnostics {
self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
}
}

self.normalize(self.span, substs)
Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(result.callee)
}

pub fn lookup_method_for_diagnostic(
&self,
self_ty: Ty<'tcx>,
segment: &hir::PathSegment<'_>,
span: Span,
call_expr: &'tcx hir::Expr<'tcx>,
self_expr: &'tcx hir::Expr<'tcx>,
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
let pick = self.lookup_probe_for_diagnostic(
segment.ident,
self_ty,
call_expr,
ProbeScope::TraitsInScope,
None,
)?;

Ok(self
.confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
.callee)
}

#[instrument(level = "debug", skip(self, call_expr))]
pub fn lookup_probe(
&self,
Expand Down
85 changes: 26 additions & 59 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@ impl<'tcx> ConstToPat<'tcx> {
def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)),
))?,
},
ty::Slice(elem_ty) => PatKind::Slice {
prefix: cv
.unwrap_branch()
.iter()
.map(|val| self.recur(*val, *elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
ty::Array(elem_ty, _) => PatKind::Array {
prefix: cv
.unwrap_branch()
Expand All @@ -372,70 +381,16 @@ impl<'tcx> ConstToPat<'tcx> {
// `&str` is represented as a valtree, let's keep using this
// optimization for now.
ty::Str => PatKind::Constant { value: mir::ConstantKind::Ty(tcx.mk_const(cv, ty)) },
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
// matching against references, you can only use byte string literals.
// The typechecker has a special case for byte string literals, by treating them
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
// has no negative effects on pattern matching, even if we're actually matching on
// arrays.
ty::Array(elem_ty, _) if !self.treat_byte_string_as_slice => {
let old = self.behind_reference.replace(true);
// References have the same valtree representation as their pointee.
let array = cv;
let val = PatKind::Deref {
subpattern: Box::new(Pat {
kind: PatKind::Array {
prefix: array.unwrap_branch()
.iter()
.map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
span,
ty: tcx.mk_slice(elem_ty),
}),
};
self.behind_reference.set(old);
val
}
ty::Array(elem_ty, _) |
// Cannot merge this with the catch all branch below, because the `const_deref`
// changes the type from slice to array, we need to keep the original type in the
// pattern.
ty::Slice(elem_ty) => {
let old = self.behind_reference.replace(true);
// References have the same valtree representation as their pointee.
let array = cv;
let val = PatKind::Deref {
subpattern: Box::new(Pat {
kind: PatKind::Slice {
prefix: array.unwrap_branch()
.iter()
.map(|val| self.recur(*val, elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
span,
ty: tcx.mk_slice(elem_ty),
}),
};
self.behind_reference.set(old);
val
}
// Backwards compatibility hack: support references to non-structural types,
// but hard error if we aren't behind a double reference. We could just use
// the fallback code path below, but that would allow *more* of this fishy
// code to compile, as then it only goes through the future incompat lint
// instead of a hard error.
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() {
if !self.saw_const_match_error.get()
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
span,
Expand All @@ -456,16 +411,28 @@ impl<'tcx> ConstToPat<'tcx> {
// convert the dereferenced constant to a pattern that is the sub-pattern of the
// deref pattern.
_ => {
if !pointee_ty.is_sized(tcx, param_env) {
if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
tcx.sess.emit_err(err);

// FIXME: introduce PatKind::Error to silence follow up diagnostics due to unreachable patterns.
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
// matching against references, you can only use byte string literals.
// The typechecker has a special case for byte string literals, by treating them
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
// has no negative effects on pattern matching, even if we're actually matching on
// arrays.
let pointee_ty = match *pointee_ty.kind() {
ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
tcx.mk_slice(elem_ty)
}
_ => *pointee_ty,
};
// References have the same valtree representation as their pointee.
let subpattern = self.recur(cv, *pointee_ty, false)?;
let subpattern = self.recur(cv, pointee_ty, false)?;
self.behind_reference.set(old);
PatKind::Deref { subpattern }
}
Expand Down
93 changes: 40 additions & 53 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,21 +304,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let res = binding.res();
self.check_reserved_macro_name(key.ident, res);
self.set_binding_parent_module(binding, module);
self.update_resolution(module, key, |this, resolution| {
if let Some(old_binding) = resolution.binding {
if res == Res::Err && old_binding.res() != Res::Err {
// Do not override real bindings with `Res::Err`s from error recovery.
return Ok(());
}

let mut resolution = self.resolution(module, key).borrow_mut();
let old_binding = resolution.binding();
let mut t = Ok(());
if let Some(old_binding) = resolution.binding {
if res == Res::Err && old_binding.res() != Res::Err {
// Do not override real bindings with `Res::Err`s from error recovery.
} else {
match (old_binding.is_glob_import(), binding.is_glob_import()) {
(true, true) => {
if res != old_binding.res() {
resolution.binding = Some(this.ambiguity(
resolution.binding = Some(self.ambiguity(
AmbiguityKind::GlobVsGlob,
old_binding,
binding,
));
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
} else if !old_binding.vis.is_at_least(binding.vis, self.tcx) {
// We are glob-importing the same item but with greater visibility.
resolution.binding = Some(binding);
}
Expand All @@ -330,7 +332,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&& key.ns == MacroNS
&& nonglob_binding.expansion != LocalExpnId::ROOT
{
resolution.binding = Some(this.ambiguity(
resolution.binding = Some(self.ambiguity(
AmbiguityKind::GlobVsExpanded,
nonglob_binding,
glob_binding,
Expand All @@ -342,66 +344,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if let Some(old_binding) = resolution.shadowed_glob {
assert!(old_binding.is_glob_import());
if glob_binding.res() != old_binding.res() {
resolution.shadowed_glob = Some(this.ambiguity(
resolution.shadowed_glob = Some(self.ambiguity(
AmbiguityKind::GlobVsGlob,
old_binding,
glob_binding,
));
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
} else if !old_binding.vis.is_at_least(binding.vis, self.tcx) {
resolution.shadowed_glob = Some(glob_binding);
}
} else {
resolution.shadowed_glob = Some(glob_binding);
}
}
(false, false) => {
return Err(old_binding);
t = Err(old_binding);
}
}
} else {
resolution.binding = Some(binding);
}
} else {
resolution.binding = Some(binding);
};

Ok(())
})
}

fn ambiguity(
&self,
kind: AmbiguityKind,
primary_binding: &'a NameBinding<'a>,
secondary_binding: &'a NameBinding<'a>,
) -> &'a NameBinding<'a> {
self.arenas.alloc_name_binding(NameBinding {
ambiguity: Some((secondary_binding, kind)),
..primary_binding.clone()
})
}

// Use `f` to mutate the resolution of the name in the module.
// If the resolution becomes a success, define it in the module's glob importers.
fn update_resolution<T, F>(&mut self, module: Module<'a>, key: BindingKey, f: F) -> T
where
F: FnOnce(&mut Resolver<'a, 'tcx>, &mut NameResolution<'a>) -> T,
{
// Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
// during which the resolution might end up getting re-defined via a glob cycle.
let (binding, t) = {
let resolution = &mut *self.resolution(module, key).borrow_mut();
let old_binding = resolution.binding();

let t = f(self, resolution);

match resolution.binding() {
_ if old_binding.is_some() => return t,
None => return t,
Some(binding) => match old_binding {
Some(old_binding) if ptr::eq(old_binding, binding) => return t,
_ => (binding, t),
},
}
let (binding, t) = match resolution.binding() {
_ if old_binding.is_some() => return t,
None => return t,
Some(binding) => match old_binding {
Some(old_binding) if ptr::eq(old_binding, binding) => return t,
_ => (binding, t),
},
};

drop(resolution);

// Define `binding` in `module`s glob importers.
for import in module.glob_importers.borrow_mut().iter() {
let mut ident = key.ident;
Expand All @@ -420,6 +396,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
t
}

fn ambiguity(
&self,
kind: AmbiguityKind,
primary_binding: &'a NameBinding<'a>,
secondary_binding: &'a NameBinding<'a>,
) -> &'a NameBinding<'a> {
self.arenas.alloc_name_binding(NameBinding {
ambiguity: Some((secondary_binding, kind)),
..primary_binding.clone()
})
}

// Define a dummy resolution containing a `Res::Err` as a placeholder for a failed
// or indeterminate resolution, also mark such failed imports as used to avoid duplicate diagnostics.
fn import_dummy_binding(&mut self, import: &'a Import<'a>, is_indeterminate: bool) {
Expand Down Expand Up @@ -769,9 +757,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.emit();
}
let key = BindingKey::new(target, ns);
this.update_resolution(parent, key, |_, resolution| {
resolution.single_imports.remove(&Interned::new_unchecked(import));
});
let mut resolution = this.resolution(parent, key).borrow_mut();
resolution.single_imports.remove(&Interned::new_unchecked(import));
}
}
}
Expand Down
Loading