Skip to content

Commit db6d0b1

Browse files
committed
Check associated type implementations for generic mismatches
1 parent 01a4650 commit db6d0b1

File tree

6 files changed

+195
-26
lines changed

6 files changed

+195
-26
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,7 @@ impl<'tcx> ObligationCause<'tcx> {
19121912
use crate::traits::ObligationCauseCode::*;
19131913
match self.code {
19141914
CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
1915+
CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"),
19151916
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) =>
19161917
Error0308(match source {
19171918
hir::MatchSource::IfLetDesugar { .. } =>
@@ -1948,6 +1949,7 @@ impl<'tcx> ObligationCause<'tcx> {
19481949
use crate::traits::ObligationCauseCode::*;
19491950
match self.code {
19501951
CompareImplMethodObligation { .. } => "method type is compatible with trait",
1952+
CompareImplTypeObligation { .. } => "associated type is compatible with trait",
19511953
ExprAssignable => "expression is assignable",
19521954
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
19531955
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",

src/librustc/traits/error_reporting.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
702702
SelectionError::Unimplemented => {
703703
if let ObligationCauseCode::CompareImplMethodObligation {
704704
item_name, impl_item_def_id, trait_item_def_id,
705+
} | ObligationCauseCode::CompareImplTypeObligation {
706+
item_name, impl_item_def_id, trait_item_def_id,
705707
} = obligation.cause.code {
706708
self.report_extra_impl_obligation(
707709
span,
@@ -2631,6 +2633,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
26312633
but not on the corresponding trait method",
26322634
predicate));
26332635
}
2636+
ObligationCauseCode::CompareImplTypeObligation { .. } => {
2637+
err.note(&format!(
2638+
"the requirement `{}` appears on the associated impl type\
2639+
but not on the corresponding associated trait type",
2640+
predicate));
2641+
}
26342642
ObligationCauseCode::ReturnType |
26352643
ObligationCauseCode::ReturnValue(_) |
26362644
ObligationCauseCode::BlockTailExpression(_) => (),

src/librustc/traits/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ pub enum ObligationCauseCode<'tcx> {
230230
trait_item_def_id: DefId,
231231
},
232232

233+
/// Error derived when matching traits/impls; see ObligationCause for more details
234+
CompareImplTypeObligation {
235+
item_name: ast::Name,
236+
impl_item_def_id: DefId,
237+
trait_item_def_id: DefId,
238+
},
239+
233240
/// Checking that this expression can be assigned where it needs to be
234241
// FIXME(eddyb) #11161 is the original Expr required?
235242
ExprAssignable,

src/librustc/traits/structural_impls.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,15 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
514514
impl_item_def_id,
515515
trait_item_def_id,
516516
}),
517+
super::CompareImplTypeObligation {
518+
item_name,
519+
impl_item_def_id,
520+
trait_item_def_id,
521+
} => Some(super::CompareImplTypeObligation {
522+
item_name,
523+
impl_item_def_id,
524+
trait_item_def_id,
525+
}),
517526
super::ExprAssignable => Some(super::ExprAssignable),
518527
super::MatchExpressionArm(box super::MatchExpressionArmCause {
519528
arm_span,

src/librustc_typeck/check/compare_method.rs

Lines changed: 155 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc::ty::{self, TyCtxt, GenericParamDefKind};
55
use rustc::ty::util::ExplicitSelf;
66
use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
77
use rustc::ty::error::{ExpectedFound, TypeError};
8-
use rustc::ty::subst::{Subst, InternalSubsts, SubstsRef};
8+
use rustc::ty::subst::{Subst, InternalSubsts};
99
use rustc::util::common::ErrorReported;
1010
use errors::{Applicability, DiagnosticId};
1111

@@ -26,7 +26,7 @@ use rustc_error_codes::*;
2626
/// - `trait_m`: the method in the trait
2727
/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
2828
29-
pub fn compare_impl_method<'tcx>(
29+
crate fn compare_impl_method<'tcx>(
3030
tcx: TyCtxt<'tcx>,
3131
impl_m: &ty::AssocItem,
3232
impl_m_span: Span,
@@ -181,13 +181,14 @@ fn compare_predicate_entailment<'tcx>(
181181
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
182182

183183
// Check region bounds.
184-
check_region_bounds_on_impl_method(tcx,
185-
impl_m_span,
186-
impl_m,
187-
trait_m,
188-
&trait_m_generics,
189-
&impl_m_generics,
190-
trait_to_skol_substs)?;
184+
check_region_bounds_on_impl_item(
185+
tcx,
186+
impl_m_span,
187+
impl_m,
188+
trait_m,
189+
&trait_m_generics,
190+
&impl_m_generics,
191+
)?;
191192

192193
// Create obligations for each predicate declared by the impl
193194
// definition in the context of the trait's parameter
@@ -361,25 +362,22 @@ fn compare_predicate_entailment<'tcx>(
361362
})
362363
}
363364

364-
fn check_region_bounds_on_impl_method<'tcx>(
365+
fn check_region_bounds_on_impl_item<'tcx>(
365366
tcx: TyCtxt<'tcx>,
366367
span: Span,
367368
impl_m: &ty::AssocItem,
368369
trait_m: &ty::AssocItem,
369370
trait_generics: &ty::Generics,
370371
impl_generics: &ty::Generics,
371-
trait_to_skol_substs: SubstsRef<'tcx>,
372372
) -> Result<(), ErrorReported> {
373373
let trait_params = trait_generics.own_counts().lifetimes;
374374
let impl_params = impl_generics.own_counts().lifetimes;
375375

376-
debug!("check_region_bounds_on_impl_method: \
376+
debug!("check_region_bounds_on_impl_item: \
377377
trait_generics={:?} \
378-
impl_generics={:?} \
379-
trait_to_skol_substs={:?}",
378+
impl_generics={:?}",
380379
trait_generics,
381-
impl_generics,
382-
trait_to_skol_substs);
380+
impl_generics);
383381

384382
// Must have same number of early-bound lifetime parameters.
385383
// Unfortunately, if the user screws up the bounds, then this
@@ -391,20 +389,25 @@ fn check_region_bounds_on_impl_method<'tcx>(
391389
// are zero. Since I don't quite know how to phrase things at
392390
// the moment, give a kind of vague error message.
393391
if trait_params != impl_params {
392+
let item_kind = assoc_item_kind_str(impl_m);
394393
let def_span = tcx.sess.source_map().def_span(span);
395394
let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span);
396395
let mut err = struct_span_err!(
397396
tcx.sess,
398397
span,
399398
E0195,
400-
"lifetime parameters or bounds on method `{}` do not match the trait declaration",
399+
"lifetime parameters or bounds on {} `{}` do not match the trait declaration",
400+
item_kind,
401401
impl_m.ident,
402402
);
403-
err.span_label(span, "lifetimes do not match method in trait");
403+
err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind));
404404
if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
405405
let def_sp = tcx.sess.source_map().def_span(sp);
406406
let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp);
407-
err.span_label(sp, "lifetimes in impl do not match this method in trait");
407+
err.span_label(
408+
sp,
409+
&format!("lifetimes in impl do not match this {} in trait", item_kind),
410+
);
408411
}
409412
err.emit();
410413
return Err(ErrorReported);
@@ -603,6 +606,8 @@ fn compare_number_of_generics<'tcx>(
603606
("const", trait_own_counts.consts, impl_own_counts.consts),
604607
];
605608

609+
let item_kind = assoc_item_kind_str(impl_);
610+
606611
let mut err_occurred = false;
607612
for &(kind, trait_count, impl_count) in &matchings {
608613
if impl_count != trait_count {
@@ -647,8 +652,9 @@ fn compare_number_of_generics<'tcx>(
647652
let mut err = tcx.sess.struct_span_err_with_code(
648653
spans,
649654
&format!(
650-
"method `{}` has {} {kind} parameter{} but its trait \
655+
"{} `{}` has {} {kind} parameter{} but its trait \
651656
declaration has {} {kind} parameter{}",
657+
item_kind,
652658
trait_.ident,
653659
impl_count,
654660
pluralize!(impl_count),
@@ -961,7 +967,7 @@ fn compare_synthetic_generics<'tcx>(
961967
}
962968
}
963969

964-
pub fn compare_const_impl<'tcx>(
970+
crate fn compare_const_impl<'tcx>(
965971
tcx: TyCtxt<'tcx>,
966972
impl_c: &ty::AssocItem,
967973
impl_c_span: Span,
@@ -1059,3 +1065,131 @@ pub fn compare_const_impl<'tcx>(
10591065
fcx.regionck_item(impl_c_hir_id, impl_c_span, &[]);
10601066
});
10611067
}
1068+
1069+
crate fn compare_ty_impl<'tcx>(
1070+
tcx: TyCtxt<'tcx>,
1071+
impl_ty: &ty::AssocItem,
1072+
impl_ty_span: Span,
1073+
trait_ty: &ty::AssocItem,
1074+
impl_trait_ref: ty::TraitRef<'tcx>,
1075+
trait_item_span: Option<Span>,
1076+
) {
1077+
debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
1078+
1079+
let _: Result<(), ErrorReported> = (|| {
1080+
compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
1081+
1082+
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)
1083+
})();
1084+
}
1085+
1086+
/// The equivalent of [compare_predicate_entailment], but for associated types
1087+
/// instead of associated functions.
1088+
fn compare_type_predicate_entailment(
1089+
tcx: TyCtxt<'tcx>,
1090+
impl_ty: &ty::AssocItem,
1091+
impl_ty_span: Span,
1092+
trait_ty: &ty::AssocItem,
1093+
impl_trait_ref: ty::TraitRef<'tcx>,
1094+
) -> Result<(), ErrorReported> {
1095+
let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
1096+
let trait_to_impl_substs = impl_substs.rebase_onto(tcx,
1097+
impl_ty.container.id(),
1098+
impl_trait_ref.substs);
1099+
1100+
let impl_ty_generics = tcx.generics_of(impl_ty.def_id);
1101+
let trait_ty_generics = tcx.generics_of(trait_ty.def_id);
1102+
let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
1103+
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
1104+
1105+
check_region_bounds_on_impl_item(
1106+
tcx,
1107+
impl_ty_span,
1108+
impl_ty,
1109+
trait_ty,
1110+
&trait_ty_generics,
1111+
&impl_ty_generics,
1112+
)?;
1113+
1114+
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
1115+
1116+
if impl_ty_own_bounds.is_empty() {
1117+
// Nothing to check.
1118+
return Ok(());
1119+
}
1120+
1121+
// This `HirId` should be used for the `body_id` field on each
1122+
// `ObligationCause` (and the `FnCtxt`). This is what
1123+
// `regionck_item` expects.
1124+
let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id).unwrap();
1125+
let cause = ObligationCause {
1126+
span: impl_ty_span,
1127+
body_id: impl_ty_hir_id,
1128+
code: ObligationCauseCode::CompareImplTypeObligation {
1129+
item_name: impl_ty.ident.name,
1130+
impl_item_def_id: impl_ty.def_id,
1131+
trait_item_def_id: trait_ty.def_id,
1132+
},
1133+
};
1134+
1135+
debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
1136+
1137+
// The predicates declared by the impl definition, the trait and the
1138+
// associated type in the trait are assumed.
1139+
let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
1140+
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
1141+
hybrid_preds.predicates.extend(
1142+
trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
1143+
1144+
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
1145+
1146+
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
1147+
let param_env = ty::ParamEnv::new(
1148+
tcx.intern_predicates(&hybrid_preds.predicates),
1149+
Reveal::UserFacing,
1150+
None
1151+
);
1152+
let param_env = traits::normalize_param_env_or_error(tcx,
1153+
impl_ty.def_id,
1154+
param_env,
1155+
normalize_cause.clone());
1156+
tcx.infer_ctxt().enter(|infcx| {
1157+
let inh = Inherited::new(infcx, impl_ty.def_id);
1158+
let infcx = &inh.infcx;
1159+
1160+
debug!("compare_type_predicate_entailment: caller_bounds={:?}",
1161+
param_env.caller_bounds);
1162+
1163+
let mut selcx = traits::SelectionContext::new(&infcx);
1164+
1165+
for predicate in impl_ty_own_bounds.predicates {
1166+
let traits::Normalized { value: predicate, obligations } =
1167+
traits::normalize(&mut selcx, param_env, normalize_cause.clone(), &predicate);
1168+
1169+
inh.register_predicates(obligations);
1170+
inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate));
1171+
}
1172+
1173+
// Check that all obligations are satisfied by the implementation's
1174+
// version.
1175+
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
1176+
infcx.report_fulfillment_errors(errors, None, false);
1177+
return Err(ErrorReported);
1178+
}
1179+
1180+
// Finally, resolve all regions. This catches wily misuses of
1181+
// lifetime parameters.
1182+
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
1183+
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
1184+
1185+
Ok(())
1186+
})
1187+
}
1188+
1189+
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
1190+
match impl_item.kind {
1191+
ty::AssocKind::Const => "const",
1192+
ty::AssocKind::Method => "method",
1193+
ty::AssocKind::Type | ty::AssocKind::OpaqueTy => "type",
1194+
}
1195+
}

src/librustc_typeck/check/mod.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ pub use self::Expectation::*;
154154
use self::autoderef::Autoderef;
155155
use self::callee::DeferredCallResolution;
156156
use self::coercion::{CoerceMany, DynamicCoerceMany};
157-
pub use self::compare_method::{compare_impl_method, compare_const_impl};
157+
use self::compare_method::{compare_impl_method, compare_const_impl, compare_ty_impl};
158158
use self::method::{MethodCallee, SelfSource};
159159
use self::TupleArgumentsFlag::*;
160160

@@ -2014,41 +2014,50 @@ fn check_impl_items_against_trait<'tcx>(
20142014
}
20152015
}
20162016
hir::ImplItemKind::Method(..) => {
2017-
let trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
2017+
let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
20182018
if ty_trait_item.kind == ty::AssocKind::Method {
20192019
compare_impl_method(tcx,
20202020
&ty_impl_item,
20212021
impl_item.span,
20222022
&ty_trait_item,
20232023
impl_trait_ref,
2024-
trait_span);
2024+
opt_trait_span);
20252025
} else {
20262026
let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324,
20272027
"item `{}` is an associated method, \
20282028
which doesn't match its trait `{}`",
20292029
ty_impl_item.ident,
20302030
impl_trait_ref.print_only_trait_path());
20312031
err.span_label(impl_item.span, "does not match trait");
2032-
if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
2032+
if let Some(trait_span) = opt_trait_span {
20332033
err.span_label(trait_span, "item in trait");
20342034
}
20352035
err.emit()
20362036
}
20372037
}
20382038
hir::ImplItemKind::OpaqueTy(..) |
20392039
hir::ImplItemKind::TyAlias(_) => {
2040+
let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
20402041
if ty_trait_item.kind == ty::AssocKind::Type {
20412042
if ty_trait_item.defaultness.has_value() {
20422043
overridden_associated_type = Some(impl_item);
20432044
}
2045+
compare_ty_impl(
2046+
tcx,
2047+
&ty_impl_item,
2048+
impl_item.span,
2049+
&ty_trait_item,
2050+
impl_trait_ref,
2051+
opt_trait_span,
2052+
)
20442053
} else {
20452054
let mut err = struct_span_err!(tcx.sess, impl_item.span, E0325,
20462055
"item `{}` is an associated type, \
20472056
which doesn't match its trait `{}`",
20482057
ty_impl_item.ident,
20492058
impl_trait_ref.print_only_trait_path());
20502059
err.span_label(impl_item.span, "does not match trait");
2051-
if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
2060+
if let Some(trait_span) = opt_trait_span {
20522061
err.span_label(trait_span, "item in trait");
20532062
}
20542063
err.emit()

0 commit comments

Comments
 (0)