Skip to content

Commit 9a174b3

Browse files
committed
Check that has_logical_alias is used with the correct purities
1 parent 5f3a6a5 commit 9a174b3

5 files changed

Lines changed: 29 additions & 12 deletions

File tree

creusot-contracts-proc/src/creusot.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn ensures_inner(attr: TS1, tokens: TS1, logical_alias_path: bool) -> TS1 {
159159
let ens_name = generate_unique_ident(&item.name());
160160
let name_tag = format!("{}", quote! { #ens_name });
161161
let logical_alias = if logical_alias_path {
162-
quote!(#[creusot::decl::logical_alias_path = #name_tag])
162+
quote_spanned!(term.span() => #[creusot::decl::logical_alias_path = #name_tag])
163163
} else {
164164
quote!()
165165
};
@@ -624,7 +624,7 @@ pub fn has_logical_alias(attr: TS1, tokens: TS1) -> TS1 {
624624
quote!(#pat)
625625
}
626626
});
627-
let ensures_contract = quote!(result == #logic_path(#(#args),*));
627+
let ensures_contract = quote_spanned!(logic_path.span() => result == #logic_path(#(#args),*));
628628
ensures_inner(ensures_contract.into(), tokens, true)
629629
}
630630

creusot/src/contracts_items/attributes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{ctx::TranslationCtx, error::CannotFetchThir};
44
use rustc_ast::Param;
55
use rustc_hir::{AttrArgs, Attribute, def_id::DefId};
66
use rustc_middle::ty::TyCtxt;
7-
use rustc_span::Symbol;
7+
use rustc_span::{Span, Symbol};
88
use why3::declaration::Attribute as WAttribute;
99

1010
/// Helper macro, converts `creusot::foo::bar` into `["creusot", "foo", "bar"]`.
@@ -157,7 +157,7 @@ pub(crate) fn is_open_inv_param(tcx: TyCtxt, p: &Param) -> bool {
157157
pub(crate) fn function_has_logical_alias(
158158
ctx: &mut TranslationCtx,
159159
def_id: DefId,
160-
) -> Result<Option<DefId>, CannotFetchThir> {
160+
) -> Result<Option<(Span, DefId)>, CannotFetchThir> {
161161
let mut attrs =
162162
get_attrs(ctx.get_attrs_unchecked(def_id), &["creusot", "decl", "logical_alias_path"]);
163163
if attrs.len() >= 2 {
@@ -179,7 +179,7 @@ pub(crate) fn function_has_logical_alias(
179179
let ensures_body = ctx.term(ensures_def_id)?.unwrap();
180180
match &ensures_body.1.kind {
181181
crate::translation::pearlite::TermKind::Binary { rhs, .. } => match &rhs.kind {
182-
crate::translation::pearlite::TermKind::Call { id, .. } => Ok(Some(*id)),
182+
crate::translation::pearlite::TermKind::Call { id, .. } => Ok(Some((attr.span(), *id))),
183183
_ => unreachable!(),
184184
},
185185
_ => unreachable!(),

creusot/src/ctx.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pub struct TranslationCtx<'tcx> {
156156
laws: OnceMap<DefId, Box<Vec<DefId>>>,
157157
/// Maps the [`DefId`] of a program function `f` with a `#[has_logical_alias(f')]`
158158
/// attribute to the logical function `f'`
159-
logical_aliases: HashMap<DefId, DefId>,
159+
logical_aliases: HashMap<DefId, (Span, DefId)>,
160160
fmir_body: OnceMap<BodyId, Box<fmir::Body<'tcx>>>,
161161
terms: OnceMap<DefId, Box<Option<ScopedTerm<'tcx>>>>,
162162
trait_impl: OnceMap<DefId, Box<TraitImpl<'tcx>>>,
@@ -237,23 +237,24 @@ impl<'tcx> TranslationCtx<'tcx> {
237237
/// Get the _logical alias_ of the given program function, if any.
238238
///
239239
/// Logical aliases are defined with the `#[has_logical_alias(...)]` attribute.
240-
pub(crate) fn logical_alias(&self, def_id: DefId) -> Option<DefId> {
240+
///
241+
/// The returned span is the span of the attribute.
242+
pub(crate) fn logical_alias(&self, def_id: DefId) -> Option<(Span, DefId)> {
241243
self.logical_aliases.get(&def_id).copied()
242244
}
243245

244246
pub(crate) fn load_logical_aliases(&mut self) -> Result<(), CannotFetchThir> {
245247
// FIXME: what about functions from another crate?
246-
// FIXME: ensure here that the functions have the correct purity (program & logical)
247248
let mut err = None;
248249
for def_id in self.tcx.hir().body_owners() {
249250
match function_has_logical_alias(self, def_id.to_def_id()) {
250-
Ok(Some(aliased)) => {
251+
Ok(Some((span, aliased))) => {
251252
trace!(
252253
"`{}` is an alias for `{}`",
253254
self.def_path_str(def_id),
254255
self.def_path_str(aliased),
255256
);
256-
self.logical_aliases.insert(def_id.to_def_id(), aliased);
257+
self.logical_aliases.insert(def_id.to_def_id(), (span, aliased));
257258
}
258259
Ok(None) => {}
259260
Err(e) => CannotFetchThir::merge_opt(&mut err, e),

creusot/src/translation/pearlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ impl<'tcx> ThirTerm<'_, 'tcx> {
794794
.collect::<Result<_, _>>()?;
795795
let id = match self.ctx.logical_alias(id) {
796796
None => id,
797-
Some(alias_id) => alias_id,
797+
Some((_, alias_id)) => alias_id,
798798
};
799799
Ok(Term::call_no_normalize(self.ctx.tcx, id, subst, args).span(span))
800800
}

creusot/src/validate/purity.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ pub(crate) fn validate_purity(
103103

104104
let def_id = def_id.to_def_id();
105105
let purity = Purity::of_def_id(ctx, def_id);
106+
if let Some((span, alias_id)) = ctx.logical_alias(def_id) {
107+
if !matches!(purity, Purity::Program { .. }) {
108+
ctx.error(
109+
ctx.def_ident_span(def_id).unwrap_or_default(),
110+
"Only program functions can use `#[has_logical_alias(...)]`",
111+
)
112+
.with_span_label(span, "alias defined here")
113+
.emit();
114+
}
115+
let alias_purity = Purity::of_def_id(ctx, alias_id);
116+
if !matches!(alias_purity, Purity::Logic { .. }) {
117+
ctx.error(span, "Only logic functions can be aliased")
118+
.with_note(format!("{} is not a logic function", ctx.def_path_str(alias_id)))
119+
.emit();
120+
}
121+
}
106122
if matches!(purity, Purity::Program { .. })
107123
&& (is_no_translate(ctx.tcx, def_id) || is_trusted_item(ctx.tcx, def_id))
108124
{
@@ -198,7 +214,7 @@ impl<'a, 'tcx> thir::visit::Visitor<'a, 'tcx> for PurityVisitor<'a, 'tcx> {
198214

199215
let fn_purity = self.purity(fun, func_did, args);
200216
let fn_alias_purity = match self.ctx.logical_alias(func_did) {
201-
Some(alias_did) => self.purity(fun, alias_did, args),
217+
Some((_, alias_did)) => self.purity(fun, alias_did, args),
202218
None => fn_purity,
203219
};
204220
if !(self.context.can_call(fn_purity)

0 commit comments

Comments
 (0)