From abade6707c3badf53e1796ad07f65e7a058c43d1 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 11 Dec 2018 00:05:35 +0000 Subject: [PATCH] Fixed issue #56199. --- src/librustc/ty/mod.rs | 84 +++++++++++++++++---------- src/librustc_typeck/check/callee.rs | 2 +- src/librustc_typeck/check/mod.rs | 69 ++++++++++++++-------- src/test/ui/issues/issue-56199.rs | 23 ++++++++ src/test/ui/issues/issue-56199.stderr | 30 ++++++++++ 5 files changed, 152 insertions(+), 56 deletions(-) create mode 100644 src/test/ui/issues/issue-56199.rs create mode 100644 src/test/ui/issues/issue-56199.stderr diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4633ab1166347..c3bb1afd98f23 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1754,17 +1754,19 @@ bitflags! { pub struct AdtFlags: u32 { const NO_ADT_FLAGS = 0; const IS_ENUM = 1 << 0; - const IS_PHANTOM_DATA = 1 << 1; - const IS_FUNDAMENTAL = 1 << 2; - const IS_UNION = 1 << 3; - const IS_BOX = 1 << 4; + const IS_UNION = 1 << 1; + const IS_STRUCT = 1 << 2; + const IS_TUPLE_STRUCT = 1 << 3; + const IS_PHANTOM_DATA = 1 << 4; + const IS_FUNDAMENTAL = 1 << 5; + const IS_BOX = 1 << 6; /// Indicates whether the type is an `Arc`. - const IS_ARC = 1 << 5; + const IS_ARC = 1 << 7; /// Indicates whether the type is an `Rc`. - const IS_RC = 1 << 6; + const IS_RC = 1 << 8; /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. /// (i.e., this flag is never set unless this ADT is an enum). - const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 7; + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 9; } } @@ -2079,31 +2081,43 @@ impl<'a, 'gcx, 'tcx> AdtDef { repr: ReprOptions) -> Self { debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); let mut flags = AdtFlags::NO_ADT_FLAGS; + + if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; + } + flags |= match kind { + AdtKind::Enum => AdtFlags::IS_ENUM, + AdtKind::Union => AdtFlags::IS_UNION, + AdtKind::Struct => AdtFlags::IS_STRUCT, + }; + + if let AdtKind::Struct = kind { + let variant_def = &variants[VariantIdx::new(0)]; + let def_key = tcx.def_key(variant_def.did); + match def_key.disambiguated_data.data { + DefPathData::StructCtor => flags |= AdtFlags::IS_TUPLE_STRUCT, + _ => (), + } + } + let attrs = tcx.get_attrs(did); if attr::contains_name(&attrs, "fundamental") { - flags = flags | AdtFlags::IS_FUNDAMENTAL; + flags |= AdtFlags::IS_FUNDAMENTAL; } if Some(did) == tcx.lang_items().phantom_data() { - flags = flags | AdtFlags::IS_PHANTOM_DATA; + flags |= AdtFlags::IS_PHANTOM_DATA; } if Some(did) == tcx.lang_items().owned_box() { - flags = flags | AdtFlags::IS_BOX; + flags |= AdtFlags::IS_BOX; } if Some(did) == tcx.lang_items().arc() { - flags = flags | AdtFlags::IS_ARC; + flags |= AdtFlags::IS_ARC; } if Some(did) == tcx.lang_items().rc() { - flags = flags | AdtFlags::IS_RC; - } - if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") { - debug!("found non-exhaustive variant list for {:?}", did); - flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; - } - match kind { - AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM, - AdtKind::Union => flags = flags | AdtFlags::IS_UNION, - AdtKind::Struct => {} + flags |= AdtFlags::IS_RC; } + AdtDef { did, variants, @@ -2114,25 +2128,31 @@ impl<'a, 'gcx, 'tcx> AdtDef { #[inline] pub fn is_struct(&self) -> bool { - !self.is_union() && !self.is_enum() + self.flags.contains(AdtFlags::IS_STRUCT) + } + + /// If this function returns `true`, it implies that `is_struct` must return `true`. + #[inline] + pub fn is_tuple_struct(&self) -> bool { + self.flags.contains(AdtFlags::IS_TUPLE_STRUCT) } #[inline] pub fn is_union(&self) -> bool { - self.flags.intersects(AdtFlags::IS_UNION) + self.flags.contains(AdtFlags::IS_UNION) } #[inline] pub fn is_enum(&self) -> bool { - self.flags.intersects(AdtFlags::IS_ENUM) + self.flags.contains(AdtFlags::IS_ENUM) } #[inline] pub fn is_variant_list_non_exhaustive(&self) -> bool { - self.flags.intersects(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) + self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) } - /// Returns the kind of the ADT - Struct or Enum. + /// Returns the kind of the ADT. #[inline] pub fn adt_kind(&self) -> AdtKind { if self.is_enum() { @@ -2161,33 +2181,33 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } - /// Returns whether this type is #[fundamental] for the purposes + /// Returns whether this type is `#[fundamental]` for the purposes /// of coherence checking. #[inline] pub fn is_fundamental(&self) -> bool { - self.flags.intersects(AdtFlags::IS_FUNDAMENTAL) + self.flags.contains(AdtFlags::IS_FUNDAMENTAL) } /// Returns `true` if this is PhantomData. #[inline] pub fn is_phantom_data(&self) -> bool { - self.flags.intersects(AdtFlags::IS_PHANTOM_DATA) + self.flags.contains(AdtFlags::IS_PHANTOM_DATA) } /// Returns `true` if this is `Arc`. pub fn is_arc(&self) -> bool { - self.flags.intersects(AdtFlags::IS_ARC) + self.flags.contains(AdtFlags::IS_ARC) } /// Returns `true` if this is `Rc`. pub fn is_rc(&self) -> bool { - self.flags.intersects(AdtFlags::IS_RC) + self.flags.contains(AdtFlags::IS_RC) } /// Returns `true` if this is Box. #[inline] pub fn is_box(&self) -> bool { - self.flags.intersects(AdtFlags::IS_BOX) + self.flags.contains(AdtFlags::IS_BOX) } /// Returns whether this type has a destructor. diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 7a71cf57a2f27..3ed1ab21e817f 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -280,7 +280,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Def::Local(id) | Def::Upvar(id, ..) => { Some(self.tcx.hir.span(id)) } - _ => self.tcx.hir.span_if_local(def.def_id()) + _ => def.opt_def_id().and_then(|did| self.tcx.hir.span_if_local(did)), }; if let Some(span) = def_span { let label = match (unit_variant, inner_callee_path) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e30a79b25de7f..ab0eb215d0c42 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -98,7 +98,8 @@ use rustc::mir::interpret::{ConstValue, GlobalId}; use rustc::ty::subst::{CanonicalUserSubsts, UnpackedKind, Subst, Substs, UserSelfTy, UserSubsts}; use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine}; -use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, RegionKind}; +use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, + RegionKind}; use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc::ty::fold::TypeFoldable; use rustc::ty::query::Providers; @@ -3221,8 +3222,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { return_expr_ty); } - // A generic function for checking the then and else in an if - // or if-else. + // A generic function for checking the 'then' and 'else' clauses in an 'if' + // or 'if-else' expression. fn check_then_else(&self, cond_expr: &'gcx hir::Expr, then_expr: &'gcx hir::Expr, @@ -3547,8 +3548,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // we don't look at stability attributes on // struct-like enums (yet...), but it's definitely not - // a bug to have construct one. - if adt_kind != ty::AdtKind::Enum { + // a bug to have constructed one. + if adt_kind != AdtKind::Enum { tcx.check_stability(v_field.did, Some(expr_id), field.span); } @@ -5148,26 +5149,48 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }).unwrap_or(false); let mut new_def = def; - let (def_id, ty) = if let Def::SelfCtor(impl_def_id) = def { - let ty = self.impl_self_ty(span, impl_def_id).ty; - - match ty.ty_adt_def() { - Some(adt_def) if adt_def.is_struct() => { - let variant = adt_def.non_enum_variant(); - new_def = Def::StructCtor(variant.did, variant.ctor_kind); - (variant.did, self.tcx.type_of(variant.did)) - } - _ => { - (impl_def_id, self.tcx.types.err) - } + let (def_id, ty) = match def { + Def::SelfCtor(impl_def_id) => { + let ty = self.impl_self_ty(span, impl_def_id).ty; + let adt_def = ty.ty_adt_def(); + + match adt_def { + Some(adt_def) if adt_def.is_tuple_struct() => { + let variant = adt_def.non_enum_variant(); + new_def = Def::StructCtor(variant.did, variant.ctor_kind); + (variant.did, self.tcx.type_of(variant.did)) + } + _ => { + let mut err = self.tcx.sess.struct_span_err(span, + "the `Self` constructor can only be used with tuple or unit structs"); + if let Some(adt_def) = adt_def { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.note("did you mean to use one of the enum's variants?"); + }, + AdtKind::Union => {}, + AdtKind::Struct => { + err.span_label( + span, + format!("did you mean `Self {{ /* fields */ }}`?"), + ); + } + } + } + err.emit(); + + (impl_def_id, self.tcx.types.err) + } + }; } - } else { - let def_id = def.def_id(); + _ => { + let def_id = def.def_id(); - // The things we are substituting into the type should not contain - // escaping late-bound regions, and nor should the base type scheme. - let ty = self.tcx.type_of(def_id); - (def_id, ty) + // The things we are substituting into the type should not contain + // escaping late-bound regions, and nor should the base type scheme. + let ty = self.tcx.type_of(def_id); + (def_id, ty) + } }; let substs = AstConv::create_substs_for_generic_args( diff --git a/src/test/ui/issues/issue-56199.rs b/src/test/ui/issues/issue-56199.rs new file mode 100644 index 0000000000000..bbd51823cf1af --- /dev/null +++ b/src/test/ui/issues/issue-56199.rs @@ -0,0 +1,23 @@ + +enum Foo {} +struct Bar {} + +impl Foo { + fn foo() { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple structs + } +} + +impl Bar { + fn bar() { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple structs + } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-56199.stderr b/src/test/ui/issues/issue-56199.stderr new file mode 100644 index 0000000000000..feb88e926b2d8 --- /dev/null +++ b/src/test/ui/issues/issue-56199.stderr @@ -0,0 +1,30 @@ +error: the `Self` constructor can only be used with tuple structs + --> $DIR/issue-56199.rs:7:17 + | +LL | let _ = Self; + | ^^^^ + | + = note: did you mean to use one of the enum's variants? + +error: the `Self` constructor can only be used with tuple structs + --> $DIR/issue-56199.rs:9:17 + | +LL | let _ = Self(); + | ^^^^ + | + = note: did you mean to use one of the enum's variants? + +error: the `Self` constructor can only be used with tuple structs + --> $DIR/issue-56199.rs:16:17 + | +LL | let _ = Self; + | ^^^^ did you mean `Self { /* fields */ }`? + +error: the `Self` constructor can only be used with tuple structs + --> $DIR/issue-56199.rs:18:17 + | +LL | let _ = Self(); + | ^^^^ did you mean `Self { /* fields */ }`? + +error: aborting due to 4 previous errors +