Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
rahxephon89 committed Mar 13, 2024
1 parent 0c7a0ae commit 50c9b5d
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
//! have the same path appearing multiple times in the graph), if the last edge is mut, then
//! the set of temporaries associated with those paths must be a singleton. This basically
//! states that the same mutable reference in `temps` cannot be used twice.
//! e. For any path `p`, if a certain edge is mut, then all edges in its prefix must be mut as well.
use crate::{
pipeline::livevar_analysis_processor::{LiveVarAnnotation, LiveVarInfoAtCodeOffset},
Expand Down Expand Up @@ -1125,27 +1126,12 @@ impl<'env, 'state> LifetimeAnalysisStep<'env, 'state> {
),
_ => (BTreeSet::new(), "".to_string()),
};
let (action, attempt) = if edge.kind.is_mut() {
("mutably", "mutable")
} else {
("immutably", "immutable")
};
let reason = if other_edge.kind.is_mut() {
"mutable references exist"
} else {
"immutable references exist"
};
let mut info = self.borrow_info(label, |e| e != edge);
info.push((self.cur_loc(), "requirement enforced here".to_string()));
self.error_with_hints(
&edge.loc,
format!(
"cannot {action} borrow {what}since {reason}",
action = action,
what = temp_str,
reason = reason
),
format!("{} borrow attempted here", attempt),
self.edge_error(
edge,
other_edge,
temp_str,
info.into_iter()
.chain(self.usage_info(&other_edge.target, |t| !temps.contains(t))),
);
Expand Down Expand Up @@ -1223,6 +1209,36 @@ impl<'env, 'state> LifetimeAnalysisStep<'env, 'state> {
}
}

fn edge_error<'a>(
&self,
edge: &'a BorrowEdge,
other_edge: &'a BorrowEdge,
what: String,
hints: impl Iterator<Item = (Loc, String)>,
) {
let (action, attempt) = if edge.kind.is_mut() {
("mutably", "mutable")
} else {
("immutably", "immutable")
};
let reason = if other_edge.kind.is_mut() {
"mutable references exist"
} else {
"immutable references exist"
};
self.error_with_hints(
&edge.loc,
format!(
"cannot {action} borrow {what}since {reason}",
action = action,
what = what,
reason = reason
),
format!("{} borrow attempted here", attempt),
hints,
);
}

#[inline]
fn global_env(&self) -> &GlobalEnv {
self.target().global_env()
Expand Down Expand Up @@ -1405,10 +1421,40 @@ impl<'env, 'state> LifetimeAnalysisStep<'env, 'state> {
let is_mut = self.ty(dest).is_mutable_reference();
let struct_env = self.global_env().get_struct(struct_.to_qualified_id());
let field_id = struct_env.get_field_by_offset(*field_offs).get_id();
self.state.add_edge(
label,
BorrowEdge::new(BorrowEdgeKind::BorrowField(is_mut, field_id), loc, child),
let edge = BorrowEdge::new(
BorrowEdgeKind::BorrowField(is_mut, field_id),
loc.clone(),
child,
);
// Check condition (e) in the file header documentation.
let node_opt = self.state.graph.get(&label);
if node_opt.is_some() && is_mut {
let mut parent_node = None;
let mut parent_edge = None;
for parent in node_opt.unwrap().parents.iter() {
for ch in self.state.children(parent) {
if ch.target.0 == label.0 && !ch.kind.is_mut() {
parent_node = Some(parent);
parent_edge = Some(ch);
break;
}
}
if parent_node.is_some() {
break;
}
}
if parent_node.is_some() {
let name = format!("{} ", self.target().get_local_name_for_error_message(src));
self.edge_error(
&edge,
parent_edge.unwrap(),
name,
self.borrow_info(parent_node.unwrap(), |_| true).into_iter(),
);
return;
}
}
self.state.add_edge(label, edge);
}

/// Process a function call. For now we implement standard Move semantics, where every
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

Diagnostics:
error: cannot mutably borrow local `s_ref` since immutable references exist
┌─ tests/reference-safety/mut_ref_from_immut.move:11:17
10 │ public fun f1(s_ref: &S) acquires S {
│ ----- previous local borrow
11 │ let _ = &mut s_ref.g;
│ ^^^^^^^^^^^^ mutable borrow attempted here

error: cannot mutably borrow local `s` since immutable references exist
┌─ tests/reference-safety/mut_ref_from_immut.move:16:9
15 │ let s = borrow_global<S>(@0x1);
│ ---------------------- previous global borrow
16 │ &mut s.g;
│ ^^^^^^^^ mutable borrow attempted here

error: cannot mutably borrow local `s_ref` since immutable references exist
┌─ tests/reference-safety/mut_ref_from_immut.move:23:17
22 │ let s_ref = &s;
│ -- previous local borrow
23 │ let x = &mut s_ref.g;
│ ^^^^^^^^^^^^ mutable borrow attempted here

error: cannot mutably borrow local `b_ref` since immutable references exist
┌─ tests/reference-safety/mut_ref_from_immut.move:50:17
49 │ let b_ref = &a_ref.b;
│ -------- previous field borrow
50 │ let x = &mut b_ref.g;
│ ^^^^^^^^^^^^ mutable borrow attempted here
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module 0x42::test{

struct S has key, drop{
g: G,
}
struct G has store, drop{
u: u64
}

public fun f1(s_ref: &S) acquires S {
let _ = &mut s_ref.g;
}

public fun f2() acquires S {
let s = borrow_global<S>(@0x1);
&mut s.g;
}

public fun f3(): u64 acquires S {
let g = G {u:2};
let s = S {g};
let s_ref = &s;
let x = &mut s_ref.g;
x.u
}

public fun no_error(): u64 acquires S {
let g = G {u:2};
let s = S {g};
let s_ref = &mut s;
let x = &mut s_ref.g;
x.u
}


struct A has key, drop{
b: B,
}

struct B has store, drop{
g: G,
}

public fun f4(): u64 acquires S {
let g = G {u:2};
let b = B {g};
let a = A {b};
let a_ref = &mut a;
let b_ref = &a_ref.b;
let x = &mut b_ref.g;
x.u
}

public fun no_error_2(): u64 acquires S {
let g = G {u:2};
let b = B {g};
let a = A {b};
let a_ref = &mut a;
let b_ref = &mut a_ref.b;
let x = &mut b_ref.g;
x.u
}

}

0 comments on commit 50c9b5d

Please sign in to comment.