Skip to content

Commit 88ab7d5

Browse files
Merge pull request #4028 from thomasspriggs/tas/optional_cast
Add try_dynamic_cast from rvalue to `optionalt`
2 parents d66ccf8 + 52e95a6 commit 88ab7d5

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

src/util/expr_cast.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,32 @@ auto expr_try_dynamic_cast(TExpr &base)
107107
return ret;
108108
}
109109

110+
/// \brief Try to cast a generic exprt to a specific derived class.
111+
/// \tparam T: The type to cast the \p base param to.
112+
/// \tparam TType: The original type to cast from, must be a exprt rvalue.
113+
/// \param base: A generic \ref exprt rvalue.
114+
/// \return Cast value in an optionalt<T> or empty if \a base is not an instance
115+
/// of T.
116+
template <typename T, typename TExpr>
117+
optionalt<T> expr_try_dynamic_cast(TExpr &&base)
118+
{
119+
static_assert(
120+
std::is_rvalue_reference<decltype(base)>::value,
121+
"This template overload must only match where base is an rvalue.");
122+
static_assert(
123+
std::is_base_of<exprt, typename std::decay<TExpr>::type>::value,
124+
"Tried to expr_try_dynamic_cast from something that wasn't an exprt.");
125+
static_assert(
126+
std::is_base_of<exprt, T>::value,
127+
"The template argument T must be derived from exprt.");
128+
static_assert(!std::is_const<TExpr>::value, "Attempted to move from const.");
129+
if(!can_cast_expr<T>(base))
130+
return {};
131+
optionalt<T> ret{static_cast<T &&>(base)};
132+
validate_expr(*ret);
133+
return ret;
134+
}
135+
110136
/// \brief Try to cast a reference to a generic typet to a specific derived
111137
/// class
112138
/// \tparam T: The reference or const reference type to \a TUnderlying to cast
@@ -134,6 +160,32 @@ auto type_try_dynamic_cast(TType &base) ->
134160
return ret;
135161
}
136162

163+
/// \brief Try to cast a generic typet to a specific derived class.
164+
/// \tparam T: The type to cast the \p base param to.
165+
/// \tparam TType: The original type to cast from, must be a typet rvalue.
166+
/// \param base: A generic \ref typet rvalue.
167+
/// \return Cast value in an optionalt<T> or empty if \a base is not an instance
168+
/// of T.
169+
template <typename T, typename TType>
170+
optionalt<T> type_try_dynamic_cast(TType &&base)
171+
{
172+
static_assert(
173+
std::is_rvalue_reference<decltype(base)>::value,
174+
"This template overload must only match where base is an rvalue.");
175+
static_assert(
176+
std::is_base_of<typet, typename std::decay<TType>::type>::value,
177+
"Tried to type_try_dynamic_cast from something that wasn't an typet.");
178+
static_assert(
179+
std::is_base_of<typet, T>::value,
180+
"The template argument T must be derived from typet.");
181+
static_assert(!std::is_const<TType>::value, "Attempted to move from const.");
182+
if(!can_cast_type<T>(base))
183+
return {};
184+
optionalt<T> ret{static_cast<T &&>(base)};
185+
validate_type(*ret);
186+
return ret;
187+
}
188+
137189
namespace detail // NOLINT
138190
{
139191

unit/util/expr_cast/expr_cast.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ SCENARIO("expr_dynamic_cast",
2525
{
2626
const exprt &expr=symbol_expr;
2727

28+
// Check pointer overload is used, when casting from reference.
29+
static_assert(
30+
std::is_same<
31+
decltype(expr_try_dynamic_cast<symbol_exprt>(expr)),
32+
const symbol_exprt *>::value,
33+
"expr_try_dynamic_cast on a reference parameter must return a pointer.");
34+
2835
THEN("Try-casting from exprt reference to symbol_exprt pointer "
2936
"returns a value")
3037
{
@@ -97,4 +104,124 @@ SCENARIO("expr_dynamic_cast",
97104
REQUIRE_NOTHROW(expr_dynamic_cast<symbol_exprt>(expr_ref));
98105
}
99106
}
107+
GIVEN("An exprt value upcast from a symbolt")
108+
{
109+
exprt expr = symbol_exprt{};
110+
111+
THEN(
112+
"Trying casting from an exprt lvalue to a symbol_exprt should yield a "
113+
"non null pointer.")
114+
{
115+
symbol_exprt *result = expr_try_dynamic_cast<symbol_exprt>(expr);
116+
REQUIRE(result != nullptr);
117+
}
118+
119+
THEN(
120+
"Trying casting from an exprt lvalue to a transt should yield a null "
121+
"pointer.")
122+
{
123+
transt *result = expr_try_dynamic_cast<transt>(expr);
124+
REQUIRE(result == nullptr);
125+
}
126+
127+
THEN(
128+
"Trying casting from an exprt rvalue reference to a symbol_exprt should "
129+
"yield a non empty optional.")
130+
{
131+
optionalt<symbol_exprt> result =
132+
expr_try_dynamic_cast<symbol_exprt>(std::move(expr));
133+
REQUIRE(result.has_value());
134+
}
135+
136+
THEN(
137+
"Trying casting from an exprt rvalue reference to a transt should yield "
138+
"a empty optional.")
139+
{
140+
optionalt<transt> result = expr_try_dynamic_cast<transt>(std::move(expr));
141+
REQUIRE_FALSE(result.has_value());
142+
}
143+
}
144+
}
145+
146+
SCENARIO("type_dynamic_cast", "[core][utils][expr_cast][type_dynamic_cast]")
147+
{
148+
string_typet string_type;
149+
GIVEN("A typet value upcast from a string_typet")
150+
{
151+
typet type = string_type;
152+
153+
THEN(
154+
"Trying casting from a typet lvalue to a string_typet should yield a non "
155+
"null pointer.")
156+
{
157+
string_typet *result = type_try_dynamic_cast<string_typet>(type);
158+
REQUIRE(result != nullptr);
159+
}
160+
161+
THEN(
162+
"Trying casting from a typet lvalue to a struct_typet should yield a "
163+
"null pointer.")
164+
{
165+
struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
166+
REQUIRE(result == nullptr);
167+
}
168+
169+
THEN(
170+
"Trying casting from a typet rvalue reference to a symbol_exprt should "
171+
"yield a non empty optional.")
172+
{
173+
optionalt<string_typet> result =
174+
type_try_dynamic_cast<string_typet>(std::move(type));
175+
REQUIRE(result.has_value());
176+
}
177+
178+
THEN(
179+
"Trying casting from a typet rvalue reference to a struct_typet should "
180+
"yield a empty optional.")
181+
{
182+
optionalt<struct_typet> result =
183+
type_try_dynamic_cast<struct_typet>(std::move(type));
184+
REQUIRE_FALSE(result.has_value());
185+
}
186+
}
187+
GIVEN("A const typet reference upcast from a string_typet")
188+
{
189+
const typet &type = string_type;
190+
191+
THEN(
192+
"Trying casting from a const reference to a string_typet should yield a "
193+
"non null pointer to const.")
194+
{
195+
const string_typet *result = type_try_dynamic_cast<string_typet>(type);
196+
REQUIRE(result != nullptr);
197+
}
198+
199+
THEN(
200+
"Trying casting from a const reference to a struct_typet should yield a "
201+
"null pointer to const.")
202+
{
203+
const struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
204+
REQUIRE(result == nullptr);
205+
}
206+
}
207+
GIVEN("A typet reference upcast from a string_typet")
208+
{
209+
typet &type = string_type;
210+
211+
THEN(
212+
"Trying casting from a reference to a string_typet should yield a non "
213+
"null pointer.")
214+
{
215+
string_typet *result = type_try_dynamic_cast<string_typet>(type);
216+
REQUIRE(result != nullptr);
217+
}
218+
219+
THEN(
220+
"Trying casting from a reference to a struct_typet should yield a null "
221+
"pointer.")
222+
{
223+
struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
224+
REQUIRE(result == nullptr);
225+
}
226+
}
100227
}

0 commit comments

Comments
 (0)