Skip to content

Commit

Permalink
Fixed issue rust-lang#56199.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexreg committed Dec 15, 2018
1 parent 0a1b226 commit 700651c
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 54 deletions.
84 changes: 52 additions & 32 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -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() {
Expand Down Expand Up @@ -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<T>.
#[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<T>`.
pub fn is_arc(&self) -> bool {
self.flags.intersects(AdtFlags::IS_ARC)
self.flags.contains(AdtFlags::IS_ARC)
}

/// Returns `true` if this is `Rc<T>`.
pub fn is_rc(&self) -> bool {
self.flags.intersects(AdtFlags::IS_RC)
self.flags.contains(AdtFlags::IS_RC)
}

/// Returns `true` if this is Box<T>.
#[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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
64 changes: 43 additions & 21 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ mod op;

use astconv::AstConv;
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
use rustc::hir::{self, GenericArg, Node, ItemKind, PatKind};
use rustc::hir::{self, AdtKind, GenericArg, ItemKind, Node, PatKind};
use rustc::hir::def::Def;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
Expand Down Expand Up @@ -3217,8 +3217,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,
Expand Down Expand Up @@ -5156,26 +5156,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(
Expand Down
23 changes: 23 additions & 0 deletions src/test/ui/issues/issue-56199.rs
Original file line number Diff line number Diff line change
@@ -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() {}
30 changes: 30 additions & 0 deletions src/test/ui/issues/issue-56199.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 700651c

Please sign in to comment.