about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonas Schievink <jonasschievink@gmail.com>2020-10-03 00:31:12 +0200
committerGitHub <noreply@github.com>2020-10-03 00:31:12 +0200
commit65228686649c68f491d3b763e9dde52c23fea9d7 (patch)
treef2868cb1c2c8fb3c56235745a1232c4b76b7501a
parentccc020ab42b4209940b821ba4fd0e1240d3f7561 (diff)
parent804d159c6276025e26ade90a6ef013bda33207c3 (diff)
Rollup merge of #77251 - dtolnay:drop, r=Aaron1011
Bypass const_item_mutation if const's type has Drop impl

Follow-up to #75573. This PR disables the const_item_mutation lint in cases that the const has a Drop impl which observes the mutation.

```rust
struct Log { msg: &'static str }
const LOG: Log = Log { msg: "" };
impl Drop for Log {
    fn drop(&mut self) { println!("{}", self.msg); }
}

LOG.msg = "wow";  // prints "wow"
```

r? @Aaron1011
-rw-r--r--compiler/rustc_mir/src/transform/check_const_item_mutation.rs31
-rw-r--r--src/test/ui/lint/lint-const-item-mutation.rs21
-rw-r--r--src/test/ui/lint/lint-const-item-mutation.stderr64
3 files changed, 102 insertions, 14 deletions
diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
index 0281c478a6c..b6d57b899dd 100644
--- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
+++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
@@ -31,6 +31,35 @@ impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
             None
         }
     }
+
+    fn is_const_item_without_destructor(&self, local: Local) -> Option<DefId> {
+        let def_id = self.is_const_item(local)?;
+        let mut any_dtor = |_tcx, _def_id| Ok(());
+
+        // We avoid linting mutation of a const item if the const's type has a
+        // Drop impl. The Drop logic observes the mutation which was performed.
+        //
+        //     pub struct Log { msg: &'static str }
+        //     pub const LOG: Log = Log { msg: "" };
+        //     impl Drop for Log {
+        //         fn drop(&mut self) { println!("{}", self.msg); }
+        //     }
+        //
+        //     LOG.msg = "wow";  // prints "wow"
+        //
+        // FIXME(https://github.com/rust-lang/rust/issues/77425):
+        // Drop this exception once there is a stable attribute to suppress the
+        // const item mutation lint for a single specific const only. Something
+        // equivalent to:
+        //
+        //     #[const_mutation_allowed]
+        //     pub const LOG: Log = Log { msg: "" };
+        match self.tcx.calculate_dtor(def_id, &mut any_dtor) {
+            Some(_) => None,
+            None => Some(def_id),
+        }
+    }
+
     fn lint_const_item_usage(
         &self,
         const_item: DefId,
@@ -59,7 +88,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
             // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
             // so emitting a lint would be redundant.
             if !lhs.projection.is_empty() {
-                if let Some(def_id) = self.is_const_item(lhs.local) {
+                if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) {
                     // Don't lint on writes through a pointer
                     // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`)
                     if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) {
diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs
index 43371560e02..c49a13f1065 100644
--- a/src/test/ui/lint/lint-const-item-mutation.rs
+++ b/src/test/ui/lint/lint-const-item-mutation.rs
@@ -9,9 +9,26 @@ impl MyStruct {
     fn use_mut(&mut self) {}
 }
 
+struct Mutable {
+    msg: &'static str,
+}
+impl Drop for Mutable {
+    fn drop(&mut self) {
+        println!("{}", self.msg);
+    }
+}
+
+struct Mutable2 { // this one has drop glue but not a Drop impl
+    msg: &'static str,
+    other: String,
+}
+
 const ARRAY: [u8; 1] = [25];
 const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
 const RAW_PTR: *mut u8 = 1 as *mut u8;
+const MUTABLE: Mutable = Mutable { msg: "" };
+const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() };
+const VEC: Vec<i32> = Vec::new();
 
 fn main() {
     ARRAY[0] = 5; //~ WARN attempting to modify
@@ -29,4 +46,8 @@ fn main() {
         *RAW_PTR = 0;
         *MY_STRUCT.raw_ptr = 0;
     }
+
+    MUTABLE.msg = "wow"; // no warning, because Drop observes the mutation
+    MUTABLE2.msg = "wow"; //~ WARN attempting to modify
+    VEC.push(0); //~ WARN taking a mutable reference to a `const` item
 }
diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr
index c5a221128ff..11b5124b2d2 100644
--- a/src/test/ui/lint/lint-const-item-mutation.stderr
+++ b/src/test/ui/lint/lint-const-item-mutation.stderr
@@ -1,5 +1,5 @@
 warning: attempting to modify a `const` item
-  --> $DIR/lint-const-item-mutation.rs:17:5
+  --> $DIR/lint-const-item-mutation.rs:34:5
    |
 LL |     ARRAY[0] = 5;
    |     ^^^^^^^^^^^^
@@ -7,39 +7,39 @@ LL |     ARRAY[0] = 5;
    = note: `#[warn(const_item_mutation)]` on by default
    = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:12:1
+  --> $DIR/lint-const-item-mutation.rs:26:1
    |
 LL | const ARRAY: [u8; 1] = [25];
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: attempting to modify a `const` item
-  --> $DIR/lint-const-item-mutation.rs:18:5
+  --> $DIR/lint-const-item-mutation.rs:35:5
    |
 LL |     MY_STRUCT.field = false;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:13:1
+  --> $DIR/lint-const-item-mutation.rs:27:1
    |
 LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: attempting to modify a `const` item
-  --> $DIR/lint-const-item-mutation.rs:19:5
+  --> $DIR/lint-const-item-mutation.rs:36:5
    |
 LL |     MY_STRUCT.inner_array[0] = 'b';
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:13:1
+  --> $DIR/lint-const-item-mutation.rs:27:1
    |
 LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: taking a mutable reference to a `const` item
-  --> $DIR/lint-const-item-mutation.rs:20:5
+  --> $DIR/lint-const-item-mutation.rs:37:5
    |
 LL |     MY_STRUCT.use_mut();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -52,13 +52,13 @@ note: mutable reference created due to call to this method
 LL |     fn use_mut(&mut self) {}
    |     ^^^^^^^^^^^^^^^^^^^^^
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:13:1
+  --> $DIR/lint-const-item-mutation.rs:27:1
    |
 LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: taking a mutable reference to a `const` item
-  --> $DIR/lint-const-item-mutation.rs:21:5
+  --> $DIR/lint-const-item-mutation.rs:38:5
    |
 LL |     &mut MY_STRUCT;
    |     ^^^^^^^^^^^^^^
@@ -66,13 +66,13 @@ LL |     &mut MY_STRUCT;
    = note: each usage of a `const` item creates a new temporary
    = note: the mutable reference will refer to this temporary, not the original `const` item
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:13:1
+  --> $DIR/lint-const-item-mutation.rs:27:1
    |
 LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: taking a mutable reference to a `const` item
-  --> $DIR/lint-const-item-mutation.rs:22:5
+  --> $DIR/lint-const-item-mutation.rs:39:5
    |
 LL |     (&mut MY_STRUCT).use_mut();
    |     ^^^^^^^^^^^^^^^^
@@ -80,10 +80,48 @@ LL |     (&mut MY_STRUCT).use_mut();
    = note: each usage of a `const` item creates a new temporary
    = note: the mutable reference will refer to this temporary, not the original `const` item
 note: `const` item defined here
-  --> $DIR/lint-const-item-mutation.rs:13:1
+  --> $DIR/lint-const-item-mutation.rs:27:1
    |
 LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: 6 warnings emitted
+warning: attempting to modify a `const` item
+  --> $DIR/lint-const-item-mutation.rs:51:5
+   |
+LL |     MUTABLE2.msg = "wow";
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
+note: `const` item defined here
+  --> $DIR/lint-const-item-mutation.rs:30:1
+   |
+LL | const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: taking a mutable reference to a `const` item
+  --> $DIR/lint-const-item-mutation.rs:52:5
+   |
+LL |     VEC.push(0);
+   |     ^^^^^^^^^^^
+   |
+   = note: each usage of a `const` item creates a new temporary
+   = note: the mutable reference will refer to this temporary, not the original `const` item
+note: mutable reference created due to call to this method
+  --> $SRC_DIR/alloc/src/vec.rs:LL:COL
+   |
+LL | /     pub fn push(&mut self, value: T) {
+LL | |         // This will panic or abort if we would allocate > isize::MAX bytes
+LL | |         // or if the length increment would overflow for zero-sized types.
+LL | |         if self.len == self.buf.capacity() {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+note: `const` item defined here
+  --> $DIR/lint-const-item-mutation.rs:31:1
+   |
+LL | const VEC: Vec<i32> = Vec::new();
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 8 warnings emitted