]> git.lizzy.rs Git - rust.git/commitdiff
typeck: Fix ICE with struct update syntax
authorDan Robertson <danlrobertson89@gmail.com>
Fri, 11 May 2018 03:56:08 +0000 (03:56 +0000)
committerDan Robertson <danlrobertson89@gmail.com>
Sat, 12 May 2018 02:47:58 +0000 (02:47 +0000)
If check_expr_struct_fields fails, do not continue to record update.
If we continue to record update, the struct may cause us to ICE later
on indexing a field that may or may not exist.

src/librustc_typeck/check/mod.rs
src/test/ui/issue-50618.rs [new file with mode: 0644]
src/test/ui/issue-50618.stderr [new file with mode: 0644]

index ef14fa9a12248eb5a49d189eda474601d617e778..ad89e1dca869751e1f0ae2b89b04eaadee2d4e4c 100644 (file)
@@ -3278,7 +3278,7 @@ fn check_expr_struct_fields(&self,
                                 span: Span,
                                 variant: &'tcx ty::VariantDef,
                                 ast_fields: &'gcx [hir::Field],
-                                check_completeness: bool) {
+                                check_completeness: bool) -> bool {
         let tcx = self.tcx;
 
         let adt_ty_hint =
@@ -3380,6 +3380,7 @@ fn check_expr_struct_fields(&self,
                                           truncated_fields_error))
                 .emit();
         }
+        error_happened
     }
 
     fn check_struct_fields_on_error(&self,
@@ -3478,24 +3479,29 @@ fn check_expr_struct(&self,
             }
         }
 
-        self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields,
-                                      base_expr.is_none());
+        let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span,
+                                                           variant, fields, base_expr.is_none());
         if let &Some(ref base_expr) = base_expr {
-            self.check_expr_has_type_or_error(base_expr, struct_ty);
-            match struct_ty.sty {
-                ty::TyAdt(adt, substs) if adt.is_struct() => {
-                    let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
-                        self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs))
-                    }).collect();
-
-                    self.tables
-                        .borrow_mut()
-                        .fru_field_types_mut()
-                        .insert(expr.hir_id, fru_field_types);
-                }
-                _ => {
-                    span_err!(self.tcx.sess, base_expr.span, E0436,
-                              "functional record update syntax requires a struct");
+            // If check_expr_struct_fields hit an error, do not attempt to populate
+            // the fields with the base_expr. This could cause us to hit errors later
+            // when certain fields are assumed to exist that in fact do not.
+            if !error_happened {
+                self.check_expr_has_type_or_error(base_expr, struct_ty);
+                match struct_ty.sty {
+                    ty::TyAdt(adt, substs) if adt.is_struct() => {
+                        let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
+                            self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs))
+                        }).collect();
+
+                        self.tables
+                            .borrow_mut()
+                            .fru_field_types_mut()
+                            .insert(expr.hir_id, fru_field_types);
+                    }
+                    _ => {
+                        span_err!(self.tcx.sess, base_expr.span, E0436,
+                                  "functional record update syntax requires a struct");
+                    }
                 }
             }
         }
diff --git a/src/test/ui/issue-50618.rs b/src/test/ui/issue-50618.rs
new file mode 100644 (file)
index 0000000..ed427c2
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2017 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.
+
+struct Point {
+    pub x: u64,
+    pub y: u64,
+}
+
+const TEMPLATE: Point = Point {
+    x: 0,
+    y: 0
+};
+
+fn main() {
+    let _ = || {
+        Point {
+            nonexistent: 0,
+            //~^ ERROR struct `Point` has no field named `nonexistent`
+            ..TEMPLATE
+        }
+    };
+}
diff --git a/src/test/ui/issue-50618.stderr b/src/test/ui/issue-50618.stderr
new file mode 100644 (file)
index 0000000..07cc5a1
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0560]: struct `Point` has no field named `nonexistent`
+  --> $DIR/issue-50618.rs:24:13
+   |
+LL |             nonexistent: 0,
+   |             ^^^^^^^^^^^ `Point` does not have this field
+   |
+   = note: available fields are: `x`, `y`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0560`.