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

Improve selection errors for ~const trait bounds #92256

Merged
merged 2 commits into from
Jan 27, 2022
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
8 changes: 4 additions & 4 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl ObligationCauseCode<'_> {

// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ObligationCauseCode<'_>, 40);
static_assert_size!(ObligationCauseCode<'_>, 48);

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum StatementAsExpression {
Expand Down Expand Up @@ -440,11 +440,11 @@ pub struct IfExpressionCause {

#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
pub struct DerivedObligationCause<'tcx> {
/// The trait reference of the parent obligation that led to the
/// The trait predicate of the parent obligation that led to the
/// current obligation. Note that only trait obligations lead to
/// derived obligations, so we just store the trait reference here
/// derived obligations, so we just store the trait predicate here
/// directly.
pub parent_trait_ref: ty::PolyTraitRef<'tcx>,
pub parent_trait_pred: ty::PolyTraitPredicate<'tcx>,

/// The parent trait had this cause.
pub parent_code: Lrc<ObligationCauseCode<'tcx>>,
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,17 @@ impl<'tcx> TraitPredicate<'tcx> {
*param_env = param_env.with_constness(self.constness.and(param_env.constness()))
}
}

/// Remap the constness of this predicate before emitting it for diagnostics.
pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) {
// this is different to `remap_constness` that callees want to print this predicate
// in case of selection errors. `T: ~const Drop` bounds cannot end up here when the
// param_env is not const because we it is always satisfied in non-const contexts.
if let hir::Constness::NotConst = param_env.constness() {
self.constness = ty::BoundConstness::NotConst;
}
}

pub fn def_id(self) -> DefId {
self.trait_ref.def_id
}
Expand All @@ -784,6 +795,14 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> {
self.map_bound(|trait_ref| trait_ref.self_ty())
}

/// Remap the constness of this predicate before emitting it for diagnostics.
pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) {
*self = self.map_bound(|mut p| {
p.remap_constness_diag(param_env);
p
});
}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
Expand Down
43 changes: 41 additions & 2 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2413,6 +2413,29 @@ impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> {
}
}

#[derive(Copy, Clone, TypeFoldable, Lift)]
pub struct TraitPredPrintModifiersAndPath<'tcx>(ty::TraitPredicate<'tcx>);

impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}

impl<'tcx> ty::TraitPredicate<'tcx> {
pub fn print_modifiers_and_trait_path(self) -> TraitPredPrintModifiersAndPath<'tcx> {
TraitPredPrintModifiersAndPath(self)
}
}

impl<'tcx> ty::PolyTraitPredicate<'tcx> {
pub fn print_modifiers_and_trait_path(
self,
) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> {
self.map_bound(TraitPredPrintModifiersAndPath)
}
}

forward_display_to_print! {
Ty<'tcx>,
&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
Expand All @@ -2427,6 +2450,7 @@ forward_display_to_print! {
ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>,
ty::Binder<'tcx, ty::FnSig<'tcx>>,
ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>,
ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>,
ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>,
Expand Down Expand Up @@ -2491,6 +2515,18 @@ define_print_and_forward_display! {
p!(print_def_path(self.0.def_id, &[]));
}

TraitPredPrintModifiersAndPath<'tcx> {
if let ty::BoundConstness::ConstIfConst = self.0.constness {
p!("~const ")
}

if let ty::ImplPolarity::Negative = self.0.polarity {
p!("!")
}

p!(print(self.0.trait_ref.print_only_trait_path()));
}

ty::ParamTy {
p!(write("{}", self.name))
}
Expand All @@ -2508,8 +2544,11 @@ define_print_and_forward_display! {
}

ty::TraitPredicate<'tcx> {
p!(print(self.trait_ref.self_ty()), ": ",
print(self.trait_ref.print_only_trait_path()))
p!(print(self.trait_ref.self_ty()), ": ");
if let ty::BoundConstness::ConstIfConst = self.constness {
p!("~const ");
}
p!(print(self.trait_ref.print_only_trait_path()))
}

ty::ProjectionPredicate<'tcx> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ symbols! {
and,
and_then,
any,
append_const_msg,
arbitrary_enum_discriminant,
arbitrary_self_types,
arith_offset,
Expand Down
99 changes: 68 additions & 31 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
self.note_obligation_cause_code(
&mut err,
&obligation.predicate,
obligation.param_env,
obligation.cause.code(),
&mut vec![],
&mut Default::default(),
Expand Down Expand Up @@ -288,7 +289,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(trait_predicate) => {
let trait_predicate = bound_predicate.rebind(trait_predicate);
let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);

trait_predicate.remap_constness_diag(obligation.param_env);
let predicate_is_const = ty::BoundConstness::ConstIfConst
== trait_predicate.skip_binder().constness;

if self.tcx.sess.has_errors() && trait_predicate.references_error() {
return;
Expand All @@ -305,13 +310,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
})
.unwrap_or_default();

let OnUnimplementedNote { message, label, note, enclosing_scope } =
self.on_unimplemented_note(trait_ref, &obligation);
let OnUnimplementedNote {
message,
label,
note,
enclosing_scope,
append_const_msg,
} = self.on_unimplemented_note(trait_ref, &obligation);
let have_alt_message = message.is_some() || label.is_some();
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
let is_unsize =
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
let (message, note) = if is_try_conversion {
let (message, note, append_const_msg) = if is_try_conversion {
(
Some(format!(
"`?` couldn't convert the error to `{}`",
Expand All @@ -322,21 +332,38 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
conversion on the error value using the `From` trait"
.to_owned(),
),
Some(None),
)
} else {
(message, note)
(message, note, append_const_msg)
};

let mut err = struct_span_err!(
self.tcx.sess,
span,
E0277,
"{}",
message.unwrap_or_else(|| format!(
"the trait bound `{}` is not satisfied{}",
trait_ref.without_const().to_predicate(tcx),
post_message,
))
message
.and_then(|cannot_do_this| {
match (predicate_is_const, append_const_msg) {
// do nothing if predicate is not const
(false, _) => Some(cannot_do_this),
// suggested using default post message
(true, Some(None)) => {
Some(format!("{cannot_do_this} in const contexts"))
}
// overriden post message
(true, Some(Some(post_message))) => {
Some(format!("{cannot_do_this}{post_message}"))
}
// fallback to generic message
(true, None) => None,
}
})
.unwrap_or_else(|| format!(
"the trait bound `{}` is not satisfied{}",
trait_predicate, post_message,
))
);

if is_try_conversion {
Expand Down Expand Up @@ -384,15 +411,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
format!(
"{}the trait `{}` is not implemented for `{}`",
pre_message,
trait_ref.print_only_trait_path(),
trait_predicate.print_modifiers_and_trait_path(),
trait_ref.skip_binder().self_ty(),
)
};

if self.suggest_add_reference_to_arg(
&obligation,
&mut err,
&trait_ref,
trait_predicate,
have_alt_message,
) {
self.note_obligation_cause(&mut err, &obligation);
Expand Down Expand Up @@ -435,18 +462,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
err.span_label(enclosing_scope_span, s.as_str());
}

self.suggest_dereferences(&obligation, &mut err, trait_ref);
self.suggest_fn_call(&obligation, &mut err, trait_ref);
self.suggest_remove_reference(&obligation, &mut err, trait_ref);
self.suggest_semicolon_removal(&obligation, &mut err, span, trait_ref);
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
self.suggest_semicolon_removal(
&obligation,
&mut err,
span,
trait_predicate,
);
self.note_version_mismatch(&mut err, &trait_ref);
self.suggest_remove_await(&obligation, &mut err);

if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() {
self.suggest_await_before_try(&mut err, &obligation, trait_ref, span);
self.suggest_await_before_try(
&mut err,
&obligation,
trait_predicate,
span,
);
}

if self.suggest_impl_trait(&mut err, span, &obligation, trait_ref) {
if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) {
err.emit();
return;
}
Expand Down Expand Up @@ -494,7 +531,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// which is somewhat confusing.
self.suggest_restricting_param_bound(
&mut err,
trait_ref,
trait_predicate,
obligation.cause.body_id,
);
} else if !have_alt_message {
Expand All @@ -506,7 +543,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// Changing mutability doesn't make a difference to whether we have
// an `Unsize` impl (Fixes ICE in #71036)
if !is_unsize {
self.suggest_change_mut(&obligation, &mut err, trait_ref);
self.suggest_change_mut(&obligation, &mut err, trait_predicate);
}

// If this error is due to `!: Trait` not implemented but `(): Trait` is
Expand Down Expand Up @@ -1121,7 +1158,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_ref: ty::PolyTraitPredicate<'tcx>,
new_self_ty: Ty<'tcx>,
) -> PredicateObligation<'tcx>;

Expand Down Expand Up @@ -1540,7 +1577,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
) -> Option<(String, Option<Span>)> {
match code {
ObligationCauseCode::BuiltinDerivedObligation(data) => {
let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref);
let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
match self.get_parent_trait_ref(&data.parent_code) {
Some(t) => Some(t),
None => {
Expand Down Expand Up @@ -1593,21 +1630,20 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_ref: ty::PolyTraitPredicate<'tcx>,
new_self_ty: Ty<'tcx>,
) -> PredicateObligation<'tcx> {
assert!(!new_self_ty.has_escaping_bound_vars());

let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef {
substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]),
let trait_pred = trait_ref.map_bound_ref(|tr| ty::TraitPredicate {
trait_ref: ty::TraitRef {
substs: self.tcx.mk_substs_trait(new_self_ty, &tr.trait_ref.substs[1..]),
..tr.trait_ref
},
..*tr
});

Obligation::new(
ObligationCause::dummy(),
param_env,
trait_ref.without_const().to_predicate(self.tcx),
)
Obligation::new(ObligationCause::dummy(), param_env, trait_pred.to_predicate(self.tcx))
}

#[instrument(skip(self), level = "debug")]
Expand Down Expand Up @@ -2008,6 +2044,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
self.note_obligation_cause_code(
err,
&obligation.predicate,
obligation.param_env,
obligation.cause.code(),
&mut vec![],
&mut Default::default(),
Expand Down Expand Up @@ -2155,7 +2192,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
cause_code: &ObligationCauseCode<'tcx>,
) -> bool {
if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code {
let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref);
let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
let self_ty = parent_trait_ref.skip_binder().self_ty();
if obligated_types.iter().any(|ot| ot == &self_ty) {
return true;
Expand Down
Loading