Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow enum variants to be used through a type alias of the enum #31179

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,29 +714,6 @@ match Something::NotFoo {
```
"##,

E0422: r##"
You are trying to use an identifier that is either undefined or not a
struct. For instance:
```
fn main () {
let x = Foo { x: 1, y: 2 };
}
```

In this case, `Foo` is undefined, so it inherently isn't anything, and
definitely not a struct.

```
fn main () {
let foo = 1;
let x = foo { x: 1, y: 2 };
}
```

In this case, `foo` is defined, but is not a struct, so Rust can't use
it as one.
"##,

E0423: r##"
A `struct` variant name was used like a function name. Example of
erroneous code:
Expand Down
37 changes: 8 additions & 29 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ pub enum ResolutionError<'a> {
NotAnAssociatedConst(&'a str),
/// error E0421: unresolved associated const
UnresolvedAssociatedConst(&'a str),
/// error E0422: does not name a struct
DoesNotNameAStruct(&'a str),
/// error E0423: is a struct variant name, but this expression uses it like a function name
StructVariantUsedAsFunction(&'a str),
/// error E0424: `self` is not available in a static method
Expand Down Expand Up @@ -412,13 +410,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
"unresolved associated const `{}`",
name)
}
ResolutionError::DoesNotNameAStruct(name) => {
struct_span_err!(resolver.session,
span,
E0422,
"`{}` does not name a structure",
name)
}
ResolutionError::StructVariantUsedAsFunction(path_name) => {
struct_span_err!(resolver.session,
span,
Expand Down Expand Up @@ -2862,18 +2853,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}

PatStruct(ref path, _, _) => {
match self.resolve_path(pat_id, path, 0, TypeNS, false) {
Some(definition) => {
match self.resolve_possibly_assoc_item(pat_id, None, path, TypeNS, false) {
ResolveAttempt(Some(definition)) => {
self.record_def(pattern.id, definition);
}
result => {
debug!("(resolving pattern) didn't find struct def: {:?}", result);
resolve_error(
self,
path.span,
ResolutionError::DoesNotNameAStruct(
&*path_names_to_string(path, 0))
);
_ => {
self.resolve_path(pat_id, path, 0, TypeNS, false);
self.record_def(pattern.id, err_path_resolution());
}
}
Expand Down Expand Up @@ -3665,16 +3650,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Resolve the path to the structure it goes to. We don't
// check to ensure that the path is actually a structure; that
// is checked later during typeck.
match self.resolve_path(expr.id, path, 0, TypeNS, false) {
Some(definition) => self.record_def(expr.id, definition),
None => {
debug!("(resolving expression) didn't find struct def",);

resolve_error(self,
path.span,
ResolutionError::DoesNotNameAStruct(
&*path_names_to_string(path, 0))
);
match self.resolve_possibly_assoc_item(expr.id, None, path, TypeNS, false) {
ResolveAttempt(Some(definition)) => self.record_def(expr.id, definition),
_ => {
self.resolve_path(expr.id, path, 0, TypeNS, false);
self.record_def(expr.id, err_path_resolution());
}
}
Expand Down
19 changes: 12 additions & 7 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,12 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat,
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;

let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
let path_res = *tcx.def_map.borrow().get(&pat.id).unwrap();
let def = match resolve_ty_and_def_ufcs(fcx, path_res, None, path, pat.span, pat.id) {
Some((_, _, def)) => def,
None => Def::Err,
};

let variant = match fcx.def_struct_variant(def, path.span) {
Some((_, variant)) => variant,
None => {
Expand Down Expand Up @@ -640,8 +645,9 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
};

// Items that were partially resolved before should have been resolved to
// associated constants (i.e. not methods).
if path_res.depth != 0 && !check_assoc_item_is_const(pcx, def, pat.span) {
// variants or associated constants (i.e. not methods).
if let Def::Variant(..) = def {
} else if path_res.depth != 0 && !check_assoc_item_is_const(pcx, def, pat.span) {
fcx.write_error(pat.id);
return;
}
Expand Down Expand Up @@ -675,10 +681,9 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
};

// If we didn't have a fully resolved path to start with, we had an
// associated const, and we should quit now, since the rest of this
// function uses checks specific to structs and enums.
if path_res.depth != 0 {
// If we have an associated const, and we should quit now, since
// the rest of this function uses checks specific to structs and enums.
if let Def::AssociatedConst(..) = def {
if is_tuple_struct_pat {
report_bad_struct_kind(false);
} else {
Expand Down
14 changes: 13 additions & 1 deletion src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,21 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
self_ty: ty::Ty<'tcx>,
expr_id: ast::NodeId)
expr_id: ast::NodeId,
is_qpath: bool)
-> Result<(Def, LastPrivate), MethodError<'tcx>>
{
// If this is not a QPath, check if method_name is a variant of self_ty
if !is_qpath {
if let &ty::TyS { sty: ty::TyEnum(adt_def, _), .. } = self_ty {
for variant in adt_def.variants.iter() {
if variant.name == method_name {
return Ok((Def::Variant(adt_def.did, variant.did), LastMod(AllPublic)))
}
}
}
}

let mode = probe::Mode::Path;
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id));
let def_id = pick.item.def_id();
Expand Down
12 changes: 6 additions & 6 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3166,11 +3166,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
let tcx = fcx.tcx();

// Find the relevant variant
let def = lookup_full_def(tcx, path.span, expr.id);
if def == Def::Err {
check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
return;
}
let path_res = *tcx.def_map.borrow().get(&expr.id).unwrap();
let def = match resolve_ty_and_def_ufcs(fcx, path_res, None, path, expr.span, expr.id) {
Some((_, _, def)) => def,
None => Def::Err,
};
let (adt, variant) = match fcx.def_struct_variant(def, path.span) {
Some((adt, variant)) => (adt, variant),
None => {
Expand Down Expand Up @@ -3776,7 +3776,7 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
&ty_segments[base_ty_end..]);
let item_segment = path.segments.last().unwrap();
let item_name = item_segment.identifier.name;
match method::resolve_ufcs(fcx, span, item_name, ty, node_id) {
match method::resolve_ufcs(fcx, span, item_name, ty, node_id, opt_self_ty.is_some()) {
Ok((def, lp)) => {
// Write back the new resolution.
fcx.ccx.tcx.def_map.borrow_mut()
Expand Down
47 changes: 47 additions & 0 deletions src/test/run-pass/enum-alias-access-variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Checks that unit and tuple enum variants can be accessed through a type alias of the enum

#![feature(associated_consts)]
enum Foo {
Unit,
Bar(i32),
Baz { i: i32 },
}

impl Foo {
const Unit: () = ();
}

type Alias = Foo;

impl Default for Foo {
fn default() -> Self {
Self::Unit
}
}

fn main() {
let _t = Alias::Bar(0);
let _t = Alias::Baz { i: 0 };

let _t: () = <Foo>::Unit;
let _t: () = <Alias>::Unit;

let _t: Foo = Foo::Unit;
let t: Foo = Alias::Unit;

match t {
Alias::Unit => {}
Alias::Bar(_i) => {}
Alias::Baz { i: _i } => {}
}
}