Lattice Land Core Library
Loading...
Searching...
No Matches
interpretation.hpp
Go to the documentation of this file.
1// Copyright 2023 Pierre Talbot
2
3#ifndef LALA_CORE_INTERPRETATION_HPP
4#define LALA_CORE_INTERPRETATION_HPP
5
6#include "logic/logic.hpp"
7#include <optional>
8
9/**
10 * This file provides an extended interface to interpret formulas in abstract domains and abstract universes.
11 * It also provides a unified interface for tell and ask interpretation using a template parameter `IKind`.
12*/
13
14namespace lala {
15
16/** Interpret `true` in the lattice `L`.
17 * \return `true` if `L` preserves the top element w.r.t. the concrete domain or if `true` is interpreted by under-approximation (kind == ASK).
18 */
19template <class L, IKind kind, bool diagnose = false, class F>
20CUDA bool ginterpret_true(const F& f, IDiagnostics& diagnostics) {
21 assert(f.is_true());
22 if constexpr(kind == IKind::ASK || L::preserve_top) {
23 return true;
24 }
25 else {
26 const char* name = L::name;
27 RETURN_INTERPRETATION_ERROR("Bottom is not preserved, hence we cannot over-approximate `true` in this abstract domain.");
28 }
29}
30
31/** This function provides an extended and unified interface to ask and tell interpretation of formula in abstract domains.
32 * It provides default interpretation for common formulas such as `true`, `false` and conjunction of formulas whenever `A` satisfies some lattice-theoretic conditions.
33 */
34template <IKind kind, bool diagnose = false, class A, class F, class Env, class I>
35CUDA bool ginterpret_in(const A& a, const F& f, Env& env, I& intermediate, IDiagnostics& diagnostics) {
36 const char* name = A::name;
37 if(f.is_true()) {
38 return ginterpret_true<A, kind, diagnose>(f, diagnostics);
39 }
40 else if(f.is_false()) {
41 if constexpr(kind == IKind::TELL || A::preserve_bot) {
42 return a.template interpret<kind, diagnose>(f, env, intermediate, diagnostics); // We don't know how `bot` is represented by this abstract domain, so we just forward the interpretation call.
43 }
44 else {
45 RETURN_INTERPRETATION_ERROR("Bot is not preserved, hence we cannot under-approximate `true` in this abstract domain.");
46 }
47 }
48 else if(f.is(F::Seq) && f.sig() == AND) {
49 if constexpr(kind == IKind::ASK || A::preserve_meet) {
50 for(int i = 0; i < f.seq().size(); ++i) {
51 if(!ginterpret_in<kind, diagnose>(a, f.seq(i), env, intermediate, diagnostics)) {
52 return false;
53 }
54 }
55 return true;
56 }
57 else {
58 RETURN_INTERPRETATION_ERROR("Meet is not preserved, hence we cannot over-approximate conjunctions in this abstract domain.");
59 }
60 }
61 // In the other cases, we cannot provide a default interpretation, so we forward the call to the abstract domain.
62 return a.template interpret<kind, diagnose>(f, env, intermediate, diagnostics);
63}
64
65/** This function provides an extended and unified interface to ask and tell interpretation of formula in abstract universes.
66 * It provides default interpretation for common formulas such as `true`, `false`, conjunction and disjunction of formulas whenever `U` satisfies some lattice-theoretic conditions. */
67template <IKind kind, bool diagnose = false, class F, class Env, class U>
68CUDA bool ginterpret_in(const F& f, const Env& env, U& value, IDiagnostics& diagnostics) {
69 const char* name = U::name;
70 if(f.is_true()) {
71 return ginterpret_true<U, kind, diagnose>(f, diagnostics);
72 }
73 else if(f.is_false()) {
74 if constexpr(kind == IKind::TELL || U::preserve_bot) {
75 value.meet_bot();
76 return true;
77 }
78 else {
79 RETURN_INTERPRETATION_ERROR("Bot is not preserved, hence we cannot under-approximate `true` in this abstract universe.");
80 }
81 }
82 else if(f.is(F::Seq)) {
83 if(f.sig() == AND) {
84 if constexpr(kind == IKind::ASK || U::preserve_meet) {
85 for(int i = 0; i < f.seq().size(); ++i) {
86 if(!ginterpret_in<kind, diagnose>(f.seq(i), env, value, diagnostics)) {
87 return false;
88 }
89 }
90 return true;
91 }
92 else {
93 RETURN_INTERPRETATION_ERROR("Meet is not preserved, hence we cannot over-approximate conjunctions in this abstract universe.");
94 }
95 }
96 else if(f.sig() == OR) {
97 if constexpr(kind == IKind::TELL || U::preserve_join) {
98 using U2 = typename U::local_type;
99 U2 join_value = U2::bot();
100 for(int i = 0; i < f.seq().size(); ++i) {
101 U2 x = U2::top();
102 if(!ginterpret_in<kind, diagnose>(f.seq(i), env, x, diagnostics)) {
103 return false;
104 }
105 join_value.join(x);
106 }
107 value.meet(join_value);
108 return true;
109 }
110 else {
111 RETURN_INTERPRETATION_ERROR("Join is not preserved, hence we cannot under-approximate disjunctions in this abstract universe.");
112 }
113 }
114 }
115 // In the other cases, we cannot provide a default interpretation, so we forward the call to the abstract element.
116 return U::template interpret<kind, diagnose>(f, env, value, diagnostics);
117}
118
119/** Top-level version of `ginterpret_in`, we restore `env` and `intermediate` in case of failure. */
120template <IKind kind, bool diagnose = false, class A, class F, class Env, class I>
121CUDA bool top_level_ginterpret_in(const A& a, const F& f, Env& env, I& intermediate, IDiagnostics& diagnostics) {
122 auto snap = env.snapshot();
123 I copy = intermediate;
124 if(ginterpret_in<kind, diagnose>(a, f, env, intermediate, diagnostics)) {
125 return true;
126 }
127 else {
128 env.restore(snap);
129 intermediate = std::move(copy);
130 return false;
131 }
132}
133
134template <class A, class Alloc = battery::standard_allocator, class Env>
135CUDA A make_top(Env& env, Alloc alloc = Alloc{}) {
136 if constexpr(A::is_abstract_universe) {
137 return A::top();
138 }
139 else {
140 return A::top(env, alloc);
141 }
142}
143
144template <bool diagnose = false, class TellAlloc = battery::standard_allocator, class F, class Env, class L>
145CUDA bool interpret_and_tell(const F& f, Env& env, L& value, IDiagnostics& diagnostics, TellAlloc tell_alloc = TellAlloc{}) {
146 if constexpr(L::is_abstract_universe) {
147 return ginterpret_in<IKind::TELL, diagnose>(f, env, value, diagnostics);
148 }
149 else {
150 typename L::template tell_type<TellAlloc> tell(tell_alloc);
151 if(top_level_ginterpret_in<IKind::TELL, diagnose>(value, f, env, tell, diagnostics)) {
152 value.deduce(tell);
153 return true;
154 }
155 else {
156 return false;
157 }
158 }
159}
160
161template <class A, bool diagnose = false, class F, class Env, class TellAlloc = typename A::allocator_type>
162CUDA std::optional<A> create_and_interpret_and_tell(const F& f,
163 Env& env, IDiagnostics& diagnostics,
164 typename A::allocator_type alloc = typename A::allocator_type{},
165 TellAlloc tell_alloc = TellAlloc{})
166{
167 auto snap = env.snapshot();
168 A a{make_top<A>(env, alloc)};
169 if(interpret_and_tell<diagnose>(f, env, a, diagnostics, tell_alloc)) {
170 return {std::move(a)};
171 }
172 else {
173 env.restore(snap);
174 return {};
175 }
176}
177
178
179}
180
181#endif
Definition diagnostics.hpp:19
#define RETURN_INTERPRETATION_ERROR(MSG)
Definition diagnostics.hpp:155
Definition abstract_deps.hpp:14
CUDA A make_top(Env &env, Alloc alloc=Alloc{})
Definition interpretation.hpp:135
@ OR
Unary arithmetic function symbols.
Definition ast.hpp:114
@ AND
Unary arithmetic function symbols.
Definition ast.hpp:114
CUDA bool top_level_ginterpret_in(const A &a, const F &f, Env &env, I &intermediate, IDiagnostics &diagnostics)
Definition interpretation.hpp:121
CUDA bool ginterpret_true(const F &f, IDiagnostics &diagnostics)
Definition interpretation.hpp:20
CUDA std::optional< A > create_and_interpret_and_tell(const F &f, Env &env, IDiagnostics &diagnostics, typename A::allocator_type alloc=typename A::allocator_type{}, TellAlloc tell_alloc=TellAlloc{})
Definition interpretation.hpp:162
CUDA bool interpret_and_tell(const F &f, Env &env, L &value, IDiagnostics &diagnostics, TellAlloc tell_alloc=TellAlloc{})
Definition interpretation.hpp:145
CUDA bool ginterpret_in(const A &a, const F &f, Env &env, I &intermediate, IDiagnostics &diagnostics)
Definition interpretation.hpp:35