Skip to content

Commit d868517

Browse files
committed
recursively drop references inside enum
1 parent 0286a7f commit d868517

File tree

4 files changed

+191
-7
lines changed

4 files changed

+191
-7
lines changed

src/refine/env.rs

Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,35 @@ impl From<chc::Atom<Var>> for UnboundAssumption {
695695
}
696696
}
697697

698+
impl Extend<UnboundAssumption> for UnboundAssumption {
699+
fn extend<T>(&mut self, iter: T)
700+
where
701+
T: IntoIterator<Item = UnboundAssumption>,
702+
{
703+
for assumption in iter {
704+
self.conds.extend(
705+
assumption
706+
.conds
707+
.into_iter()
708+
.map(|c| c.map_var(|v| v.shift_existential(self.existentials.len()))),
709+
);
710+
self.existentials.extend(assumption.existentials);
711+
}
712+
self.conds.retain(|c| !c.is_top());
713+
}
714+
}
715+
716+
impl FromIterator<UnboundAssumption> for UnboundAssumption {
717+
fn from_iter<T>(iter: T) -> Self
718+
where
719+
T: IntoIterator<Item = UnboundAssumption>,
720+
{
721+
let mut result = UnboundAssumption::default();
722+
result.extend(iter);
723+
result
724+
}
725+
}
726+
698727
impl<'a, 'b, D> Pretty<'a, D, termcolor::ColorSpec> for &'b UnboundAssumption
699728
where
700729
D: pretty::DocAllocator<'a, termcolor::ColorSpec>,
@@ -738,6 +767,10 @@ impl UnboundAssumption {
738767
conds,
739768
}
740769
}
770+
771+
pub fn is_top(&self) -> bool {
772+
self.conds.iter().all(chc::Atom::is_top)
773+
}
741774
}
742775

743776
#[derive(Debug, Clone)]
@@ -1290,6 +1323,7 @@ impl Env {
12901323

12911324
#[derive(Debug, Clone)]
12921325
enum Path {
1326+
PlaceTy(PlaceType),
12931327
Local(Local),
12941328
Deref(Box<Path>),
12951329
TupleProj(Box<Path>, usize),
@@ -1331,6 +1365,7 @@ impl Path {
13311365
impl Env {
13321366
fn path_type(&self, path: &Path) -> PlaceType {
13331367
match path {
1368+
Path::PlaceTy(pty) => pty.clone(),
13341369
Path::Local(local) => self.local_type(*local),
13351370
Path::Deref(path) => self.path_type(path).deref(),
13361371
Path::TupleProj(path, idx) => self.path_type(path).tuple_proj(*idx),
@@ -1345,23 +1380,101 @@ impl Env {
13451380
self.path_type(&place.into())
13461381
}
13471382

1348-
fn drop_path(&mut self, path: &Path) {
1383+
fn dropping_assumption(&mut self, path: &Path) -> UnboundAssumption {
13491384
let ty = self.path_type(path);
13501385
if ty.ty.is_mut() {
1351-
self.assume(ty.into_assumption(|term| {
1386+
ty.into_assumption(|term| {
13521387
let term = term.map_var(Into::into);
13531388
term.clone().mut_final().equal_to(term.mut_current())
1354-
}));
1389+
})
13551390
} else if ty.ty.is_own() {
1356-
self.drop_path(&path.clone().deref())
1391+
self.dropping_assumption(&path.clone().deref())
13571392
} else if let Some(tty) = ty.ty.as_tuple() {
1358-
for i in 0..tty.elems.len() {
1359-
self.drop_path(&path.clone().tuple_proj(i));
1393+
(0..tty.elems.len())
1394+
.into_iter()
1395+
.map(|i| self.dropping_assumption(&path.clone().tuple_proj(i)))
1396+
.collect()
1397+
} else if let Some(ety) = ty.ty.as_enum() {
1398+
let enum_def = self.enum_defs[&ety.symbol].clone();
1399+
let matcher_pred = chc::MatcherPred::new(ety.symbol.clone(), ety.arg_sorts());
1400+
1401+
let PlaceType {
1402+
ty: _,
1403+
mut existentials,
1404+
term,
1405+
mut conds,
1406+
} = ty;
1407+
1408+
let mut pred_args = vec![];
1409+
for field_ty in enum_def.field_tys() {
1410+
let mut field_rty = rty::RefinedType::unrefined(field_ty.clone().vacuous());
1411+
field_rty.instantiate_ty_params(ety.args.clone());
1412+
1413+
let ev = existentials.push(field_rty.ty.to_sort());
1414+
pred_args.push(chc::Term::var(ev.into()));
1415+
1416+
if let Some(p) = field_rty.ty.as_pointer() {
1417+
if matches!(&p.elem.ty, rty::Type::Enum(e) if e.symbol == ety.symbol) {
1418+
// TODO: we need recursively defined drop_pred for the recursive ADTs!
1419+
tracing::warn!("skipping recursive variant");
1420+
continue;
1421+
}
1422+
}
1423+
1424+
let field_pty = {
1425+
let rty::RefinedType {
1426+
ty: field_ty,
1427+
refinement,
1428+
} = field_rty;
1429+
let rty::Refinement {
1430+
atoms,
1431+
existentials,
1432+
} = refinement;
1433+
PlaceType {
1434+
ty: field_ty,
1435+
existentials,
1436+
term: chc::Term::var(ev.into()),
1437+
conds: atoms
1438+
.into_iter()
1439+
.map(|a| {
1440+
a.map_var(|v| match v {
1441+
rty::RefinedTypeVar::Value => PlaceTypeVar::Existential(ev),
1442+
rty::RefinedTypeVar::Free(v) => PlaceTypeVar::Var(v.into()),
1443+
// TODO: (but otherwise we can't distinguish field existentials from them...)
1444+
rty::RefinedTypeVar::Existential(_) => {
1445+
panic!("cannot handle existentials in field_rty")
1446+
}
1447+
})
1448+
})
1449+
.collect(),
1450+
}
1451+
};
1452+
1453+
let UnboundAssumption {
1454+
existentials: assumption_existentials,
1455+
conds: assumption_conds,
1456+
} = self.dropping_assumption(&Path::PlaceTy(field_pty));
1457+
// dropping assumption should not generate any existential
1458+
assert!(assumption_existentials.is_empty());
1459+
conds.extend(assumption_conds);
13601460
}
1461+
1462+
pred_args.push(term);
1463+
conds.push(chc::Atom::new(matcher_pred.into(), pred_args));
1464+
1465+
UnboundAssumption {
1466+
existentials,
1467+
conds,
1468+
}
1469+
} else {
1470+
UnboundAssumption::default()
13611471
}
13621472
}
13631473

13641474
pub fn drop_local(&mut self, local: Local) {
1365-
self.drop_path(&Path::Local(local))
1475+
let assumption = self.dropping_assumption(&Path::Local(local));
1476+
if !assumption.is_top() {
1477+
self.assume(assumption);
1478+
}
13661479
}
13671480
}

src/rty.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,13 @@ impl<T> Type<T> {
652652
}
653653
}
654654

655+
pub fn as_pointer(&self) -> Option<&PointerType<T>> {
656+
match self {
657+
Type::Pointer(ty) => Some(ty),
658+
_ => None,
659+
}
660+
}
661+
655662
pub fn as_enum(&self) -> Option<&EnumType<T>> {
656663
match self {
657664
Type::Enum(ty) => Some(ty),

tests/ui/fail/enum_ref_drop.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@error-in-other-file: Unsat
2+
//@compile-flags: -C debug-assertions=off
3+
4+
#![feature(register_tool)]
5+
#![register_tool(thrust)]
6+
7+
pub enum X<'a, 'b> {
8+
A(&'a mut i64),
9+
B(&'b mut i64),
10+
}
11+
12+
#[thrust::trusted]
13+
#[thrust::requires(true)]
14+
#[thrust::ensures(true)]
15+
fn rand() -> i64 { unimplemented!() }
16+
17+
fn x(i: &mut i64) -> X {
18+
if *i >= 0 {
19+
X::A(i)
20+
} else {
21+
X::B(i)
22+
}
23+
}
24+
25+
fn main() {
26+
let mut i = rand();
27+
match x(&mut i) {
28+
X::A(a) => *a += 1,
29+
X::B(b) => *b = -*b,
30+
}
31+
assert!(i > 1);
32+
}

tests/ui/pass/enum_ref_drop.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@check-pass
2+
//@compile-flags: -C debug-assertions=off
3+
4+
#![feature(register_tool)]
5+
#![register_tool(thrust)]
6+
7+
pub enum X<'a, 'b> {
8+
A(&'a mut i64),
9+
B(&'b mut i64),
10+
}
11+
12+
#[thrust::trusted]
13+
#[thrust::requires(true)]
14+
#[thrust::ensures(true)]
15+
fn rand() -> i64 { unimplemented!() }
16+
17+
fn x(i: &mut i64) -> X {
18+
if *i >= 0 {
19+
X::A(i)
20+
} else {
21+
X::B(i)
22+
}
23+
}
24+
25+
fn main() {
26+
let mut i = rand();
27+
match x(&mut i) {
28+
X::A(a) => *a += 1,
29+
X::B(b) => *b = -*b,
30+
}
31+
assert!(i > 0);
32+
}

0 commit comments

Comments
 (0)