Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of expr_dynamic_cast #1279

Merged
merged 8 commits into from
Sep 22, 2017

Conversation

NathanJPhillips
Copy link
Contributor

@NathanJPhillips NathanJPhillips commented Aug 22, 2017

expr_dynamic_cast is proposed as a replacement for the family of to_*_expr functions such as to_symbol_expr. Its use can be adopted over time and this PR simply introduces the mechanism, implementations for each exprt derived type in std_expr.h and unit tests.
The primary advantage of expr_dynamic_cast is that it reduces from three to one the number of things you need to remember to ensure consistency, all you need now is the name of the type to which you are casting.
Previously code has looked like this:

void f(exprt *expr)
{
  if(expr.id() == ID_symbol)
  {
    symbol_exprt *symbol=to_symbol_expr(expr);
    use_symbol(symbol);
  }
}

Now code could look like this:

void f(exprt *expr)
{
  symbol_exprt *symbol=expr_dynamic_cast<symbol_exprt *>(expr);
  if(symbol!=nullptr)
  {
    use_symbol(symbol);
  }
}

Instead of having to get the right ID and to_*_expr function you simply call expr_dynamic_cast with the type you wish to convert to and template specialization checks the ID for you.
Additionally this code allows for conversion of const or non-const pointers and references.
Consistently with the STL, conversion of an invalid reference throws std::bad_cast, while conversion of an invalid pointer returns nullptr.

@kroening
Copy link
Member

We want to get rid of all pointers, and thus I am no fan of the new idiom.

@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch from 54eb948 to bd34218 Compare August 22, 2017 17:53
@NathanJPhillips
Copy link
Contributor Author

@kroening, that's a fair point. This code does also allow for the following mode of operation:

void f(exprt &expr)
{
  if(check_expr_type<symbol_exprt>(expr))
  {
    symbol_exprt &symbol=expr_dynamic_cast<symbol_exprt &>(expr);
    use_symbol(symbol);
  }
}

This has the same advantages mentioned above and might be a better way of using it.

#ifndef CPROVER_UTIL_EXPR_CAST_H
#define CPROVER_UTIL_EXPR_CAST_H

/*!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ///

@@ -0,0 +1,157 @@
#ifndef CPROVER_UTIL_EXPR_CAST_H
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move comment header with author here (look at other files)

: value.operands().size()>=number, message);
}

#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// CPROVER_UTIL_EXPR_CAST_H

@@ -3666,6 +4244,22 @@ inline ieee_float_op_exprt &to_ieee_float_op_expr(exprt &expr)
return static_cast<ieee_float_op_exprt &>(expr);
}

// template<>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this commented out?

const exprt &value,
exprt::operandst::size_type number,
const char *message,
bool allowMore = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove spaces around =

DATA_INVARIANT(
allowMore
? value.operands().size()==number
: value.operands().size()>=number, message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put message on separate line

*/
template<typename T> void validate_expr(const T &value) { }

template<typename T> struct remove_constt;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd provide only ref and const ref versions.

template<typename T> struct ptr_typet;
template<typename T> struct ptr_typet<T *> { using type = T; };
template<typename T> struct ref_typet;
template<typename T> struct ref_typet<T &> { using type = T; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These metafunctions can be found in <type_traits>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? I thought those versions only removed the const/ref if it's there, returning the basic type otherwise, which would cause the template to be resolved in unwanted situations.

* \param base Reference to a generic \ref exprt
* \return true if \a base is of type \a T
*/
template<typename T> void validate_expr(const T &value) { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think plain function overloading would be better than specialization. Also, I'd consider combining check_expr_type and validate_expr seeing as their roles are so similar. If we just used validate_expr, we could ignore template specialization completely, and simply provide overloads for all the different types we want to support.

@marek-trtik
Copy link
Contributor

marek-trtik commented Aug 23, 2017

Original Nathan's examples can be shortened into typical use of down-casting:

void f(exprt *expr)
{
  if(auto *symbol=expr_dynamic_cast<symbol_exprt *>(expr))
    use_symbol(symbol);
}

void f(exprt &expr)
{
  if(auto *symbol=expr_dynamic_cast<symbol_exprt *>(&expr))
    use_symbol(*symbol);
}

In the second case (i.e. void f(exprt &expr)) the expression expr_dynamic_cast<symbol_exprt *>(expr) should throw, if expr is not of symbol_exprt. That is why &expr is used (to prevent throwing).

@marek-trtik
Copy link
Contributor

Besides expr_dynamic_cast I'd also like to have type_dynamic_cast so that I could do down-casting in typet hierarchy.

@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch 3 times, most recently from 1068427 to 12318c3 Compare August 23, 2017 13:19
@NathanJPhillips
Copy link
Contributor Author

I think all the requested changes are now implemented. @peterschrammel @reuk

@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch from 12318c3 to 6196b39 Compare August 24, 2017 13:12
@NathanJPhillips NathanJPhillips changed the base branch from master to develop August 29, 2017 14:08
@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch from 6196b39 to 293dca4 Compare August 29, 2017 14:14
@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch from 293dca4 to 255845e Compare August 30, 2017 10:11
Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, one other point: the current to_* functions raise an invariant rather than throw std::bad_cast. We should probably keep doing that at least for now -- with the std::bad_cast solution at the moment we would catch it at top level and thus lose the backtrace.

/// \tparam T The exprt-derived class to check for
/// \param base Reference to a generic \ref exprt
/// \return true if \a base is of type \a T
template<typename T> bool check_expr_type(const exprt &base);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think of a synonym for type (class? kind? Simply check_expr_cast?) since the current name suggests we're talking about the unrelated base.type().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like kind fwiw

/// \brief Cast a pointer to a generic exprt to a specific derived class
/// \tparam T The pointer or const pointer type to \a TUnderlying to cast to
/// \tparam TUnderlying An exprt-derived class type
/// \param base Pointer to a generic \ref exprt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document TExpr

static_assert(
std::is_base_of<exprt, TUnderlying>::value,
"The template argument T must be derived from exprt.");
if(base == nullptr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert that TExpr is (const?) exprt (*?)?

const exprt &value,
exprt::operandst::size_type number,
const char *message,
bool allowMore=false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase -> under_scores

{
return base.id()==ID_and;
}
// inline void validate_expr(const and_exprt &value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete or uncomment (other commented validate operations too)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, these match the commented code above.

@NathanJPhillips
Copy link
Contributor Author

@smowton - are you saying that we currently do not support the use of exceptions in the CBMC code-base? That's amazing!

@NathanJPhillips
Copy link
Contributor Author

@smowton All your comments addressed.

@smowton
Copy link
Contributor

smowton commented Aug 31, 2017

@NathanJPhillips the docs still say \throw std::bad_cast. Otherwise LGTM.

@smowton
Copy link
Contributor

smowton commented Aug 31, 2017

(Exceptions will work, they just won't result in as thorough a trace dump as an invariant failure)

@NathanJPhillips NathanJPhillips force-pushed the feature/expr_dynamic_cast branch 2 times, most recently from c183aa8 to 2acb409 Compare August 31, 2017 17:06
@smowton
Copy link
Contributor

smowton commented Sep 13, 2017

I'd like to try to get this in, since I think it improves on the existing if(e.id()==ID_something) { const auto &derived=to_derived_type(e) } both by avoiding repeated id() checks (in the if-test and in the cast op) and by ensuring the two are consistent, and allowing more thorough validation (e.g. that the operands are correct).

@kroening @peterschrammel if we used the new nonstd::optional would that address your concerns? The cast would return optional<std::reference_wrapper<target_type>>, which supports an operator-> to get the target expr. Example usage:

if(auto symbol_expr=dynamic_cast_expr<symbol_exprt>(expr))
{
  irep_idt identifier=symbol_expr->get_identifier();
}

As you can see the syntax is pointer-like, but optional<reference_wrapper<T>> guards the pointer such that its referee can't be changed.

Added missing documentation of type parameter
Added static assert for type of TExpr
This better matches the existing behaviour
@smowton smowton force-pushed the feature/expr_dynamic_cast branch 2 times, most recently from 56ffb39 to b032901 Compare September 19, 2017 16:31
@smowton
Copy link
Contributor

smowton commented Sep 19, 2017

@peterschrammel @kroening changed from pointer to optional style, please re-review

@NathanJPhillips please check I haven't vandalised your code too badly

template<typename T, typename TConst, typename TUnderlying, typename TExpr>
optionalt<std::reference_wrapper<TConst>> expr_try_dynamic_cast(TExpr &base)
{
static_assert(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use invariant.h?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a compile-time assertion.


THEN("Casting from exprt reference to transt reference should throw")
{
// This no longer throws exceptions when our custom asserts are set to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They will be made throw exceptions soon.

{
return base.id()==ID_and;
}
// inline void validate_expr(const and_exprt &value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove rather than commenting out.

Copy link
Contributor

@smowton smowton Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended to mirror the commented-out validation for the same expressions in the classic to_*_expr functions. I presume those are intended to be brought back in eventually but sometimes in the product we still encounter and_exprt with a single or zero operands (logically sound but undesirable)

{
return base.id()==ID_or;
}
// inline void validate_expr(const or_exprt &value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove (several instances)

@smowton smowton force-pushed the feature/expr_dynamic_cast branch from b032901 to 0e4b1bd Compare September 20, 2017 13:43
@smowton smowton force-pushed the feature/expr_dynamic_cast branch from 0e4b1bd to da4df03 Compare September 20, 2017 13:44
@smowton
Copy link
Contributor

smowton commented Sep 21, 2017

@peterschrammel I think I have nothing to do here, happy that your comments are addressed?

@smowton smowton self-assigned this Sep 21, 2017
@smowton smowton merged commit 0e70863 into diffblue:develop Sep 22, 2017
@NathanJPhillips NathanJPhillips deleted the feature/expr_dynamic_cast branch October 2, 2017 10:00
smowton added a commit to smowton/cbmc that referenced this pull request May 9, 2018
d0d3620 Merge remote-tracking branch 'upstream/develop' into security-scanner-support
875d554 Merge branch 'develop' of github.com:diffblue/cbmc into develop
3a06eda don't rely on index expressions to be vector or arrays
6d6e1df Merge pull request diffblue#1407 from jasigal/fix/instantiate-not-contains#TG-592
8da3eef Merge pull request diffblue#1414 from antlechner/antonia/Character
0b56d97 Fix signatures for methods in java.lang.Character
e4e2b10 TG-592 Implemented the correct instantiation procedure for not contains constraints
ae83e4e Added install command for required projects.
0e70863 Merge pull request diffblue#1279 from NathanJPhillips/feature/expr_dynamic_cast
1e9837e Merge pull request diffblue#1405 from smowton/cleanup/remove_instanceof
7eb3c76 Merge pull request diffblue#1411 from diffblue/revert-1322-feature/java_support_generics
3b3fee3 Revert "TG-374 Feature/java support generics"
0e14431 Merge pull request diffblue#1408 from reuk/reuk/regression-filenames
2ddb0bc Simplify remove_instanceof logic
b4f57ee Merge pull request diffblue#1410 from LAJW/fix-test-gen
bcfb914 Remove invariant causing failure in unit test generation involving CharSequences
93149be Merge pull request diffblue#1322 from mgudemann/feature/java_support_generics
c731b98 Merge pull request diffblue#1404 from LAJW/string-refinement-functional
e5f5e8b Use `optionalt` for signature
f68e817 Parse LocalVariabletypetable / Signature attributes use in types
585fb66 Merge pull request diffblue#1400 from reuk/reuk/enable-compile-commands
da4df03 Switch from pointers to optionalt
413fc1b Automatically deduce test names from dir names
05d5a9b Address commenters' suggestions
c48170e Merge pull request diffblue#192 from diffblue/smowton/feature/split_frontend_final_stage
0823f05 allow goto-cc -E
00cbad7 test for va_args
96f2d9e Added a PRECONDITION assert/invariant
b8ab624 Code review fixup
775d9dd Renamed can_cast_expr
c372eb3 Used typeinfo functions instead of rolling own
95f6505 Comment on types for which expr_dynamic_cast is not implemented
d35710f Made validate_expr non-template
de8a8d0 Implementation of expr_dynamic_cast
f763691 Tidy up remove_instanceof
f4df5c6 Add tests for mixed GOTO and C input
215d5bf Split the entry-point-generation phase into two parts
ab347d5 Merge pull request diffblue#195 from diffblue/bugfix/missing-const_cast
73fba6e Fixed missing const_cast
751e8f5 Adhere to lintage
b544a4b Fix unit test build
9b8a025 Fix get lambdas
bee20f1 Remove unused add_lemma parameter
d03866d static add axioms for string assings
1531f0e static debug_model
f5c1b29 static get_array
7f11ccf static set_char_array_equality
229568a static Instantiate not contains
78303df Static concretize strings, lengths and results
918297c static concretize length
a4c8cf5 Static index set functions
e5e1ff4 static index_set functions
65ad3db Public methods made public
2eed573 Static add_axioms for strings
666c146 make static check_axioms
bcd6111 Static is_axiom_sat
01b8301 static is_valid_string_constraint
cfb47db Remove unused functions from string_refinement header
8e69d6d Make functions static
0cec13d Merge pull request diffblue#1360 from KPouliasis/konst_splice_call
9f9f30d fixup return value
eaf97f6 Simplify remove_instanceof logic
bc30987 Merge pull request diffblue#1235 from romainbrenguier/feature/string-max-input-length#948
0323ed0 Merge pull request diffblue#1349 from peterschrammel/cleanup/use-preformatted-output
621da87 Tests for new option string-max-input-length
440d19f Adding string max input length option
e957025 Print results to result stream instead of status
ad6484e Print XML and JSON objects on message stream
4969295 Tidy up remove_instanceof
6cb2f90 Merge pull request diffblue#1398 from diffblue/json_direct_return
f365afe Merge pull request diffblue#1392 from reuk/reuk/fixup-appveyor-regressions
4a1565b Enable compile command output
523f028 Merge pull request diffblue#1368 from diffblue/use_size_t
358b435 prefer a ranged for over indexed iteration
69a8eaf use std::size_t for container indices
cef7659 counters need to be size_t
2a7a0e8 the step number needs to be a size_t
9d48225 json_irept now returns ireps and jsont directly
f22a864 Merge pull request diffblue#1386 from diffblue/return_directly
2b050a0 Merge pull request diffblue#1372 from diffblue/preconditions
6a509a8 goto-instrument no longer needs partial inlining by default
e3f75d3 fixup coverage tests
1ff6bf2 missing include
ac022e2 inlined functions are no longer ignored when doing coverage
4cb72b3 check error message in test
3a4ebeb use preconditions in the library
f443b18 partial inlining is no longer needed
5624b15 instrumentation for preconditions
54e80da added __CPROVER_precondtion(c, d)
1d81035 Merge pull request diffblue#1393 from diffblue/byte_extract_is_binary
4e1fe93 Merge pull request diffblue#1396 from diffblue/String6-fixup
9497b02 Merge pull request diffblue#1395 from diffblue/msvc_unistd
17c6ca0 MSVC doesn't have strcasecmp and strncasecmp; use header for free()
237b31a Visual Studio doesn't have unistd.h; signal.h isn't needed for the MSVC build
5ef9c17 Revert 20e4def (preformatted_output flag)
3d4c794 Pass ostream instead of using cout in natural_loops
2716410 Update linter to cope with CBMC subtree
63fc53b Stop using sed to modify scripts!
58b75cf Merge pull request diffblue#1307 from zemanlx/coverity
da91319 Adapt to upstream change in write_goto_binary interface and languaget
177a13d Add Coverity scan
22fb7c1 added splice-call feature in goto instrument It prepends a nullary function call say foo in the body of another call say moo when called as --splice-call moo,foo added tests
a400c23 Merge pull request diffblue#1387 from thk123/feature/disable-mac-builds
68f2862 byte_extract expressions are binary expressions
ec6ad09 Merge pull request diffblue#1379 from diffblue/remove-symex
1c1d3c2 remove path-symex and the symex tool
f96ff48 Merge pull request diffblue#1384 from thk123/bugfix/regresssion-makefile
785bf43 Disable OSX builds
428b985 return STL containers directly for some variants of compute_called_functions
4bdd9c4 Corrected regression makefile
2b2a841 Convert display_index_set to a free function
9fff116 Remove constructor boilerplate
150bab1 Group string_refinement arguments
317c1c6 Group bv_refinement config variables
bf47f81 Use expr_cast in string_exprt casts
c5fa708 Refactor integer conversions
f99c8ff Replace exceptions with optional

git-subtree-dir: cbmc
git-subtree-split: d0d3620
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants