Skip to content

Commit fdf32a0

Browse files
DispatchFromDyn: require additional checks
The new checks are we check the pair of constituent types for same shapes structurally, and demand capability for dispatch for ADTs as usual; and we demand source generic parameter to be unsized of another for sanity. Signed-off-by: Xiangfei Ding <dingxiangfei2009@protonmail.ch>
1 parent a591113 commit fdf32a0

File tree

6 files changed

+166
-112
lines changed

6 files changed

+166
-112
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
149149
hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate
150150
.label = can't implement cross-crate trait for type in another crate
151151
152+
hir_analysis_dispatch_from_dyn_multiple_refs = the trait `DispatchFromDyn` does not allow dispatch through references
153+
.note = the trait `DispatchFromDyn` may only be implemented when dispatch goes through at most one reference to a generic
154+
152155
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
153156
154157
hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else

compiler/rustc_hir_analysis/src/coherence/builtin.rs

Lines changed: 143 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,15 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
203203
let tcx = checker.tcx;
204204
let impl_did = checker.impl_def_id;
205205
let trait_ref = checker.impl_header.trait_ref.instantiate_identity();
206+
assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn));
207+
let dispatch_from_dyn_trait_did = trait_ref.def_id;
206208
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
207209

208210
let span = tcx.def_span(impl_did);
209211
let trait_name = "DispatchFromDyn";
210212

211-
let source = trait_ref.self_ty();
212-
let target = {
213-
assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn));
214-
215-
trait_ref.args.type_at(1)
216-
};
213+
let mut source = trait_ref.self_ty();
214+
let mut target = trait_ref.args.type_at(1);
217215

218216
// Check `CoercePointee` impl is WF -- if not, then there's no reason to report
219217
// redundant errors for `DispatchFromDyn`. This is best effort, though.
@@ -242,138 +240,173 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
242240
// trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else
243241
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
244242
// even if they do not carry that attribute.
245-
match (source.kind(), target.kind()) {
246-
(&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
247-
if pat_a != pat_b {
248-
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
249-
span,
250-
trait_name,
251-
pat_a: pat_a.to_string(),
252-
pat_b: pat_b.to_string(),
253-
}));
243+
let mut behind_ref = false;
244+
loop {
245+
match (source.kind(), target.kind()) {
246+
(&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
247+
if pat_a != pat_b {
248+
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
249+
span,
250+
trait_name,
251+
pat_a: pat_a.to_string(),
252+
pat_b: pat_b.to_string(),
253+
}));
254+
}
255+
source = ty_a;
256+
target = ty_b;
254257
}
255-
Ok(())
256-
}
257258

258-
(&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
259-
if r_a == *r_b && mutbl_a == *mutbl_b =>
260-
{
261-
Ok(())
262-
}
263-
(&ty::RawPtr(_, a_mutbl), &ty::RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()),
264-
(&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))
265-
if def_a.is_struct() && def_b.is_struct() =>
266-
{
267-
if def_a != def_b {
268-
let source_path = tcx.def_path_str(def_a.did());
269-
let target_path = tcx.def_path_str(def_b.did());
270-
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
271-
span,
272-
trait_name,
273-
note: true,
274-
source_path,
275-
target_path,
276-
}));
259+
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b))
260+
if !behind_ref && r_a == r_b && mutbl_a == mutbl_b =>
261+
{
262+
source = ty_a;
263+
target = ty_b;
264+
behind_ref = true;
277265
}
278-
279-
if def_a.repr().c() || def_a.repr().packed() {
280-
return Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
266+
(&ty::RawPtr(ty_a, a_mutbl), &ty::RawPtr(ty_b, b_mutbl))
267+
if !behind_ref && a_mutbl == b_mutbl =>
268+
{
269+
source = ty_a;
270+
target = ty_b;
271+
behind_ref = true;
281272
}
273+
(&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))
274+
if !behind_ref && def_a.is_struct() && def_b.is_struct() =>
275+
{
276+
if def_a != def_b {
277+
let source_path = tcx.def_path_str(def_a.did());
278+
let target_path = tcx.def_path_str(def_b.did());
279+
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
280+
span,
281+
trait_name,
282+
note: true,
283+
source_path,
284+
target_path,
285+
}));
286+
}
282287

283-
let fields = &def_a.non_enum_variant().fields;
284-
285-
let mut res = Ok(());
286-
let coerced_fields = fields
287-
.iter_enumerated()
288-
.filter_map(|(i, field)| {
289-
// Ignore PhantomData fields
290-
let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();
291-
if tcx
292-
.try_normalize_erasing_regions(
293-
ty::TypingEnv::non_body_analysis(tcx, def_a.did()),
294-
unnormalized_ty,
295-
)
296-
.unwrap_or(unnormalized_ty)
297-
.is_phantom_data()
298-
{
299-
return None;
300-
}
288+
if def_a.repr().c() || def_a.repr().packed() {
289+
return Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
290+
}
301291

302-
let ty_a = field.ty(tcx, args_a);
303-
let ty_b = field.ty(tcx, args_b);
304-
305-
// FIXME: We could do normalization here, but is it really worth it?
306-
if ty_a == ty_b {
307-
// Allow 1-ZSTs that don't mention type params.
308-
//
309-
// Allowing type params here would allow us to possibly transmute
310-
// between ZSTs, which may be used to create library unsoundness.
311-
if let Ok(layout) =
312-
tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a))
313-
&& layout.is_1zst()
314-
&& !ty_a.has_non_region_param()
292+
let fields = &def_a.non_enum_variant().fields;
293+
294+
let coerced_fields: Vec<_> = fields
295+
.iter_enumerated()
296+
.filter_map(|(i, field)| {
297+
// Ignore PhantomData fields
298+
let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();
299+
if tcx
300+
.try_normalize_erasing_regions(
301+
ty::TypingEnv::non_body_analysis(tcx, def_a.did()),
302+
unnormalized_ty,
303+
)
304+
.unwrap_or(unnormalized_ty)
305+
.is_phantom_data()
315306
{
316-
// ignore 1-ZST fields
317307
return None;
318308
}
319309

320-
res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
321-
span,
322-
name: field.ident(tcx),
323-
ty: ty_a,
324-
}));
310+
let ty_a = field.ty(tcx, args_a);
311+
let ty_b = field.ty(tcx, args_b);
312+
313+
// FIXME: We could do normalization here, but is it really worth it?
314+
if ty_a == ty_b {
315+
// Allow 1-ZSTs that don't mention type params.
316+
//
317+
// Allowing type params here would allow us to possibly transmute
318+
// between ZSTs, which may be used to create library unsoundness.
319+
if let Ok(layout) =
320+
tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a))
321+
&& layout.is_1zst()
322+
&& !ty_a.has_non_region_param()
323+
{
324+
// ignore 1-ZST fields
325+
return None;
326+
}
325327

326-
None
327-
} else {
328-
Some((i, ty_a, ty_b, tcx.def_span(field.did)))
328+
Some(Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
329+
span,
330+
name: field.ident(tcx),
331+
ty: ty_a,
332+
})))
333+
} else {
334+
Some(Ok((i, ty_a, ty_b, tcx.def_span(field.did))))
335+
}
336+
})
337+
.collect::<Result<_, _>>()?;
338+
339+
if coerced_fields.is_empty() {
340+
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
341+
span,
342+
trait_name,
343+
note: true,
344+
}));
345+
}
346+
if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
347+
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
348+
ocx.register_obligation(Obligation::new(
349+
tcx,
350+
cause.clone(),
351+
param_env,
352+
ty::TraitRef::new(tcx, dispatch_from_dyn_trait_did, [ty_a, ty_b]),
353+
));
354+
let errors = ocx.evaluate_obligations_error_on_ambiguity();
355+
if !errors.is_empty() {
356+
if is_from_coerce_pointee_derive(tcx, span) {
357+
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
358+
span,
359+
trait_name,
360+
ty: trait_ref.self_ty(),
361+
field_span,
362+
field_ty: ty_a,
363+
}));
364+
} else {
365+
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
366+
}
329367
}
330-
})
331-
.collect::<Vec<_>>();
332-
res?;
333368

334-
if coerced_fields.is_empty() {
335-
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
369+
// Finally, resolve all regions.
370+
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
371+
372+
return Ok(());
373+
}
374+
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
336375
span,
337376
trait_name,
338-
note: true,
377+
number: coerced_fields.len(),
378+
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
339379
}));
340-
} else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
380+
}
381+
(ty::Param(_), ty::Param(_)) => {
341382
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
342383
ocx.register_obligation(Obligation::new(
343384
tcx,
344385
cause.clone(),
345386
param_env,
346-
ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]),
387+
ty::TraitRef::new(
388+
tcx,
389+
tcx.require_lang_item(LangItem::Unsize, span),
390+
[source, target],
391+
),
347392
));
348393
let errors = ocx.evaluate_obligations_error_on_ambiguity();
349394
if !errors.is_empty() {
350-
if is_from_coerce_pointee_derive(tcx, span) {
351-
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
352-
span,
353-
trait_name,
354-
ty: trait_ref.self_ty(),
355-
field_span,
356-
field_ty: ty_a,
357-
}));
358-
} else {
359-
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
360-
}
395+
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
361396
}
362-
363-
// Finally, resolve all regions.
364397
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
365-
366-
Ok(())
367-
} else {
368-
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
369-
span,
370-
trait_name,
371-
number: coerced_fields.len(),
372-
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
373-
}));
398+
return Ok(());
399+
}
400+
_ => {
401+
if behind_ref {
402+
return Err(tcx.dcx().emit_err(errors::DispatchFromDynMultiRefs { span }));
403+
} else {
404+
return Err(tcx
405+
.dcx()
406+
.emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));
407+
}
374408
}
375409
}
376-
_ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),
377410
}
378411
}
379412

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,13 @@ pub(crate) struct CoerceSamePatKind {
12581258
pub pat_b: String,
12591259
}
12601260

1261+
#[derive(Diagnostic)]
1262+
#[diag(hir_analysis_dispatch_from_dyn_multiple_refs)]
1263+
pub(crate) struct DispatchFromDynMultiRefs {
1264+
#[primary_span]
1265+
pub span: Span,
1266+
}
1267+
12611268
#[derive(Diagnostic)]
12621269
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
12631270
pub(crate) struct CoerceSameStruct {

library/core/src/pat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<pattern_type!(*const U is !
8282
{
8383
}
8484

85-
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
85+
impl<T: DispatchFromDyn<U> + Unsize<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
8686

8787
impl<T: PointeeSized> Unpin for pattern_type!(*const T is !null) {}
8888

tests/ui/traits/dispatch-from-dyn-invalid-impls.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,9 @@ where
6868
{
6969
}
7070

71+
pub struct Ptr<T: ?Sized>(PhantomData<T>, Box<T>);
72+
73+
impl<'a, T: ?Sized, U: ?Sized> DispatchFromDyn<&'a Ptr<U>> for &'a Ptr<T> {}
74+
//~^ ERROR the trait `DispatchFromDyn` does not allow dispatch through references
75+
7176
fn main() {}

tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@ LL | | T: Unsize<U>
5454
|
5555
= note: extra field `1` of type `OverAlignedZst` is not allowed
5656

57-
error: aborting due to 5 previous errors
57+
error: the trait `DispatchFromDyn` does not allow dispatch through references
58+
--> $DIR/dispatch-from-dyn-invalid-impls.rs:73:1
59+
|
60+
LL | impl<'a, T: ?Sized, U: ?Sized> DispatchFromDyn<&'a Ptr<U>> for &'a Ptr<T> {}
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
63+
error: aborting due to 6 previous errors
5864

5965
Some errors have detailed explanations: E0374, E0375, E0378.
6066
For more information about an error, try `rustc --explain E0374`.

0 commit comments

Comments
 (0)