Skip to content

Commit baf2b38

Browse files
committed
add coherence check for FRTs
1 parent a75ba70 commit baf2b38

File tree

6 files changed

+176
-1
lines changed

6 files changed

+176
-1
lines changed

compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
//! `tcx.inherent_impls(def_id)`). That value, however,
88
//! is computed by selecting an idea from this table.
99
10+
use std::ops::ControlFlow;
11+
1012
use rustc_hir as hir;
1113
use rustc_hir::attrs::AttributeKind;
1214
use rustc_hir::def::DefKind;
1315
use rustc_hir::def_id::{DefId, LocalDefId};
1416
use rustc_hir::find_attr;
1517
use rustc_middle::bug;
1618
use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type};
17-
use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
19+
use rustc_middle::ty::{self, CrateInherentImpls, FieldPath, Ty, TyCtxt};
1820
use rustc_span::{ErrorGuaranteed, sym};
1921

2022
use crate::errors;
@@ -112,6 +114,40 @@ impl<'tcx> InherentCollect<'tcx> {
112114
}
113115
}
114116

117+
fn check_field_type(
118+
&mut self,
119+
impl_def_id: LocalDefId,
120+
container: Ty<'tcx>,
121+
field_path: FieldPath<'tcx>,
122+
) -> Result<(), ErrorGuaranteed> {
123+
if !matches!(container.kind(), ty::Adt(..)) {
124+
return Err(self
125+
.tcx
126+
.dcx()
127+
.emit_err(errors::InherentTyOutsideNew { span: self.tcx.def_span(impl_def_id) }));
128+
}
129+
if field_path
130+
.walk(self.tcx, container, |base, _, _, _| {
131+
if let ty::Adt(def, _) = base.kind()
132+
&& !def.did().is_local()
133+
{
134+
ControlFlow::Break(())
135+
} else {
136+
ControlFlow::Continue(())
137+
}
138+
})
139+
.is_none()
140+
{
141+
self.impls_map
142+
.incoherent_impls
143+
.entry(SimplifiedType::Field)
144+
.or_default()
145+
.push(impl_def_id);
146+
}
147+
148+
Ok(())
149+
}
150+
115151
fn check_primitive_impl(
116152
&mut self,
117153
impl_def_id: LocalDefId,
@@ -166,6 +202,7 @@ impl<'tcx> InherentCollect<'tcx> {
166202
}
167203
match *self_ty.kind() {
168204
ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
205+
ty::Field(container, field_path) => self.check_field_type(id, container, field_path),
169206
ty::Foreign(did) => self.check_def_id(id, self_ty, did),
170207
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
171208
self.check_def_id(id, self_ty, data.principal_def_id().unwrap())

compiler/rustc_hir_analysis/src/coherence/orphan.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Orphan checker: every impl either implements a trait defined in this
22
//! crate or pertains to a type defined in this crate.
33
4+
use std::ops::ControlFlow;
5+
46
use rustc_data_structures::fx::FxIndexSet;
57
use rustc_errors::ErrorGuaranteed;
68
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
@@ -151,6 +153,34 @@ pub(crate) fn orphan_check_impl(
151153
},
152154
),
153155

156+
ty::Field(container, field_path) => {
157+
let non_local_impl = if !matches!(container.kind(), ty::Adt(..)) {
158+
NonlocalImpl::DisallowBecauseNonlocal
159+
} else {
160+
field_path
161+
.walk(tcx, *container, |base, _, _, _| match base.kind() {
162+
ty::Adt(def, _) => {
163+
// We don't check the field type for locality, since having a non-local
164+
// type as a field in a struct shouldn't make the field type non-local.
165+
if !def.did().is_local() {
166+
ControlFlow::Break(NonlocalImpl::DisallowBecauseNonlocal)
167+
} else {
168+
ControlFlow::Continue(())
169+
}
170+
}
171+
// Tuples are the only exception.
172+
ty::Tuple(..) => ControlFlow::Continue(()),
173+
_ => {
174+
panic!(
175+
"only adts & tuples are supported by `field_of!` but found: {base}"
176+
)
177+
}
178+
})
179+
.unwrap_or(NonlocalImpl::Allow)
180+
};
181+
(LocalImpl::Allow, non_local_impl)
182+
}
183+
154184
// extern { type OpaqueType; }
155185
// impl AutoTrait for OpaqueType {}
156186
ty::Foreign(did) => (

compiler/rustc_next_trait_solver/src/coherence.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,31 @@ where
419419
self.found_non_local_ty(ty)
420420
}
421421
}
422+
ty::Field(container, field_path) => {
423+
if !matches!(container.kind(), ty::Adt(..)) {
424+
self.found_non_local_ty(container)
425+
} else {
426+
field_path
427+
.walk(self.infcx.cx(), container, |base, _, _, _| match base.kind() {
428+
ty::Adt(def, _) => {
429+
// We don't check the field type for locality, since having a non-local
430+
// type as a field in a struct shouldn't make the field type non-local.
431+
if !self.def_id_is_local(def.def_id()) {
432+
ControlFlow::Break(self.found_non_local_ty(base))
433+
} else {
434+
ControlFlow::Continue(())
435+
}
436+
}
437+
// Tuples are the only exception.
438+
ty::Tuple(..) => ControlFlow::Continue(()),
439+
_ => {
440+
// FIXME(field_projections): no `bug!` available?
441+
panic!("field_path should only consist of tuples and structs for `ty::Field`, but found {base:?}")
442+
}
443+
})
444+
.unwrap_or(ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(container)))
445+
}
446+
}
422447
ty::Foreign(def_id) => {
423448
if self.def_id_is_local(def_id) {
424449
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub struct Point {
2+
pub x: usize,
3+
pub y: usize,
4+
}
5+
6+
pub trait ForeignTrait {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ aux-build:extern-crate.rs
2+
#![allow(incomplete_features)]
3+
#![feature(field_projections)]
4+
extern crate extern_crate;
5+
6+
use std::field::field_of;
7+
8+
use extern_crate::{ForeignTrait, Point};
9+
10+
pub trait MyTrait {}
11+
12+
impl MyTrait for field_of!(Point, x) {}
13+
14+
impl extern_crate::ForeignTrait for field_of!(Point, x) {}
15+
//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117]
16+
17+
pub struct Player {
18+
pos: Point,
19+
hp: (u32, u32),
20+
}
21+
22+
impl ForeignTrait for field_of!(Player, pos) {}
23+
24+
impl ForeignTrait for field_of!(Player, pos.x) {}
25+
//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117]
26+
27+
impl MyTrait for field_of!(Player, pos) {}
28+
29+
impl MyTrait for field_of!(Player, pos.x) {}
30+
31+
impl MyTrait for field_of!((usize, usize), 0) {}
32+
33+
impl ForeignTrait for field_of!((usize, usize), 0) {}
34+
//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117]
35+
36+
impl ForeignTrait for field_of!(Player, hp.0) {}
37+
38+
fn main() {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
2+
--> $DIR/incoherent-impl.rs:14:1
3+
|
4+
LL | impl extern_crate::ForeignTrait for field_of!(Point, x) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------------
6+
| |
7+
| `Point` is not defined in the current crate
8+
|
9+
= note: impl doesn't have any local type before any uncovered type parameters
10+
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
11+
= note: define and implement a trait or new type instead
12+
13+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
14+
--> $DIR/incoherent-impl.rs:24:1
15+
|
16+
LL | impl ForeignTrait for field_of!(Player, pos.x) {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^------------------------
18+
| |
19+
| `Point` is not defined in the current crate
20+
|
21+
= note: impl doesn't have any local type before any uncovered type parameters
22+
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
23+
= note: define and implement a trait or new type instead
24+
25+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
26+
--> $DIR/incoherent-impl.rs:33:1
27+
|
28+
LL | impl ForeignTrait for field_of!((usize, usize), 0) {}
29+
| ^^^^^^^^^^^^^^^^^^^^^^----------------------------
30+
| |
31+
| this is not defined in the current crate because tuples are always foreign
32+
|
33+
= note: impl doesn't have any local type before any uncovered type parameters
34+
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
35+
= note: define and implement a trait or new type instead
36+
37+
error: aborting due to 3 previous errors
38+
39+
For more information about this error, try `rustc --explain E0117`.

0 commit comments

Comments
 (0)