Skip to content

Commit fbed195

Browse files
authored
Rollup merge of #133226 - compiler-errors:opt-in-pointer-like, r=lcnr
Make `PointerLike` opt-in instead of built-in The `PointerLike` trait currently is a built-in trait that computes the layout of the type. This is a bit problematic, because types implement this trait automatically. Since this can be broken due to semver-compatible changes to a type's layout, this is undesirable. Also, calling `layout_of` in the trait system also causes cycles. This PR makes the trait implemented via regular impls, and adds additional validation on top to make sure that those impls are valid. This could eventually be `derive()`d for custom smart pointers, and we can trust *that* as a semver promise rather than risking library authors accidentally breaking it. On the other hand, we may never expose `PointerLike`, but at least now the implementation doesn't invoke `layout_of` which could cause ICEs or cause cycles. Right now for a `PointerLike` impl to be valid, it must be an ADT that is `repr(transparent)` and the non-1zst field needs to implement `PointerLike`. There are also some primitive impls for `&T`/ `&mut T`/`*const T`/`*mut T`/`Box<T>`.
2 parents 7fc2b33 + 228068b commit fbed195

File tree

25 files changed

+195
-156
lines changed

25 files changed

+195
-156
lines changed

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+71-14
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,19 @@ pub(super) fn check_trait<'tcx>(
3737
) -> Result<(), ErrorGuaranteed> {
3838
let lang_items = tcx.lang_items();
3939
let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
40-
let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop);
41-
res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy));
42-
res = res.and(checker.check(lang_items.const_param_ty_trait(), |checker| {
40+
checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;
41+
checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
42+
checker.check(lang_items.const_param_ty_trait(), |checker| {
4343
visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy)
44-
}));
45-
res = res.and(checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
44+
})?;
45+
checker.check(lang_items.unsized_const_param_ty_trait(), |checker| {
4646
visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy)
47-
}));
48-
49-
res = res.and(
50-
checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized),
51-
);
52-
res.and(
53-
checker
54-
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn),
55-
)
47+
})?;
48+
checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?;
49+
checker
50+
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;
51+
checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?;
52+
Ok(())
5653
}
5754

5855
struct Checker<'tcx> {
@@ -663,3 +660,63 @@ fn infringing_fields_error<'tcx>(
663660

664661
err.emit()
665662
}
663+
664+
fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
665+
let tcx = checker.tcx;
666+
let typing_env = ty::TypingEnv::non_body_analysis(tcx, checker.impl_def_id);
667+
let impl_span = tcx.def_span(checker.impl_def_id);
668+
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
669+
670+
// If an ADT is repr(transparent)...
671+
if let ty::Adt(def, args) = *self_ty.kind()
672+
&& def.repr().transparent()
673+
{
674+
// FIXME(compiler-errors): This should and could be deduplicated into a query.
675+
// Find the nontrivial field.
676+
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did());
677+
let nontrivial_field = def.all_fields().find(|field_def| {
678+
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
679+
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
680+
.is_ok_and(|layout| layout.layout.is_1zst())
681+
});
682+
683+
if let Some(nontrivial_field) = nontrivial_field {
684+
// Check that the nontrivial field implements `PointerLike`.
685+
let nontrivial_field = nontrivial_field.ty(tcx, args);
686+
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
687+
let ocx = ObligationCtxt::new(&infcx);
688+
ocx.register_bound(
689+
ObligationCause::misc(impl_span, checker.impl_def_id),
690+
param_env,
691+
nontrivial_field,
692+
tcx.lang_items().pointer_like().unwrap(),
693+
);
694+
// FIXME(dyn-star): We should regionck this implementation.
695+
if ocx.select_all_or_error().is_empty() {
696+
return Ok(());
697+
}
698+
}
699+
}
700+
701+
let is_permitted_primitive = match *self_ty.kind() {
702+
ty::Adt(def, _) => def.is_box(),
703+
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
704+
_ => false,
705+
};
706+
707+
if is_permitted_primitive
708+
&& let Ok(layout) = tcx.layout_of(typing_env.as_query_input(self_ty))
709+
&& layout.layout.is_pointer_like(&tcx.data_layout)
710+
{
711+
return Ok(());
712+
}
713+
714+
Err(tcx
715+
.dcx()
716+
.struct_span_err(
717+
impl_span,
718+
"implementation must be applied to type that has the same ABI as a pointer, \
719+
or is `repr(transparent)` and whose field is `PointerLike`",
720+
)
721+
.emit())
722+
}

compiler/rustc_middle/src/ty/context.rs

-14
Original file line numberDiff line numberDiff line change
@@ -602,19 +602,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
602602
self.coroutine_is_async_gen(coroutine_def_id)
603603
}
604604

605-
// We don't use `TypingEnv` here as it's only defined in `rustc_middle` and
606-
// `rustc_next_trait_solver` shouldn't have to know about it.
607-
fn layout_is_pointer_like(
608-
self,
609-
typing_mode: ty::TypingMode<'tcx>,
610-
param_env: ty::ParamEnv<'tcx>,
611-
ty: Ty<'tcx>,
612-
) -> bool {
613-
let typing_env = ty::TypingEnv { typing_mode, param_env };
614-
self.layout_of(self.erase_regions(typing_env).as_query_input(self.erase_regions(ty)))
615-
.is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout))
616-
}
617-
618605
type UnsizingParams = &'tcx rustc_index::bit_set::BitSet<u32>;
619606
fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams {
620607
self.unsizing_params_for_adt(adt_def_id)
@@ -688,7 +675,6 @@ bidirectional_lang_item_map! {
688675
Metadata,
689676
Option,
690677
PointeeTrait,
691-
PointerLike,
692678
Poll,
693679
Sized,
694680
TransmuteTrait,

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

-10
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,6 @@ where
159159
goal: Goal<I, Self>,
160160
) -> Result<Candidate<I>, NoSolution>;
161161

162-
/// A type is `PointerLike` if we can compute its layout, and that layout
163-
/// matches the layout of `usize`.
164-
fn consider_builtin_pointer_like_candidate(
165-
ecx: &mut EvalCtxt<'_, D>,
166-
goal: Goal<I, Self>,
167-
) -> Result<Candidate<I>, NoSolution>;
168-
169162
/// A type is a `FnPtr` if it is of `FnPtr` type.
170163
fn consider_builtin_fn_ptr_trait_candidate(
171164
ecx: &mut EvalCtxt<'_, D>,
@@ -449,9 +442,6 @@ where
449442
ty::ClosureKind::FnOnce,
450443
)
451444
}
452-
Some(TraitSolverLangItem::PointerLike) => {
453-
G::consider_builtin_pointer_like_candidate(self, goal)
454-
}
455445
Some(TraitSolverLangItem::FnPtrTrait) => {
456446
G::consider_builtin_fn_ptr_trait_candidate(self, goal)
457447
}

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

-7
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,6 @@ where
210210
Err(NoSolution)
211211
}
212212

213-
fn consider_builtin_pointer_like_candidate(
214-
_ecx: &mut EvalCtxt<'_, D>,
215-
_goal: Goal<I, Self>,
216-
) -> Result<Candidate<I>, NoSolution> {
217-
unreachable!("PointerLike is not const")
218-
}
219-
220213
fn consider_builtin_fn_ptr_trait_candidate(
221214
_ecx: &mut EvalCtxt<'_, D>,
222215
_goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

-7
Original file line numberDiff line numberDiff line change
@@ -363,13 +363,6 @@ where
363363
panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
364364
}
365365

366-
fn consider_builtin_pointer_like_candidate(
367-
_ecx: &mut EvalCtxt<'_, D>,
368-
goal: Goal<I, Self>,
369-
) -> Result<Candidate<I>, NoSolution> {
370-
panic!("`PointerLike` does not have an associated type: {:?}", goal);
371-
}
372-
373366
fn consider_builtin_fn_ptr_trait_candidate(
374367
_ecx: &mut EvalCtxt<'_, D>,
375368
goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

-26
Original file line numberDiff line numberDiff line change
@@ -248,32 +248,6 @@ where
248248
)
249249
}
250250

251-
fn consider_builtin_pointer_like_candidate(
252-
ecx: &mut EvalCtxt<'_, D>,
253-
goal: Goal<I, Self>,
254-
) -> Result<Candidate<I>, NoSolution> {
255-
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
256-
return Err(NoSolution);
257-
}
258-
259-
let cx = ecx.cx();
260-
// But if there are inference variables, we have to wait until it's resolved.
261-
if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() {
262-
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
263-
}
264-
265-
if cx.layout_is_pointer_like(
266-
ecx.typing_mode(goal.param_env),
267-
goal.param_env,
268-
goal.predicate.self_ty(),
269-
) {
270-
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
271-
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
272-
} else {
273-
Err(NoSolution)
274-
}
275-
}
276-
277251
fn consider_builtin_fn_ptr_trait_candidate(
278252
ecx: &mut EvalCtxt<'_, D>,
279253
goal: Goal<I, Self>,

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

-31
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
111111
self.assemble_candidates_for_transmutability(obligation, &mut candidates);
112112
} else if tcx.is_lang_item(def_id, LangItem::Tuple) {
113113
self.assemble_candidate_for_tuple(obligation, &mut candidates);
114-
} else if tcx.is_lang_item(def_id, LangItem::PointerLike) {
115-
self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
116114
} else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) {
117115
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
118116
} else {
@@ -1216,35 +1214,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12161214
}
12171215
}
12181216

1219-
fn assemble_candidate_for_pointer_like(
1220-
&mut self,
1221-
obligation: &PolyTraitObligation<'tcx>,
1222-
candidates: &mut SelectionCandidateSet<'tcx>,
1223-
) {
1224-
// The regions of a type don't affect the size of the type
1225-
let tcx = self.tcx();
1226-
let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty());
1227-
1228-
// But if there are inference variables, we have to wait until it's resolved.
1229-
if (obligation.param_env, self_ty).has_non_region_infer() {
1230-
candidates.ambiguous = true;
1231-
return;
1232-
}
1233-
1234-
// We should erase regions from both the param-env and type, since both
1235-
// may have infer regions. Specifically, after canonicalizing and instantiating,
1236-
// early bound regions turn into region vars in both the new and old solver.
1237-
let key = self.infcx.pseudo_canonicalize_query(
1238-
tcx.erase_regions(obligation.param_env),
1239-
tcx.erase_regions(self_ty),
1240-
);
1241-
if let Ok(layout) = tcx.layout_of(key)
1242-
&& layout.layout.is_pointer_like(&tcx.data_layout)
1243-
{
1244-
candidates.vec.push(BuiltinCandidate { has_nested: false });
1245-
}
1246-
}
1247-
12481217
fn assemble_candidates_for_fn_ptr_trait(
12491218
&mut self,
12501219
obligation: &PolyTraitObligation<'tcx>,

compiler/rustc_type_ir/src/interner.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::lang_items::TraitSolverLangItem;
1313
use crate::relate::Relate;
1414
use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult};
1515
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
16-
use crate::{self as ty, TypingMode, search_graph};
16+
use crate::{self as ty, search_graph};
1717

1818
pub trait Interner:
1919
Sized
@@ -278,13 +278,6 @@ pub trait Interner:
278278
fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool;
279279
fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool;
280280

281-
fn layout_is_pointer_like(
282-
self,
283-
typing_mode: TypingMode<Self>,
284-
param_env: Self::ParamEnv,
285-
ty: Self::Ty,
286-
) -> bool;
287-
288281
type UnsizingParams: Deref<Target = BitSet<u32>>;
289282
fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams;
290283

compiler/rustc_type_ir/src/lang_items.rs

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ pub enum TraitSolverLangItem {
3131
Metadata,
3232
Option,
3333
PointeeTrait,
34-
PointerLike,
3534
Poll,
3635
Sized,
3736
TransmuteTrait,

library/alloc/src/boxed.rs

+6
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ use core::error::{self, Error};
191191
use core::fmt;
192192
use core::future::Future;
193193
use core::hash::{Hash, Hasher};
194+
#[cfg(not(bootstrap))]
195+
use core::marker::PointerLike;
194196
use core::marker::{Tuple, Unsize};
195197
use core::mem::{self, SizedTypeProperties};
196198
use core::ops::{
@@ -2131,3 +2133,7 @@ impl<E: Error> Error for Box<E> {
21312133
Error::provide(&**self, request);
21322134
}
21332135
}
2136+
2137+
#[cfg(not(bootstrap))]
2138+
#[unstable(feature = "pointer_like_trait", issue = "none")]
2139+
impl<T> PointerLike for Box<T> {}

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
#![feature(panic_internals)]
137137
#![feature(pattern)]
138138
#![feature(pin_coerce_unsized_trait)]
139+
#![feature(pointer_like_trait)]
139140
#![feature(ptr_internals)]
140141
#![feature(ptr_metadata)]
141142
#![feature(ptr_sub_ptr)]

library/core/src/marker.rs

+12
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,18 @@ pub trait Tuple {}
981981
)]
982982
pub trait PointerLike {}
983983

984+
#[cfg(not(bootstrap))]
985+
marker_impls! {
986+
#[unstable(feature = "pointer_like_trait", issue = "none")]
987+
PointerLike for
988+
usize,
989+
{T} &T,
990+
{T} &mut T,
991+
{T} *const T,
992+
{T} *mut T,
993+
{T: PointerLike} crate::pin::Pin<T>,
994+
}
995+
984996
/// A marker for types which can be used as types of `const` generic parameters.
985997
///
986998
/// These types must have a proper equivalence relation (`Eq`) and it must be automatically

tests/crashes/113280.rs

-16
This file was deleted.

tests/crashes/127676.rs

-8
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ edition:2018
2+
3+
#![feature(dyn_star, const_async_blocks)]
4+
//~^ WARN the feature `dyn_star` is incomplete
5+
6+
static S: dyn* Send + Sync = async { 42 };
7+
//~^ needs to have the same ABI as a pointer
8+
9+
pub fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/async-block-dyn-star.rs:3:12
3+
|
4+
LL | #![feature(dyn_star, const_async_blocks)]
5+
| ^^^^^^^^
6+
|
7+
= note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
error[E0277]: `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}` needs to have the same ABI as a pointer
11+
--> $DIR/async-block-dyn-star.rs:6:30
12+
|
13+
LL | static S: dyn* Send + Sync = async { 42 };
14+
| ^^^^^^^^^^^^ `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}` needs to be a pointer-like type
15+
|
16+
= help: the trait `PointerLike` is not implemented for `{async block@$DIR/async-block-dyn-star.rs:6:30: 6:35}`
17+
18+
error: aborting due to 1 previous error; 1 warning emitted
19+
20+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)