Skip to content

Commit 98f5b11

Browse files
committed
Migrate from #[structural_match] attribute a lang-item trait.
(Or more precisely, a pair of such traits: one for `derive(PartialEq)` and one for `derive(Eq)`.) ((The addition of the second marker trait, `StructuralEq`, is largely a hack to work-around `fn (&T)` not implementing `PartialEq` and `Eq`; see also issue rust-lang#46989; otherwise I would just check if `Eq` is implemented.)) Note: this does not use trait fulfillment error-reporting machinery; it just uses the trait system to determine if the ADT was tagged or not. (Nonetheless, I have kept an `on_unimplemented` message on the new trait for structural_match check, even though it is currently not used.) Note also: this does *not* resolve the ICE from rust-lang#65466, as noted in a comment added in this commit. Further work is necessary to resolve that and other problems with the structural match checking, especially to do so without breaking stable code (adapted from test fn-ptr-is-structurally-matchable.rs): ```rust fn r_sm_to(_: &SM) {} fn main() { const CFN6: Wrap<fn(&SM)> = Wrap(r_sm_to); let input: Wrap<fn(&SM)> = Wrap(r_sm_to); match Wrap(input) { Wrap(CFN6) => {} Wrap(_) => {} }; } ``` where we would hit a problem with the strategy of unconditionally checking for `PartialEq` because the type `for <'a> fn(&'a SM)` does not currently even *implement* `PartialEq`. ---- added review feedback: * use an or-pattern * eschew `return` when tail position will do. * don't need fresh_expansion; just add `structural_match` to appropriate `allow_internal_unstable` attributes. also fixed example in doc comment so that it actually compiles.
1 parent 620083a commit 98f5b11

File tree

16 files changed

+508
-172
lines changed

16 files changed

+508
-172
lines changed

src/libcore/cmp.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
211211
/// Derive macro generating an impl of the trait `PartialEq`.
212212
#[rustc_builtin_macro]
213213
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
214-
#[allow_internal_unstable(core_intrinsics)]
214+
#[allow_internal_unstable(core_intrinsics, structural_match)]
215215
pub macro PartialEq($item:item) { /* compiler built-in */ }
216216

217217
/// Trait for equality comparisons which are [equivalence relations](
@@ -273,7 +273,7 @@ pub trait Eq: PartialEq<Self> {
273273
/// Derive macro generating an impl of the trait `Eq`.
274274
#[rustc_builtin_macro]
275275
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
276-
#[allow_internal_unstable(core_intrinsics, derive_eq)]
276+
#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
277277
pub macro Eq($item:item) { /* compiler built-in */ }
278278

279279
// FIXME: this struct is used solely by #[derive] to

src/libcore/marker.rs

+87
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,85 @@ pub trait Unsize<T: ?Sized> {
126126
// Empty.
127127
}
128128

129+
/// Required trait for constants used in pattern matches.
130+
///
131+
/// Any type that derives `PartialEq` automatically implements this trait,
132+
/// *regardless* of whether its type-parameters implement `Eq`.
133+
///
134+
/// If a `const` item contains some type that does not implement this trait,
135+
/// then that type either (1.) does not implement `PartialEq` (which means the
136+
/// constant will not provide that comparison method, which code generation
137+
/// assumes is available), or (2.) it implements *its own* version of
138+
/// `PartialEq` (which we assume does not conform to a structural-equality
139+
/// comparison).
140+
///
141+
/// In either of the two scenarios above, we reject usage of such a constant in
142+
/// a pattern match.
143+
///
144+
/// See also the [structural match RFC][RFC1445], and [issue 63438][] which
145+
/// motivated migrating from attribute-based design to this trait.
146+
///
147+
/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md
148+
/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438
149+
#[cfg(not(bootstrap))]
150+
#[unstable(feature = "structural_match", issue = "31434")]
151+
#[rustc_on_unimplemented(message="the type `{Self}` does not `#[derive(PartialEq)]`")]
152+
#[lang = "structural_peq"]
153+
pub trait StructuralPartialEq {
154+
// Empty.
155+
}
156+
157+
/// Required trait for constants used in pattern matches.
158+
///
159+
/// Any type that derives `Eq` automatically implements this trait, *regardless*
160+
/// of whether its type-parameters implement `Eq`.
161+
///
162+
/// This is a hack to workaround a limitation in our type-system.
163+
///
164+
/// Background:
165+
///
166+
/// We want to require that types of consts used in pattern matches
167+
/// have the attribute `#[derive(PartialEq, Eq)]`.
168+
///
169+
/// In a more ideal world, we could check that requirement by just checking that
170+
/// the given type implements both (1.) the `StructuralPartialEq` trait *and*
171+
/// (2.) the `Eq` trait. However, you can have ADTs that *do* `derive(PartialEq, Eq)`,
172+
/// and be a case that we want the compiler to accept, and yet the constant's
173+
/// type fails to implement `Eq`.
174+
///
175+
/// Namely, a case like this:
176+
///
177+
/// ```rust
178+
/// #[derive(PartialEq, Eq)]
179+
/// struct Wrap<X>(X);
180+
/// fn higher_order(_: &()) { }
181+
/// const CFN: Wrap<fn(&())> = Wrap(higher_order);
182+
/// fn main() {
183+
/// match CFN {
184+
/// CFN => {}
185+
/// _ => {}
186+
/// }
187+
/// }
188+
/// ```
189+
///
190+
/// (The problem in the above code is that `Wrap<fn(&())>` does not implement
191+
/// `PartialEq`, nor `Eq`, because `for<'a> fn(&'a _)` does not implement those
192+
/// traits.)
193+
///
194+
/// Therefore, we cannot rely on naive check for `StructuralPartialEq` and
195+
/// mere `Eq`.
196+
///
197+
/// As a hack to work around this, we use two separate traits injected by each
198+
/// of the two derives (`#[derive(PartialEq)]` and `#[derive(Eq)]`) and check
199+
/// that both of them are present as part of structural-match checking.
200+
#[cfg(not(bootstrap))]
201+
#[unstable(feature = "structural_match", issue = "31434")]
202+
#[rustc_on_unimplemented(message="the type `{Self}` does not `#[derive(Eq)]`")]
203+
#[lang = "structural_teq"]
204+
pub trait StructuralEq {
205+
// Empty.
206+
}
207+
129208
/// Types whose values can be duplicated simply by copying bits.
130209
///
131210
/// By default, variable bindings have 'move semantics.' In other
@@ -437,6 +516,14 @@ macro_rules! impls{
437516
$t
438517
}
439518
}
519+
520+
#[cfg(not(bootstrap))]
521+
#[unstable(feature = "structural_match", issue = "31434")]
522+
impl<T: ?Sized> StructuralPartialEq for $t<T> { }
523+
524+
#[cfg(not(bootstrap))]
525+
#[unstable(feature = "structural_match", issue = "31434")]
526+
impl<T: ?Sized> StructuralEq for $t<T> { }
440527
)
441528
}
442529

src/librustc/middle/lang_items.rs

+4
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ language_item_table! {
297297

298298
SizedTraitLangItem, "sized", sized_trait, Target::Trait;
299299
UnsizeTraitLangItem, "unsize", unsize_trait, Target::Trait;
300+
// trait injected by #[derive(PartialEq)], (i.e. "Partial EQ").
301+
StructuralPeqTraitLangItem, "structural_peq", structural_peq_trait, Target::Trait;
302+
// trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
303+
StructuralTeqTraitLangItem, "structural_teq", structural_teq_trait, Target::Trait;
300304
CopyTraitLangItem, "copy", copy_trait, Target::Trait;
301305
CloneTraitLangItem, "clone", clone_trait, Target::Trait;
302306
SyncTraitLangItem, "sync", sync_trait, Target::Trait;

src/librustc/traits/error_reporting.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21512151
ObligationCauseCode::ConstSized => {
21522152
err.note("constant expressions must have a statically known size");
21532153
}
2154+
ObligationCauseCode::ConstPatternStructural => {
2155+
err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`");
2156+
}
21542157
ObligationCauseCode::SharedStatic => {
21552158
err.note("shared static variables must have a type that implements `Sync`");
21562159
}

src/librustc/traits/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ pub enum ObligationCauseCode<'tcx> {
239239
/// Computing common supertype in the pattern guard for the arms of a match expression
240240
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
241241

242+
/// Constants in patterns must have `Structural` type.
243+
ConstPatternStructural,
244+
242245
/// Computing common supertype in an if expression
243246
IfExpression(Box<IfExpressionCause>),
244247

src/librustc/traits/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
497497
super::RepeatVec => Some(super::RepeatVec),
498498
super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }),
499499
super::ConstSized => Some(super::ConstSized),
500+
super::ConstPatternStructural => Some(super::ConstPatternStructural),
500501
super::SharedStatic => Some(super::SharedStatic),
501502
super::BuiltinDerivedObligation(ref cause) => {
502503
tcx.lift(cause).map(super::BuiltinDerivedObligation)

src/librustc/ty/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ pub use self::context::{
8484

8585
pub use self::instance::{Instance, InstanceDef};
8686

87-
pub use self::structural_match::{search_for_structural_match_violation, NonStructuralMatchTy};
87+
pub use self::structural_match::search_for_structural_match_violation;
88+
pub use self::structural_match::type_marked_structural;
89+
pub use self::structural_match::NonStructuralMatchTy;
8890

8991
pub use self::trait_def::TraitDef;
9092

0 commit comments

Comments
 (0)