about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2015-08-15 20:39:28 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2015-08-15 20:39:28 +0300
commitba98228b4d4c2676edd074e39029952d8e841502 (patch)
tree80909a1a5c232c7be3c8c64c8f501a3cdc241ae4
parentb87c2926271dcf3d0556b16ae3ea8fec75058d4a (diff)
downloadrust-ba98228b4d4c2676edd074e39029952d8e841502.tar.gz
rust-ba98228b4d4c2676edd074e39029952d8e841502.zip
clean-up ExprStruct and PatStruct type-checking
This fixes the crazy "transparent aliases" bug, which I hope nobody
relied on.
-rw-r--r--src/librustc_typeck/check/_match.rs64
-rw-r--r--src/librustc_typeck/check/mod.rs268
-rw-r--r--src/librustc_typeck/diagnostics.rs5
-rw-r--r--src/test/compile-fail/issue-15034.rs2
-rw-r--r--src/test/compile-fail/structure-constructor-type-mismatch.rs51
5 files changed, 145 insertions, 245 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 9eb372a6c59..3757c55a162 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -19,6 +19,7 @@ use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
 use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
 use check::{check_expr_with_lvalue_pref, LvaluePreference};
 use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
+use TypeAndSubsts;
 use require_same_types;
 use util::nodemap::FnvHashMap;
 
@@ -526,62 +527,31 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
                                   etc: bool, expected: Ty<'tcx>) {
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
-    let report_nonstruct = || {
-        let name = pprust::path_to_string(path);
-        span_err!(tcx.sess, pat.span, E0163,
-                  "`{}` does not name a struct or a struct variant", name);
-        fcx.write_error(pat.id);
-
-        for field in fields {
-            check_pat(pcx, &field.node.pat, tcx.types.err);
-        }
-    };
 
     let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
-    let (adt_def, variant) = match def {
-        def::DefTy(did, _) | def::DefStruct(did) => {
-            match tcx.lookup_item_type(did).ty.sty {
-                ty::TyStruct(struct_def, _) =>
-                    (struct_def, struct_def.struct_variant()),
-                _ => {
-                    report_nonstruct();
-                    return;
-                }
-            }
-        }
-        def::DefVariant(eid, vid, true) => {
-            match tcx.lookup_item_type(vid).ty.sty {
-                ty::TyEnum(enum_def, _) if enum_def.did == eid => {
-                    (enum_def, enum_def.variant_with_id(vid))
-                }
-                _ => tcx.sess.span_bug(pat.span, "variant's type is not its enum")
+    let variant = match fcx.def_struct_variant(def) {
+        Some((_, variant)) => variant,
+        None => {
+            let name = pprust::path_to_string(path);
+            span_err!(tcx.sess, pat.span, E0163,
+                      "`{}` does not name a struct or a struct variant", name);
+            fcx.write_error(pat.id);
+
+            for field in fields {
+                check_pat(pcx, &field.node.pat, tcx.types.err);
             }
-        }
-        _ => {
-            report_nonstruct();
             return;
         }
     };
 
-    instantiate_path(pcx.fcx,
-                     &path.segments,
-                     adt_def.type_scheme(tcx),
-                     &adt_def.predicates(tcx),
-                     None,
-                     def,
-                     pat.span,
-                     pat.id);
-
-    let pat_ty = fcx.node_ty(pat.id);
+    let TypeAndSubsts {
+        ty: pat_ty, substs: item_substs
+    } = pcx.fcx.instantiate_type(def.def_id(), path);
     demand::eqtype(fcx, pat.span, expected, pat_ty);
-
-    let item_substs = fcx
-        .item_substs()
-        .get(&pat.id)
-        .map(|substs| substs.substs.clone())
-        .unwrap_or_else(|| Substs::empty());
-
     check_struct_pat_fields(pcx, pat.span, fields, variant, &item_substs, etc);
+
+    fcx.write_ty(pat.id, pat_ty);
+    fcx.write_substs(pat.id, ty::ItemSubsts { substs: item_substs });
 }
 
 pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 92f39d972ee..facd60de6c2 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1379,65 +1379,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                        cause)
     }
 
-    /// Returns the type of `def_id` with all generics replaced by by fresh type/region variables.
-    /// Also returns the substitution from the type parameters on `def_id` to the fresh variables.
-    /// Registers any trait obligations specified on `def_id` at the same time.
+    /// Instantiates the type in `did` with the generics in `path` and returns
+    /// it (registering the necessary trait obligations along the way).
     ///
-    /// Note that function is only intended to be used with types (notably, not fns). This is
-    /// because it doesn't do any instantiation of late-bound regions.
+    /// Note that this function is only intended to be used with type-paths,
+    /// not with value-paths.
     pub fn instantiate_type(&self,
-                            span: Span,
-                            def_id: ast::DefId)
+                            did: ast::DefId,
+                            path: &ast::Path)
                             -> TypeAndSubsts<'tcx>
     {
+        debug!("instantiate_type(did={:?}, path={:?})", did, path);
         let type_scheme =
-            self.tcx().lookup_item_type(def_id);
+            self.tcx().lookup_item_type(did);
         let type_predicates =
-            self.tcx().lookup_predicates(def_id);
-        let substs =
-            self.infcx().fresh_substs_for_generics(
-                span,
-                &type_scheme.generics);
+            self.tcx().lookup_predicates(did);
+        let substs = astconv::ast_path_substs_for_ty(self, self,
+                                                     path.span,
+                                                     PathParamMode::Optional,
+                                                     &type_scheme.generics,
+                                                     path.segments.last().unwrap());
+        debug!("instantiate_type: ty={:?} substs={:?}", &type_scheme.ty, &substs);
         let bounds =
-            self.instantiate_bounds(span, &substs, &type_predicates);
+            self.instantiate_bounds(path.span, &substs, &type_predicates);
         self.add_obligations_for_parameters(
             traits::ObligationCause::new(
-                span,
+                path.span,
                 self.body_id,
-                traits::ItemObligation(def_id)),
+                traits::ItemObligation(did)),
             &bounds);
-        let monotype =
-            self.instantiate_type_scheme(span, &substs, &type_scheme.ty);
 
         TypeAndSubsts {
-            ty: monotype,
+            ty: self.instantiate_type_scheme(path.span, &substs, &type_scheme.ty),
             substs: substs
         }
     }
 
-    /// Returns the type that this AST path refers to. If the path has no type
-    /// parameters and the corresponding type has type parameters, fresh type
-    /// and/or region variables are substituted.
-    ///
-    /// This is used when checking the constructor in struct literals.
-    fn instantiate_struct_literal_ty(&self,
-                                     struct_ty: ty::TypeScheme<'tcx>,
-                                     path: &ast::Path)
-                                     -> TypeAndSubsts<'tcx>
+    pub fn def_struct_variant(&self,
+                              def: def::Def)
+                              -> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)>
     {
-        let ty::TypeScheme { generics, ty: decl_ty } = struct_ty;
-
-        let substs = astconv::ast_path_substs_for_ty(self, self,
-                                                     path.span,
-                                                     PathParamMode::Optional,
-                                                     &generics,
-                                                     path.segments.last().unwrap());
-
-        let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty);
-
-        TypeAndSubsts { substs: substs, ty: ty }
+        match def {
+            def::DefVariant(enum_id, variant_id, true) => {
+                let adt = self.tcx().lookup_adt_def(enum_id);
+                Some((adt, adt.variant_with_id(variant_id)))
+            }
+            def::DefTy(did, _) | def::DefStruct(did) => {
+                let typ = self.tcx().lookup_item_type(did);
+                if let ty::TyStruct(adt, _) = typ.ty.sty {
+                    Some((adt, adt.struct_variant()))
+                } else {
+                    None
+                }
+            }
+            _ => None
+        }
     }
 
+
     pub fn write_nil(&self, node_id: ast::NodeId) {
         self.write_ty(node_id, self.tcx().mk_nil());
     }
@@ -3028,18 +3027,17 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
 
-    fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                adt_ty: Ty<'tcx>,
-                                                span: Span,
-                                                variant_id: ast::DefId,
-                                                ast_fields: &'tcx [ast::Field],
-                                                check_completeness: bool) -> Result<(),()> {
+    fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                          adt_ty: Ty<'tcx>,
+                                          span: Span,
+                                          variant: ty::VariantDef<'tcx>,
+                                          ast_fields: &'tcx [ast::Field],
+                                          check_completeness: bool) {
         let tcx = fcx.ccx.tcx;
-        let (adt_def, substs) = match adt_ty.sty {
-            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => (def, substs),
-            _ => tcx.sess.span_bug(span, "non-ADT passed to check_struct_or_variant_fields")
+        let substs = match adt_ty.sty {
+            ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs,
+            _ => tcx.sess.span_bug(span, "non-ADT passed to check_expr_struct_fields")
         };
-        let variant = adt_def.variant_with_id(variant_id);
 
         let mut remaining_fields = FnvHashMap();
         for field in &variant.fields {
@@ -3076,7 +3074,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             !error_happened &&
             !remaining_fields.is_empty()
         {
-            error_happened = true;
             span_err!(tcx.sess, span, E0063,
                       "missing field{}: {}",
                       if remaining_fields.len() == 1 {""} else {"s"},
@@ -3085,68 +3082,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                       .collect::<Vec<_>>()
                                       .join(", "));
         }
-
-        if error_happened { Err(()) } else { Ok(()) }
-    }
-
-    fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
-                                         id: ast::NodeId,
-                                         span: codemap::Span,
-                                         struct_def: ty::AdtDef<'tcx>,
-                                         fields: &'tcx [ast::Field],
-                                         base_expr: Option<&'tcx ast::Expr>) {
-        let tcx = fcx.ccx.tcx;
-
-        // Generate the struct type.
-        let TypeAndSubsts {
-            ty: mut struct_type,
-            substs: _
-        } = fcx.instantiate_type(span, struct_def.did);
-
-        // Look up and check the fields.
-        let res = check_struct_or_variant_fields(fcx,
-                                                 struct_type,
-                                                 span,
-                                                 struct_def.did,
-                                                 fields,
-                                                 base_expr.is_none());
-        if res.is_err() {
-            struct_type = tcx.types.err;
-        }
-
-        // Check the base expression if necessary.
-        match base_expr {
-            None => {}
-            Some(base_expr) => {
-                check_expr_has_type(fcx, &*base_expr, struct_type);
-            }
-        }
-
-        // Write in the resulting type.
-        fcx.write_ty(id, struct_type);
-    }
-
-    fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
-                                          id: ast::NodeId,
-                                          span: codemap::Span,
-                                          enum_id: ast::DefId,
-                                          variant_id: ast::DefId,
-                                          fields: &'tcx [ast::Field]) {
-        // Look up the number of type parameters and the raw type, and
-        // determine whether the enum is region-parameterized.
-        let TypeAndSubsts {
-            ty: enum_type,
-            substs: _
-        } = fcx.instantiate_type(span, enum_id);
-
-        // Look up and check the enum variant fields.
-        let _ = check_struct_or_variant_fields(fcx,
-                                               enum_type,
-                                               span,
-                                               variant_id,
-                                               fields,
-                                               true);
-        fcx.write_ty(id, enum_type);
     }
 
     fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
@@ -3165,16 +3100,42 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
     }
 
-    fn report_exprstruct_on_nondict<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
-                                              id: ast::NodeId,
-                                              fields: &'tcx [ast::Field],
-                                              base_expr: &'tcx Option<P<ast::Expr>>,
-                                              path: &ast::Path)
+    fn check_expr_struct<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
+                                   expr: &ast::Expr,
+                                   path: &ast::Path,
+                                   fields: &'tcx [ast::Field],
+                                   base_expr: &'tcx Option<P<ast::Expr>>)
     {
-        span_err!(fcx.tcx().sess, path.span, E0071,
-                  "`{}` does not name a structure",
-                  pprust::path_to_string(path));
-        check_struct_fields_on_error(fcx, id, fields, base_expr)
+        let tcx = fcx.tcx();
+
+        // Find the relevant variant
+        let def = lookup_full_def(tcx, path.span, expr.id);
+        let (adt, variant) = match fcx.def_struct_variant(def) {
+            Some((adt, variant)) => (adt, variant),
+            None => {
+                span_err!(fcx.tcx().sess, path.span, E0071,
+                          "`{}` does not name a structure",
+                          pprust::path_to_string(path));
+                check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
+                return;
+            }
+        };
+
+        let TypeAndSubsts {
+            ty: expr_ty, ..
+        } = fcx.instantiate_type(def.def_id(), path);
+        fcx.write_ty(expr.id, expr_ty);
+
+        check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields,
+                                 base_expr.is_none());
+
+        if let &Some(ref base_expr) = base_expr {
+            check_expr_has_type(fcx, base_expr, expr_ty);
+            if adt.adt_kind() == ty::AdtKind::Enum {
+                span_err!(tcx.sess, base_expr.span, E0436,
+                          "functional record update syntax requires a struct");
+            }
+        }
     }
 
     type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty);
@@ -3625,67 +3586,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
       }
       ast::ExprStruct(ref path, ref fields, ref base_expr) => {
-        // Resolve the path.
-        let def = lookup_full_def(tcx, path.span, id);
-
-        let struct_ty = match def {
-            def::DefVariant(enum_id, variant_id, true) => {
-                if let &Some(ref base_expr) = base_expr {
-                    span_err!(tcx.sess, base_expr.span, E0436,
-                              "functional record update syntax requires a struct");
-                    fcx.write_error(base_expr.id);
-                }
-                check_struct_enum_variant(fcx, id, expr.span, enum_id,
-                                          variant_id, &fields[..]);
-                Some(tcx.lookup_item_type(enum_id))
-            }
-            def::DefTy(did, _) | def::DefStruct(did) => {
-                // Verify that this was actually a struct.
-                let typ = tcx.lookup_item_type(did);
-                if let ty::TyStruct(struct_def, _) = typ.ty.sty {
-                    check_struct_constructor(fcx,
-                                             id,
-                                             expr.span,
-                                             struct_def,
-                                             &fields,
-                                             base_expr.as_ref().map(|e| &**e));
-                } else {
-                    report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
-                }
-                Some(typ)
-            }
-            _ => {
-                report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
-                None
-            }
-        };
-
-        // Turn the path into a type and verify that that type unifies with
-        // the resulting structure type. This is needed to handle type
-        // parameters correctly.
-        if let Some(struct_ty) = struct_ty {
-            let expr_ty = fcx.expr_ty(&expr);
-            let type_and_substs = fcx.instantiate_struct_literal_ty(struct_ty, path);
-            match fcx.mk_subty(false,
-                               infer::Misc(path.span),
-                               expr_ty,
-                               type_and_substs.ty) {
-                Ok(()) => {}
-                Err(type_error) => {
-                    span_err!(fcx.tcx().sess, path.span, E0235,
-                                 "structure constructor specifies a \
-                                         structure of type `{}`, but this \
-                                         structure has type `{}`: {}",
-                                         fcx.infcx()
-                                            .ty_to_string(type_and_substs.ty),
-                                         fcx.infcx()
-                                            .ty_to_string(expr_ty),
-                                         type_error);
-                    tcx.note_and_explain_type_err(&type_error, path.span);
-                }
-            }
-        }
-
+        check_expr_struct(fcx, expr, path, fields, base_expr);
         fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized);
       }
       ast::ExprField(ref base, ref field) => {
@@ -4673,6 +4574,9 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         }
     }
 
+    debug!("instantiate_path: type of {:?} is {:?}",
+           node_id,
+           ty_substituted);
     fcx.write_ty(node_id, ty_substituted);
     fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });
     return;
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index c49f05fa7a2..e10b53f7eaf 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -746,7 +746,8 @@ enum Foo { FirstValue(i32) };
 let u = Foo::FirstValue { value: 0i32 }; // error: Foo::FirstValue
                                          // isn't a structure!
 // or even simpler, if the name doesn't refer to a structure at all.
-let t = u32 { value: 4 }; // error: `u32` does not name a structure.```
+let t = u32 { value: 4 }; // error: `u32` does not name a structure.
+```
 
 To fix this, ensure that the name was correctly spelled, and that
 the correct form of initializer was used.
@@ -2681,7 +2682,7 @@ register_diagnostics! {
     E0231, // only named substitution parameters are allowed
     E0233,
     E0234,
-    E0235, // structure constructor specifies a structure of type but
+//  E0235, // structure constructor specifies a structure of type but
     E0236, // no lang item for range syntax
     E0237, // no lang item for range syntax
     E0238, // parenthesized parameters may only be used with a trait
diff --git a/src/test/compile-fail/issue-15034.rs b/src/test/compile-fail/issue-15034.rs
index 13d27e7152b..69e10b90bfe 100644
--- a/src/test/compile-fail/issue-15034.rs
+++ b/src/test/compile-fail/issue-15034.rs
@@ -25,7 +25,7 @@ struct Parser<'a> {
 impl<'a> Parser<'a> {
     pub fn new(lexer: &'a mut Lexer) -> Parser<'a> {
         Parser { lexer: lexer }
-        //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter
+        //~^ ERROR cannot infer an appropriate lifetime
     }
 }
 
diff --git a/src/test/compile-fail/structure-constructor-type-mismatch.rs b/src/test/compile-fail/structure-constructor-type-mismatch.rs
index 02c4f3d5d52..7a6b8ff6622 100644
--- a/src/test/compile-fail/structure-constructor-type-mismatch.rs
+++ b/src/test/compile-fail/structure-constructor-type-mismatch.rs
@@ -24,41 +24,66 @@ type PairF<U> = Pair<f32,U>;
 
 fn main() {
     let pt = PointF {
-        //~^ ERROR structure constructor specifies a structure of type
+        x: 1,
+        //~^ ERROR mismatched types
         //~| expected f32
         //~| found integral variable
-        x: 1,
         y: 2,
+        //~^ ERROR mismatched types
+        //~| expected f32
+        //~| found integral variable
     };
 
     let pt2 = Point::<f32> {
-        //~^ ERROR structure constructor specifies a structure of type
+        x: 3,
+        //~^ ERROR mismatched types
         //~| expected f32
         //~| found integral variable
-        x: 3,
         y: 4,
+        //~^ ERROR mismatched types
+        //~| expected f32
+        //~| found integral variable
     };
 
     let pair = PairF {
-        //~^ ERROR structure constructor specifies a structure of type
+        x: 5,
+        //~^ ERROR mismatched types
         //~| expected f32
         //~| found integral variable
-        x: 5,
         y: 6,
     };
 
     let pair2 = PairF::<i32> {
-        //~^ ERROR structure constructor specifies a structure of type
+        x: 7,
+        //~^ ERROR mismatched types
         //~| expected f32
         //~| found integral variable
-        x: 7,
         y: 8,
     };
 
-    let pt3 = PointF::<i32> {
-        //~^ ERROR wrong number of type arguments
-        //~| ERROR structure constructor specifies a structure of type
-        x: 9,
-        y: 10,
+    let pt3 = PointF::<i32> { //~ ERROR wrong number of type arguments
+        x: 9,  //~ ERROR mismatched types
+        y: 10, //~ ERROR mismatched types
     };
+
+    match (Point { x: 1, y: 2 }) {
+        PointF::<u32> { .. } => {} //~ ERROR wrong number of type arguments
+        //~^ ERROR mismatched types
+    }
+
+    match (Point { x: 1, y: 2 }) {
+        PointF { .. } => {} //~ ERROR mismatched types
+    }
+
+    match (Point { x: 1.0, y: 2.0 }) {
+        PointF { .. } => {} // ok
+    }
+
+    match (Pair { x: 1, y: 2 }) {
+        PairF::<u32> { .. } => {} //~ ERROR mismatched types
+    }
+
+    match (Pair { x: 1.0, y: 2 }) {
+        PairF::<u32> { .. } => {} // ok
+    }
 }