Skip to content

Commit

Permalink
Auto merge of #37035 - petrochenkov:selfstruct, r=eddyb
Browse files Browse the repository at this point in the history
Support `Self` in struct expressions and patterns

Struct expressions and patterns generally support type aliases `Alias { field: 10 }` i.e. they already have to work with `ty::Ty` to do their job. `Self` is a type alias (when it's not a type parameter) => struct expressions and patterns should support `Self`.

Typical example:
```
impl MyStruct {
    fn new() -> Self {
        Self { a: 10, b: "Hello" }
    }
}
```

The first commit does some preparations and cleanups, see the commit message for  details.
This also fixes couple of bugs related to aliases in struct paths (fixes #36286).

EDIT:
Since struct expressions and patterns always work with `ty::Ty` now, associated paths in them are also supported. If associated type `A::B` successfully resolves to a struct (or union) type, then `A::B { /* fields */ }` is a valid expression/pattern. This will become more important when enum variants are treated as [associated items](#26264 (comment)).

r? @eddyb
  • Loading branch information
bors authored Oct 28, 2016
2 parents 3f44083 + 8a38928 commit 5530030
Show file tree
Hide file tree
Showing 31 changed files with 341 additions and 209 deletions.
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
delegate.matched_pat(pat, downcast_cmt, match_mode);
}
Some(Def::Struct(..)) | Some(Def::StructCtor(..)) | Some(Def::Union(..)) |
Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => {
Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) | Some(Def::SelfTy(..)) => {
debug!("struct cmt_pat={:?} pat={:?}", cmt_pat, pat);
delegate.matched_pat(pat, cmt_pat, match_mode);
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1698,7 +1698,7 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> {
match def {
Def::Variant(vid) | Def::VariantCtor(vid, ..) => self.variant_with_id(vid),
Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => self.struct_variant(),
Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => self.struct_variant(),
_ => bug!("unexpected def {:?} in variant_of_def", def)
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyAdt};
use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
use ty::TyClosure;
use ty::{TyClosure, TyProjection, TyAnon};
use ty::{TyBox, TyTrait, TyInt, TyUint, TyInfer};
use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::fold::{TypeFolder, TypeVisitor};
Expand Down Expand Up @@ -879,8 +879,8 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
})
}
TyTrait(ref data) => write!(f, "{}", data),
ty::TyProjection(ref data) => write!(f, "{}", data),
ty::TyAnon(def_id, substs) => {
TyProjection(ref data) => write!(f, "{}", data),
TyAnon(def_id, substs) => {
ty::tls::with(|tcx| {
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
// by looking up the projections associated with the def_id.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_const_eval/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
}

Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => {
Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => {
PatternKind::Leaf { subpatterns: subpatterns }
}

Expand Down
8 changes: 5 additions & 3 deletions src/librustc_passes/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,9 +565,11 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
}
}
hir::ExprStruct(..) => {
// unsafe_cell_type doesn't necessarily exist with no_core
if Some(v.tcx.expect_def(e.id).def_id()) == v.tcx.lang_items.unsafe_cell_type() {
v.add_qualif(ConstQualif::MUTABLE_MEM);
if let ty::TyAdt(adt, ..) = v.tcx.expr_ty(e).sty {
// unsafe_cell_type doesn't necessarily exist with no_core
if Some(adt.did) == v.tcx.lang_items.unsafe_cell_type() {
v.add_qualif(ConstQualif::MUTABLE_MEM);
}
}
}

Expand Down
26 changes: 1 addition & 25 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -860,31 +860,6 @@ match (A, B, C) {
```
"##,

E0422: r##"
You are trying to use an identifier that is either undefined or not a struct.
Erroneous code example:
``` compile_fail,E0422
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.
```compile_fail,E0422
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.
Expand Down Expand Up @@ -1503,6 +1478,7 @@ register_diagnostics! {
// E0419, merged into 531
// E0420, merged into 532
// E0421, merged into 531
// E0422, merged into 531/532
E0531, // unresolved pattern path kind `name`
E0532, // expected pattern path kind, found another pattern path kind
// E0427, merged into 530
Expand Down
49 changes: 14 additions & 35 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ enum ResolutionError<'a> {
IdentifierBoundMoreThanOnceInParameterList(&'a str),
/// error E0416: identifier is bound more than once in the same pattern
IdentifierBoundMoreThanOnceInSamePattern(&'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 @@ -336,15 +334,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
err.span_label(span, &format!("used in a pattern more than once"));
err
}
ResolutionError::DoesNotNameAStruct(name) => {
let mut err = struct_span_err!(resolver.session,
span,
E0422,
"`{}` does not name a structure",
name);
err.span_label(span, &format!("not a structure"));
err
}
ResolutionError::StructVariantUsedAsFunction(path_name) => {
let mut err = struct_span_err!(resolver.session,
span,
Expand Down Expand Up @@ -2383,6 +2372,18 @@ impl<'a> Resolver<'a> {
self.record_def(pat_id, resolution);
}

fn resolve_struct_path(&mut self, node_id: NodeId, path: &Path) {
// Resolution logic is equivalent for expressions and patterns,
// reuse `resolve_pattern_path` for both.
self.resolve_pattern_path(node_id, None, path, TypeNS, |def| {
match def {
Def::Struct(..) | Def::Union(..) | Def::Variant(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => true,
_ => false,
}
}, "struct, variant or union type");
}

fn resolve_pattern(&mut self,
pat: &Pat,
pat_src: PatternSource,
Expand Down Expand Up @@ -2460,13 +2461,7 @@ impl<'a> Resolver<'a> {
}

PatKind::Struct(ref path, ..) => {
self.resolve_pattern_path(pat.id, None, path, TypeNS, |def| {
match def {
Def::Struct(..) | Def::Union(..) | Def::Variant(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => true,
_ => false,
}
}, "variant, struct or type alias");
self.resolve_struct_path(pat.id, path);
}

_ => {}
Expand Down Expand Up @@ -3024,23 +3019,7 @@ impl<'a> Resolver<'a> {
}

ExprKind::Struct(ref path, ..) => {
// 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) {
Ok(definition) => self.record_def(expr.id, definition),
Err(true) => self.record_def(expr.id, err_path_resolution()),
Err(false) => {
debug!("(resolving expression) didn't find struct def",);

resolve_error(self,
path.span,
ResolutionError::DoesNotNameAStruct(
&path_names_to_string(path, 0))
);
self.record_def(expr.id, err_path_resolution());
}
}
self.resolve_struct_path(expr.id, path);

visit::walk_expr(self, expr);
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_save_analysis/dump_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
Def::StructCtor(..) | Def::VariantCtor(..) |
Def::Const(..) | Def::AssociatedConst(..) |
Def::Struct(..) | Def::Variant(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => {
Def::TyAlias(..) | Def::AssociatedTy(..) |
Def::SelfTy(..) => {
paths_to_process.push((id, p.clone(), Some(ref_kind)))
}
def => error!("unexpected definition kind when processing collected paths: {:?}",
Expand Down
22 changes: 18 additions & 4 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
def: Def,
opt_self_ty: Option<Ty<'tcx>>,
base_path_ref_id: ast::NodeId,
base_segments: &[hir::PathSegment])
base_segments: &[hir::PathSegment],
permit_variants: bool)
-> Ty<'tcx> {
let tcx = self.tcx();

Expand Down Expand Up @@ -1515,6 +1516,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
did,
base_segments.last().unwrap())
}
Def::Variant(did) if permit_variants => {
// Convert "variant type" as if it were a real type.
// The resulting `Ty` is type of the variant's enum for now.
tcx.prohibit_type_params(base_segments.split_last().unwrap().1);
self.ast_path_to_ty(rscope,
span,
param_mode,
tcx.parent_def_id(did).unwrap(),
base_segments.last().unwrap())
}
Def::TyParam(did) => {
tcx.prohibit_type_params(base_segments);

Expand Down Expand Up @@ -1604,7 +1615,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
opt_self_ty: Option<Ty<'tcx>>,
base_path_ref_id: ast::NodeId,
base_segments: &[hir::PathSegment],
assoc_segments: &[hir::PathSegment])
assoc_segments: &[hir::PathSegment],
permit_variants: bool)
-> (Ty<'tcx>, Def) {
// Convert the base type.
debug!("finish_resolving_def_to_ty(base_def={:?}, \
Expand All @@ -1619,7 +1631,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
base_def,
opt_self_ty,
base_path_ref_id,
base_segments);
base_segments,
permit_variants);
debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", base_ty);

// If any associated type segments remain, attempt to resolve them.
Expand Down Expand Up @@ -1775,7 +1788,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
opt_self_ty,
ast_ty.id,
&path.segments[..base_ty_end],
&path.segments[base_ty_end..]);
&path.segments[base_ty_end..],
false);

// Write back the new resolution.
if path_res.depth != 0 {
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expected: Ty<'tcx>) -> Ty<'tcx>
{
// Resolve the path and check the definition for errors.
let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(path, pat.id,
pat.span) {
let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(path, pat.id) {
variant_ty
} else {
for field in fields {
Expand Down
Loading

0 comments on commit 5530030

Please sign in to comment.