Skip to content

Commit 915224f

Browse files
committed
WIP: basic outlines and some PoCs
1 parent b231422 commit 915224f

File tree

6 files changed

+267
-3
lines changed

6 files changed

+267
-3
lines changed

crates/hir-ty/src/db.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use base_db::{
1111
use hir_def::{
1212
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId,
1313
ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
14-
LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
14+
LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId,
1515
};
1616
use la_arena::ArenaMap;
1717
use smallvec::SmallVec;
@@ -24,6 +24,7 @@ use crate::{
2424
lower::{GenericDefaults, GenericPredicates},
2525
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
2626
mir::{BorrowckResult, MirBody, MirLowerError},
27+
object_safety::{ObjectSafetyError, ObjectSafetyViolation},
2728
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
2829
PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
2930
};
@@ -104,6 +105,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
104105
#[salsa::cycle(crate::layout::layout_of_ty_recover)]
105106
fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>;
106107

108+
#[salsa::invoke(crate::object_safety::object_safety_of_trait_query)]
109+
fn object_safety_of_trait(
110+
&self,
111+
trait_: TraitId,
112+
) -> Result<Option<ObjectSafetyViolation>, ObjectSafetyError>;
113+
107114
#[salsa::invoke(crate::layout::target_data_layout_query)]
108115
fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
109116

crates/hir-ty/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod lang_items;
4040
pub mod layout;
4141
pub mod method_resolution;
4242
pub mod mir;
43+
pub mod object_safety;
4344
pub mod primitive;
4445
pub mod traits;
4546

crates/hir-ty/src/object_safety.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use chalk_ir::{DebruijnIndex, WhereClause};
2+
use hir_def::{
3+
lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId,
4+
TypeAliasId,
5+
};
6+
7+
use crate::{
8+
all_super_traits, db::HirDatabase, generics::generics, layout::LayoutError,
9+
lower::callable_item_sig, make_single_type_binders, static_lifetime, wrap_empty_binders, DynTy,
10+
Interner, QuantifiedWhereClauses, Substitution, TyBuilder, TyKind,
11+
};
12+
13+
#[derive(Debug, Clone, PartialEq, Eq)]
14+
pub enum ObjectSafetyError {
15+
LayoutError(LayoutError),
16+
}
17+
18+
#[derive(Debug, Clone, PartialEq, Eq)]
19+
pub enum ObjectSafetyViolation {
20+
SizedSelf,
21+
SelfReferencial,
22+
NonLifetimeBinder,
23+
Method(FunctionId, MethodViolationCode),
24+
AssocConst(ConstId),
25+
GAT(TypeAliasId),
26+
// This doesn't exist in rustc, but added for better visualization
27+
HasNonSafeSuperTrait(TraitId),
28+
}
29+
30+
#[derive(Debug, Clone, PartialEq, Eq)]
31+
pub enum MethodViolationCode {
32+
StaticMethod,
33+
ReferencesSelfInput,
34+
ReferencesSelfOutput,
35+
ReferencesImplTraitInTrait,
36+
AsyncFn,
37+
WhereClauseReferencesSelf,
38+
Generic,
39+
UndispatchableReceiver,
40+
HasNonLifetimeTypeParam,
41+
NonReceiverSelfParam,
42+
}
43+
44+
// Basically, this is almost same as `rustc_trait_selection::traits::object_safety`
45+
// but some difference;
46+
//
47+
// 1. While rustc gathers almost every violation, but this only early return on
48+
// first violation for perf.
49+
//
50+
// These can be changed anytime while implementing.
51+
pub fn object_safety_of_trait_query(
52+
db: &dyn HirDatabase,
53+
trait_: TraitId,
54+
) -> Result<Option<ObjectSafetyViolation>, ObjectSafetyError> {
55+
for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1) {
56+
if db.object_safety_of_trait(super_trait)?.is_some() {
57+
return Ok(Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait)));
58+
}
59+
}
60+
61+
if generics_require_sized_self(db, trait_.into()) {
62+
return Ok(Some(ObjectSafetyViolation::SizedSelf));
63+
}
64+
65+
// TODO: bound referencing self
66+
67+
// TODO: non lifetime binder
68+
69+
let trait_data = db.trait_data(trait_);
70+
for (_, assoc_item) in &trait_data.items {
71+
let item_violation = object_safety_violation_for_assoc_item(db, trait_, *assoc_item)?;
72+
if item_violation.is_some() {
73+
return Ok(item_violation);
74+
}
75+
}
76+
77+
Ok(None)
78+
}
79+
80+
fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool {
81+
let krate = def.module(db.upcast()).krate();
82+
let Some(_sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else {
83+
return false;
84+
};
85+
86+
let _predicates = db.generic_predicates(def);
87+
// TODO: elaborate with `utils::elaborate_clause_supertraits` and check `Self: Sized`
88+
89+
false
90+
}
91+
92+
fn object_safety_violation_for_assoc_item(
93+
db: &dyn HirDatabase,
94+
trait_: TraitId,
95+
item: AssocItemId,
96+
) -> Result<Option<ObjectSafetyViolation>, ObjectSafetyError> {
97+
match item {
98+
AssocItemId::ConstId(it) => Ok(Some(ObjectSafetyViolation::AssocConst(it))),
99+
AssocItemId::FunctionId(it) => virtual_call_violations_for_method(db, trait_, it)
100+
.map(|v| v.map(|v| ObjectSafetyViolation::Method(it, v)))
101+
.map_err(ObjectSafetyError::LayoutError),
102+
AssocItemId::TypeAliasId(it) => {
103+
let generics = generics(db.upcast(), it.into());
104+
// rustc checks if the `generic_associate_type_extended` feature gate is set
105+
if generics.len_self() > 0 && db.type_alias_impl_traits(it).is_none() {
106+
Ok(Some(ObjectSafetyViolation::GAT(it)))
107+
} else {
108+
Ok(None)
109+
}
110+
}
111+
}
112+
}
113+
114+
fn virtual_call_violations_for_method(
115+
db: &dyn HirDatabase,
116+
trait_: TraitId,
117+
func: FunctionId,
118+
) -> Result<Option<MethodViolationCode>, LayoutError> {
119+
let func_data = db.function_data(func);
120+
if !func_data.has_self_param() {
121+
return Ok(Some(MethodViolationCode::StaticMethod));
122+
}
123+
124+
// TODO: check self reference in params
125+
126+
// TODO: check self reference in return type
127+
128+
// TODO: check asyncness, RPIT
129+
130+
let generic_params = db.generic_params(func.into());
131+
if generic_params.len_type_or_consts() > 0 {
132+
return Ok(Some(MethodViolationCode::Generic));
133+
}
134+
135+
// Check if the receiver is a correct type like `Self`, `Box<Self>`, `Arc<Self>`, etc
136+
//
137+
// TODO: rustc does this in two steps :thinking_face:
138+
// I'm doing only the second, real one, layout check
139+
// TODO: clean all the messes for building receiver types to check layout of
140+
141+
// Check for types like `Rc<()>`
142+
let sig = callable_item_sig(db, func.into());
143+
// TODO: Getting receiver type that substituted `Self` by `()`. there might be more clever way?
144+
let subst = Substitution::from_iter(
145+
Interner,
146+
std::iter::repeat(TyBuilder::unit()).take(sig.len(Interner)),
147+
);
148+
let sig = sig.substitute(Interner, &subst);
149+
let receiver_ty = sig.params()[0].to_owned();
150+
let layout = db.layout_of_ty(receiver_ty, db.trait_environment(trait_.into()))?;
151+
152+
if !matches!(layout.abi, rustc_abi::Abi::Scalar(..)) {
153+
return Ok(Some(MethodViolationCode::UndispatchableReceiver));
154+
}
155+
156+
// Check for types like `Rc<dyn Trait>`
157+
// TODO: `dyn Trait` and receiver type building is a total mess
158+
let trait_ref =
159+
TyBuilder::trait_ref(db, trait_).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
160+
let bound = wrap_empty_binders(WhereClause::Implemented(trait_ref));
161+
let bounds = QuantifiedWhereClauses::from_iter(Interner, [bound]);
162+
let dyn_trait = TyKind::Dyn(DynTy {
163+
bounds: make_single_type_binders(bounds),
164+
lifetime: static_lifetime(),
165+
})
166+
.intern(Interner);
167+
let sig = callable_item_sig(db, func.into());
168+
let subst = Substitution::from_iter(
169+
Interner,
170+
std::iter::once(dyn_trait)
171+
.chain(std::iter::repeat(TyBuilder::unit()))
172+
.take(sig.len(Interner)),
173+
);
174+
let sig = sig.substitute(Interner, &subst);
175+
let receiver_ty = sig.params()[0].to_owned();
176+
let layout = db.layout_of_ty(receiver_ty, db.trait_environment(trait_.into()))?;
177+
178+
if !matches!(layout.abi, rustc_abi::Abi::ScalarPair(..)) {
179+
return Ok(Some(MethodViolationCode::UndispatchableReceiver));
180+
}
181+
182+
Ok(None)
183+
}

crates/hir/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use hir_ty::{
6666
diagnostics::BodyValidationDiagnostic,
6767
error_lifetime, known_const_to_ast,
6868
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
69-
method_resolution::{self},
69+
method_resolution,
7070
mir::{interpret_mir, MutBorrowKind},
7171
primitive::UintTy,
7272
traits::FnTrait,
@@ -145,6 +145,7 @@ pub use {
145145
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
146146
layout::LayoutError,
147147
mir::{MirEvalError, MirLowerError},
148+
object_safety::{MethodViolationCode, ObjectSafetyError, ObjectSafetyViolation},
148149
FnAbi, PointerCast, Safety,
149150
},
150151
// FIXME: Properly encapsulate mir
@@ -2625,6 +2626,13 @@ impl Trait {
26252626
.count()
26262627
}
26272628

2629+
pub fn object_safety(
2630+
&self,
2631+
db: &dyn HirDatabase,
2632+
) -> Result<Option<ObjectSafetyViolation>, ObjectSafetyError> {
2633+
db.object_safety_of_trait(self.id)
2634+
}
2635+
26282636
fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
26292637
db.trait_data(self.id)
26302638
.macro_calls

crates/ide/src/hover/render.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{mem, ops::Not};
44
use either::Either;
55
use hir::{
66
Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout,
7-
LayoutError, Name, Semantics, Trait, Type, TypeInfo,
7+
LayoutError, Name, ObjectSafetyError, ObjectSafetyViolation, Semantics, Trait, Type, TypeInfo,
88
};
99
use ide_db::{
1010
base_db::SourceDatabase,
@@ -509,6 +509,12 @@ pub(super) fn definition(
509509
_ => None,
510510
};
511511

512+
let object_safety_info = if let Definition::Trait(it) = def {
513+
render_object_safety(it.object_safety(db))
514+
} else {
515+
None
516+
};
517+
512518
let mut desc = String::new();
513519
if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits) {
514520
desc.push_str(&notable_traits);
@@ -518,6 +524,10 @@ pub(super) fn definition(
518524
desc.push_str(&layout_info);
519525
desc.push('\n');
520526
}
527+
if let Some(object_safety_info) = object_safety_info {
528+
desc.push_str(&object_safety_info);
529+
desc.push('\n');
530+
}
521531
desc.push_str(&label);
522532
if let Some(value) = value {
523533
desc.push_str(" = ");
@@ -934,3 +944,10 @@ fn keyword_hints(
934944
_ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())),
935945
}
936946
}
947+
948+
fn render_object_safety(
949+
safety: Result<Option<ObjectSafetyViolation>, ObjectSafetyError>,
950+
) -> Option<String> {
951+
// TODO: not implemened
952+
Some(format!("{safety:?}"))
953+
}

crates/ide/src/hover/tests.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8579,3 +8579,51 @@ fn main(a$0: T) {}
85798579
"#]],
85808580
);
85818581
}
8582+
8583+
#[test]
8584+
fn debug_object_safety_1() {
8585+
check(
8586+
r#"
8587+
struct Box<T>(*const T);
8588+
trait Foo$0<'a, T, U> {
8589+
fn foo(self: Box<Self>);
8590+
}
8591+
"#,
8592+
expect![[r#"
8593+
*Foo*
8594+
8595+
```rust
8596+
test
8597+
```
8598+
8599+
```rust
8600+
Ok(None)
8601+
trait Foo<'a, T, U>
8602+
```
8603+
"#]],
8604+
);
8605+
}
8606+
8607+
#[test]
8608+
fn debug_object_safety_2() {
8609+
check(
8610+
r#"
8611+
struct NonBox<T>(T);
8612+
trait Foo$0<T, U> {
8613+
fn check(self: NonBox<Self>);
8614+
}
8615+
"#,
8616+
expect![[r#"
8617+
*Foo*
8618+
8619+
```rust
8620+
test
8621+
```
8622+
8623+
```rust
8624+
Ok(Some(Method(FunctionId(0), UndispatchableReceiver)))
8625+
trait Foo<T, U>
8626+
```
8627+
"#]],
8628+
);
8629+
}

0 commit comments

Comments
 (0)