Skip to content

Commit 93a53b2

Browse files
committed
Auto merge of rust-lang#128126 - compiler-errors:incoherent-object-impl, r=<try>
Reject blanket object impls that are possibly incoherent wrt associated types I would like to make this test more sophisticated. Namely, we should plug the unconstrained associated types of the object type with placeholders, and then detect cases where the placeholders *don't* end up being what the blanket impl would have predicted. In that case, we know that we can use a `dyn Trait` to abuse the unsoundness in rust-lang#57893. However, first I'd like to see what the most naïve fallout of this is. r? `@ghost`
2 parents 8bfcae7 + 54904bd commit 93a53b2

File tree

1 file changed

+107
-1
lines changed
  • compiler/rustc_hir_analysis/src/coherence

1 file changed

+107
-1
lines changed

compiler/rustc_hir_analysis/src/coherence/mod.rs

+107-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ use crate::errors;
99
use rustc_errors::{codes::*, struct_span_code_err};
1010
use rustc_hir::def_id::{DefId, LocalDefId};
1111
use rustc_hir::LangItem;
12+
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
13+
use rustc_infer::traits::{Obligation, ObligationCause};
1214
use rustc_middle::query::Providers;
13-
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
15+
use rustc_middle::ty::ExistentialPredicateStableCmpExt;
16+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
1417
use rustc_session::parse::feature_err;
1518
use rustc_span::{sym, ErrorGuaranteed};
19+
use rustc_trait_selection::traits::ObligationCtxt;
20+
use rustc_type_ir::elaborate;
1621

1722
mod builtin;
1823
mod inherent_impls;
@@ -223,5 +228,106 @@ fn check_object_overlap<'tcx>(
223228
}
224229
}
225230
}
231+
232+
even_cooler_object_overlap(tcx, impl_def_id, trait_ref.def_id);
233+
226234
Ok(())
227235
}
236+
237+
fn even_cooler_object_overlap<'tcx>(
238+
tcx: TyCtxt<'tcx>,
239+
impl_def_id: LocalDefId,
240+
trait_def_id: DefId,
241+
) {
242+
if !tcx.is_object_safe(trait_def_id) {
243+
return;
244+
}
245+
246+
let infcx = &tcx.infer_ctxt().with_next_trait_solver(true).intercrate(true).build();
247+
248+
let def_span = tcx.def_span(impl_def_id);
249+
let impl_args = infcx.fresh_args_for_item(def_span, impl_def_id.to_def_id());
250+
let trait_ref =
251+
tcx.impl_trait_ref(impl_def_id).expect("impl of trait").instantiate(tcx, impl_args);
252+
253+
let principal = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
254+
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
255+
));
256+
let mut assoc_tys = elaborate::supertraits(tcx, ty::Binder::dummy(trait_ref))
257+
.flat_map(|trait_ref| {
258+
tcx.associated_items(trait_ref.def_id())
259+
.in_definition_order()
260+
.filter(|assoc_item| assoc_item.kind == ty::AssocKind::Type)
261+
.filter(|assoc_item| !tcx.generics_require_sized_self(assoc_item.def_id))
262+
.map(move |assoc_item| {
263+
trait_ref.map_bound(|trait_ref| {
264+
ty::ExistentialPredicate::Projection(
265+
ty::ExistentialProjection::erase_self_ty(
266+
tcx,
267+
ty::ProjectionPredicate {
268+
projection_term: ty::AliasTerm::new(
269+
tcx,
270+
assoc_item.def_id,
271+
trait_ref.args,
272+
),
273+
term: infcx.next_ty_var(def_span).into(),
274+
},
275+
),
276+
)
277+
})
278+
})
279+
})
280+
.collect::<Vec<_>>();
281+
assoc_tys.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
282+
283+
let dyn_ty = Ty::new_dynamic(
284+
tcx,
285+
tcx.mk_poly_existential_predicates_from_iter([principal].into_iter().chain(assoc_tys)),
286+
infcx.next_region_var(RegionVariableOrigin::MiscVariable(def_span)),
287+
ty::Dyn,
288+
);
289+
290+
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
291+
let cause = ObligationCause::misc(def_span, impl_def_id);
292+
let Ok(()) = ocx.eq(&cause, ty::ParamEnv::empty(), trait_ref.self_ty(), dyn_ty) else {
293+
return;
294+
};
295+
296+
ocx.register_obligations(
297+
tcx.predicates_of(impl_def_id)
298+
.instantiate(tcx, impl_args)
299+
.into_iter()
300+
.map(|(clause, _)| Obligation::new(tcx, cause.clone(), ty::ParamEnv::empty(), clause)),
301+
);
302+
303+
let errors_and_ambiguities = ocx.select_all_or_error();
304+
// We only care about the obligations that are *definitely* true errors.
305+
// Ambiguities do not prove the disjointness of two impls.
306+
let (true_errors, _ambiguities): (Vec<_>, Vec<_>) =
307+
errors_and_ambiguities.into_iter().partition(|error| error.is_true_error());
308+
309+
if true_errors.is_empty() {
310+
let associated_type_spans: Vec<_> = tcx
311+
.associated_items(impl_def_id)
312+
.in_definition_order()
313+
.filter(|assoc_item| assoc_item.kind == ty::AssocKind::Type)
314+
.map(|assoc_item| tcx.def_span(assoc_item.def_id))
315+
.collect();
316+
if !associated_type_spans.is_empty() {
317+
tcx.dcx()
318+
.struct_span_err(
319+
def_span,
320+
format!(
321+
"this impl overlaps with built-in trait impl for `{}`",
322+
infcx.resolve_vars_if_possible(dyn_ty)
323+
),
324+
)
325+
.with_span_note(
326+
associated_type_spans,
327+
"this impl may be unsound because it could provide different values \
328+
for these associated types",
329+
)
330+
.emit();
331+
}
332+
}
333+
}

0 commit comments

Comments
 (0)