E8012
E8012 — A function that saysensures !frees(p), when its body can free memory throughp: the body givesp, or a value that it gets fromp, to code that can free memory, such as a call without!freesfor that parameter, a store or anmlir!block.
What the compiler reports
E8012] at {{.*}}frees_clause_that_a_body_breaks.vx:[[@LINE+4]]:11: `frees_it` says `!frees(p)`, but its body can free memory through `p`: it gives `p` to 'dealloc', which has no `!frees` for that parameter
The fragments the test suite holds the compiler to. An ellipsis marks text the test does not constrain; the emitted message also carries a source location.
A program that triggers it
//
// Part of the Vx Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Each function here says `ensures !frees(p)`, and its body does one thing that can free the
// memory of `p`. So each one gets E8012 on the line of that thing. A function that gets a value
// with a drop by value always drops it or moves it, so it cannot say `!frees` for it. An impl
// of a trait method must keep the `!frees` of the trait.
//
import core::ptr;
import std::alloc;
import std::vec;
struct Noisy {
id : i32,
}
impl Drop for Noisy {
fn drop(self : &mut Noisy) -> void {
print!("d{} ", self.id);
}
}
struct Holder {
p : *const i64,
}
fn frees_it(p : *mut i8) -> void
ensures !frees(p)
{
dealloc(p, 8i64);
}
fn frees_a_moved_pointer(p : *mut i8) -> void
ensures !frees(p)
{
let q = offset_mut(p, 0i64);
dealloc(q, 8i64);
}
fn stores_it(h : &mut Holder, p : *const i64) -> void
ensures !frees(p)
{
h.p = p;
}
fn puts_it_in_a_value(p : *const i64) -> i64
ensures !frees(p)
{
let h = Holder {
p: p
};
return 0i64;
}
unsafe fn moves_a_value_out(p : *const Noisy) -> i32
ensures !frees(p)
{
let v = unsafe { *p };
return v.id;
}
fn writes_a_value(p : *mut Noisy) -> void
ensures !frees(p)
{
unsafe { *p = Noisy { id: 2 }; }
}
fn gives_it_to_a_method(p : *const i64) -> void
ensures !frees(p)
{
let mut v : Vec<*const i64> = Vec::new();
v.push(p);
}
fn frees_a_name_that_a_match_binds(o : Option<*mut i8>) -> void
ensures !frees(o)
{
match o {
Option::Some(q) => {
dealloc(q, 8i64);
}
Option::None => {}
}
}
fn gives_it_to_a_function_value(f : fn(*const i64) -> i64, p : *const i64) -> i64
ensures !frees(p)
{
return f(p);
}
fn gives_it_to_an_mlir_block(p : *const i64) -> i64
ensures !frees(p)
{
return mlir!( inputs : (%p = p : !llvm.ptr), returns : i64, dialects : ["llvm"]) {
%v = llvm.load %p : !llvm.ptr -> i64
macro.yield %v : i64
};
}
fn gets_a_vector(v : Vec<i64>) -> i64
ensures !frees(v)
{
return 0i64;
}
trait Reads {
fn read_one(self : &Self, p : *const i64) -> i64
ensures !frees(p);
}
struct Keeps {
k : i64,
}
impl Reads for Keeps {
fn read_one(self : &Keeps, p : *const i64) -> i64 {
let h = Holder {
p: p
};
return self.k;
}
}
fn main() -> i32 {
let x = 5i64;
let k = Keeps {
k: 1i64
};
return k.read_one(&x) as i32;
}
From tests/frontend/fail/frees_clause_that_a_body_breaks.vx, which asserts this diagnostic on every commit.
Related
Contracts and verificationThe chapter covering the rule this code enforces.
All diagnosticsEvery code the compiler can emit, grouped by stage.
E8011A pointer clause that is not well formed:
requires valid(p, n), requires initialized(p, n) or requires !overlaps(p, q, n), where p and q are raw pointer parameters, and each clause is a requires of its own. A call through a function value or a trait would not prove the clause, so a safe function with one cannot be a function value, and a safe method of a trait impl cannot have one. A frees clause is an ensures of its own, ensures frees(p) or ensures !frees(p) for a parameter p, and a method of a trait impl cannot add a !frees that the trait method does not have.