Skip to content

Commit e9cf302

Browse files
committed
Add #[pin_project] attribute for structually pinning
1 parent 4e9f848 commit e9cf302

28 files changed

+994
-124
lines changed

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub(crate) mod must_use;
4747
pub(crate) mod no_implicit_prelude;
4848
pub(crate) mod non_exhaustive;
4949
pub(crate) mod path;
50+
pub(crate) mod pin_project;
5051
pub(crate) mod proc_macro_attrs;
5152
pub(crate) mod prototype;
5253
pub(crate) mod repr;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use rustc_hir::Target;
2+
use rustc_hir::attrs::AttributeKind;
3+
use rustc_span::{Span, Symbol, sym};
4+
5+
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
6+
use crate::context::Stage;
7+
use crate::target_checking::AllowedTargets;
8+
use crate::target_checking::Policy::Allow;
9+
10+
pub(crate) struct PinProjectParser;
11+
12+
impl<S: Stage> NoArgsAttributeParser<S> for PinProjectParser {
13+
const PATH: &[Symbol] = &[sym::pin_project];
14+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
15+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
16+
Allow(Target::Enum),
17+
Allow(Target::Struct),
18+
Allow(Target::Union),
19+
]);
20+
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinProject;
21+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use crate::attributes::must_use::MustUseParser;
4646
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
4747
use crate::attributes::non_exhaustive::NonExhaustiveParser;
4848
use crate::attributes::path::PathParser as PathAttributeParser;
49+
use crate::attributes::pin_project::PinProjectParser;
4950
use crate::attributes::proc_macro_attrs::{
5051
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
5152
};
@@ -231,6 +232,7 @@ attribute_parsers!(
231232
Single<WithoutArgs<NonExhaustiveParser>>,
232233
Single<WithoutArgs<ParenSugarParser>>,
233234
Single<WithoutArgs<PassByValueParser>>,
235+
Single<WithoutArgs<PinProjectParser>>,
234236
Single<WithoutArgs<PointeeParser>>,
235237
Single<WithoutArgs<ProcMacroAttributeParser>>,
236238
Single<WithoutArgs<ProcMacroParser>>,

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
888888
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
889889
),
890890

891+
// The `#[pin_project]` attribute is part of the `pin_ergonomics` experiment
892+
// that allows structually pinning, tracked in:
893+
//
894+
// - https://github.com/rust-lang/rust/issues/130494
895+
gated!(
896+
pin_project, Normal, template!(Word), ErrorFollowing,
897+
EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_project),
898+
),
899+
891900
// ==========================================================================
892901
// Internal attributes: Stability, deprecation, and unsafe:
893902
// ==========================================================================

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,9 @@ pub enum AttributeKind {
615615
/// Represents `#[pattern_complexity_limit]`
616616
PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit },
617617

618+
/// Represents `#[pin_project]`
619+
PinProject(Span),
620+
618621
/// Represents `#[pointee]`
619622
Pointee(Span),
620623

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl AttributeKind {
7676
PassByValue(..) => Yes,
7777
Path(..) => No,
7878
PatternComplexityLimit { .. } => No,
79+
PinProject(..) => Yes,
7980
Pointee(..) => No,
8081
ProcMacro(..) => No,
8182
ProcMacroAttribute(..) => No,

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function
227227
.suggestion = cast the value to `{$cast_ty}`
228228
.teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules
229229
230+
hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_project]`
231+
.note = type defined here
232+
.suggestion = add `#[pin_project]` here
233+
230234
hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len ->
231235
[1] auto trait {$traits}
232236
*[other] auto traits {$traits}

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,3 +1156,14 @@ pub(crate) struct ConstContinueBadLabel {
11561156
#[primary_span]
11571157
pub span: Span,
11581158
}
1159+
1160+
#[derive(Diagnostic)]
1161+
#[diag(hir_typeck_project_on_non_pin_project_type)]
1162+
pub(crate) struct ProjectOnNonPinProjectType {
1163+
#[primary_span]
1164+
pub span: Span,
1165+
#[note]
1166+
pub def_span: Option<Span>,
1167+
#[suggestion(code = "#[pin_project]\n", applicability = "machine-applicable")]
1168+
pub sugg_span: Option<Span>,
1169+
}

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
554554
{
555555
debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref");
556556

557+
// if the inner_ty is an ADT, make sure that it can be structually pinned
558+
// (i.e., it is `#[pin_project]`).
559+
if let Some(adt) = inner_ty.ty_adt_def()
560+
&& !adt.is_pin_project()
561+
&& !adt.is_pin()
562+
{
563+
let def_span: Option<Span> = self.tcx.hir_span_if_local(adt.did());
564+
let sugg_span = def_span.map(|span| span.shrink_to_lo());
565+
self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType {
566+
span: pat.span,
567+
def_span,
568+
sugg_span,
569+
});
570+
}
571+
557572
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
558573
// If the pinnedness is `Not`, it means the pattern is unpinned
559574
// and thus requires an `Unpin` bound.

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ bitflags::bitflags! {
5757
const IS_UNSAFE_PINNED = 1 << 10;
5858
/// Indicates whether the type is `Pin`.
5959
const IS_PIN = 1 << 11;
60+
/// Indicates whether the type is `#[pin_project]`.
61+
const IS_PIN_PROJECT = 1 << 12;
6062
}
6163
}
6264
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -286,6 +288,10 @@ impl AdtDefData {
286288
debug!("found non-exhaustive variant list for {:?}", did);
287289
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
288290
}
291+
if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinProject(..)) {
292+
debug!("found pin-project type {:?}", did);
293+
flags |= AdtFlags::IS_PIN_PROJECT;
294+
}
289295

290296
flags |= match kind {
291297
AdtKind::Enum => AdtFlags::IS_ENUM,
@@ -439,6 +445,13 @@ impl<'tcx> AdtDef<'tcx> {
439445
self.flags().contains(AdtFlags::IS_PIN)
440446
}
441447

448+
/// Returns `true` is this is `#[pin_project]` for the purposes
449+
/// of structual pinning.
450+
#[inline]
451+
pub fn is_pin_project(self) -> bool {
452+
self.flags().contains(AdtFlags::IS_PIN_PROJECT)
453+
}
454+
442455
/// Returns `true` if this type has a destructor.
443456
pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
444457
self.destructor(tcx).is_some()

0 commit comments

Comments
 (0)