From 140ab5ac87ae4ea00165e1e34547a3445f22b298 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 25 Jan 2016 03:43:40 +0000 Subject: [PATCH 1/3] Allow access to unit and tuple variants of an enum through a type alias of the enum --- src/librustc_typeck/check/_match.rs | 12 +++---- src/librustc_typeck/check/method/mod.rs | 9 ++++++ .../run-pass/enum-alias-access-variant.rs | 32 +++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/enum-alias-access-variant.rs diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index c43349f88104d..27c0ecca73818 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -640,8 +640,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; } @@ -675,10 +676,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 { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index fc2dd4475e3ff..5d55e236451f8 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -336,6 +336,15 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr_id: ast::NodeId) -> Result<(Def, LastPrivate), MethodError<'tcx>> { + // First check if method_name is a variant of self_ty + 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(); diff --git a/src/test/run-pass/enum-alias-access-variant.rs b/src/test/run-pass/enum-alias-access-variant.rs new file mode 100644 index 0000000000000..93f167dc4ddb2 --- /dev/null +++ b/src/test/run-pass/enum-alias-access-variant.rs @@ -0,0 +1,32 @@ +// 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 or the MIT license +// , 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 + +enum Foo { + Unit, + Bar(i32), +} + +type Alias = Foo; + +impl Default for Foo { + fn default() -> Self { + Self::Unit + } +} + +fn main() { + let t = Alias::Bar(0); + match t { + Alias::Unit => {} + Alias::Bar(_i) => {} + } +} From 6729479cdeb32c57bdb3d144bf646fb94e8f990c Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 25 Jan 2016 07:06:52 +0000 Subject: [PATCH 2/3] Allow access to struct variants of an enum through a type alias of the enum --- src/librustc_resolve/diagnostics.rs | 23 ------------ src/librustc_resolve/lib.rs | 37 ++++--------------- src/librustc_typeck/check/_match.rs | 7 +++- src/librustc_typeck/check/mod.rs | 10 ++--- .../run-pass/enum-alias-access-variant.rs | 6 ++- 5 files changed, 24 insertions(+), 59 deletions(-) diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 04ab3fe70e9fa..1fd8d2b4d3510 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -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: diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 444c43163e3c3..dbd397941c12a 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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 @@ -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, @@ -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()); } } @@ -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()); } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 27c0ecca73818..b4c551e95032a 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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 => { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f49b25df66e87..4a97801c41dbc 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 => { diff --git a/src/test/run-pass/enum-alias-access-variant.rs b/src/test/run-pass/enum-alias-access-variant.rs index 93f167dc4ddb2..e8fa66bd4c1ad 100644 --- a/src/test/run-pass/enum-alias-access-variant.rs +++ b/src/test/run-pass/enum-alias-access-variant.rs @@ -13,6 +13,7 @@ enum Foo { Unit, Bar(i32), + Baz { i: i32 }, } type Alias = Foo; @@ -24,9 +25,12 @@ impl Default for Foo { } fn main() { - let t = Alias::Bar(0); + let _t = Alias::Bar(0); + let t = Alias::Baz { i: 0 }; + match t { Alias::Unit => {} Alias::Bar(_i) => {} + Alias::Baz { i: _i } => {} } } From 209e03eb9c8db828673251a652299134c79ed29e Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 25 Jan 2016 07:59:25 +0000 Subject: [PATCH 3/3] Do not allow a variant to be accessed through a qualified path --- src/librustc_typeck/check/method/mod.rs | 15 +++++++++------ src/librustc_typeck/check/mod.rs | 2 +- src/test/run-pass/enum-alias-access-variant.rs | 13 ++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 5d55e236451f8..fc45bf5780ad7 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -333,14 +333,17 @@ 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>> { - // First check if method_name is a variant of self_ty - 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))) + // 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))) + } } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4a97801c41dbc..71029be29536e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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() diff --git a/src/test/run-pass/enum-alias-access-variant.rs b/src/test/run-pass/enum-alias-access-variant.rs index e8fa66bd4c1ad..bc9faf33a0176 100644 --- a/src/test/run-pass/enum-alias-access-variant.rs +++ b/src/test/run-pass/enum-alias-access-variant.rs @@ -10,12 +10,17 @@ // 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 { @@ -26,7 +31,13 @@ impl Default for Foo { fn main() { let _t = Alias::Bar(0); - let t = Alias::Baz { i: 0 }; + let _t = Alias::Baz { i: 0 }; + + let _t: () = ::Unit; + let _t: () = ::Unit; + + let _t: Foo = Foo::Unit; + let t: Foo = Alias::Unit; match t { Alias::Unit => {}