Skip to content

Replace asserts and throws under goto-symex #6

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

Conversation

NlightNFotis
Copy link

No description provided.

Copy link
Author

@NlightNFotis NlightNFotis left a comment

Choose a reason for hiding this comment

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

Just left some comments about the rationale regarding some of the changes to make it easier for other people to review.

@@ -94,7 +101,7 @@ void symex_slice_by_tracet::slice_by_trace(
}
else if(!(g_copy.id()==ID_constant))
{
throw "guards should only be and, symbol, constant, or `not'";
UNHANDLED_CASE;
Copy link
Author

Choose a reason for hiding this comment

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

Some rationale for the changes: This is just for documentation purposes, the INVARIANT above should have crashed it anyway if it's something different.

@@ -313,8 +307,7 @@ void goto_symext::symex_printf(
statet &state,
const exprt &rhs)
{
if(rhs.operands().empty())
throw "printf expected to have at least one operand";
Copy link
Author

Choose a reason for hiding this comment

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

I have made the judgement call to drop what I consider to be redundant error messages, in the cases where the PRECONDITION assertion itself (within the context) seems self evident.

#endif
}
else
throw "unknown macro: "+id2string(identifier);
Copy link
Author

Choose a reason for hiding this comment

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

Here, I consider the whole

if (something)
{
   ...some computation...
} else {
   throw exception
}

to just have the general form:

assert (something)
... do the useful computation ...

so I have rearranged it appropriately.

Copy link
Owner

Choose a reason for hiding this comment

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

That's assuming that (something) is error checked elsewhere, and that its not being provided from user code....

throw "dead expects symbol as first operand";

PRECONDITION(code.operands().size()!=1);
PRECONDITION(code.op0().id()!=ID_symbol);
Copy link
Author

Choose a reason for hiding this comment

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

I don't know why there's this preceding space, and also the preconditions are wrong.

Something that is of the form

if (not something)
   throw exception

should have the form

assert (something);

assignment.rhs().type(), ns))
throw
"goto_symext::return_assignment type mismatch at "+
instruction.source_location.as_string()+":\n"+
Copy link
Author

Choose a reason for hiding this comment

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

Here I made the judgement call that the error message, especially within the context, was too chatty. I replaced it with an INVARIANT which keeps part of the message, but drops the extraneous information (at least for the usual case)

Copy link
Author

Choose a reason for hiding this comment

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

or to put in in another way, I have a severe dislike for an error message in an invariant that prints two expressions.pretty()

Copy link
Owner

Choose a reason for hiding this comment

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

a) - Are we sure this is not a user level error?
b) Imagine you made a modification to CBMC and hit this new INVARIANT - how would you go about debugging it? What types mismatch?

}

POSTCONDITION(!frame.return_value.is_not_nil());
Copy link
Author

Choose a reason for hiding this comment

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

Replaced the throw "return with unexpected value" with just a POSTCONDITION. I think it's cleaner this way, and more self evident what it does, while not altering the semantics of the code.

if(instruction.targets.size()!=1)
throw "no support for non-deterministic gotos";
DATA_INVARIANT(
instruction.targets.size()!=1, "no support for non-deterministic gotos");
Copy link
Author

Choose a reason for hiding this comment

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

Again, this hasn't been inverted properly, should be instruction.targets.size() == 1

Copy link

Choose a reason for hiding this comment

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

There are quite a few things in here that need additional discussion, and a couple things that definitely need to be changed.

@@ -222,7 +222,7 @@ void build_goto_trace(
else if(it->is_atomic_end() && current_time<0)
current_time*=-1;

assert(current_time>=0);
PRECONDITION(current_time>=0);

Choose a reason for hiding this comment

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

This isn't a precondition, this is an invariant that's supposed to be maintained by the current code block.

(Again, difference - Precondition failure is meant to indicate that the function was used incorrectly, whereas most other invariant failures are meant to indicate that the function itself is broken).

@@ -51,8 +51,7 @@ void goto_symex_statet::level0t::operator()(

const symbolt *s;
const bool found_l0 = !ns.lookup(obj_identifier, s);
DATA_INVARIANT(
found_l0, "level0: failed to find " + id2string(obj_identifier));
INVARIANT(found_l0, "level0: failed to find " + id2string(obj_identifier));

Choose a reason for hiding this comment

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

Nice catch, this indeed doesn't seem to meet the definition of DATA_INVARIANT

DATA_INVARIANT(
expr.type().id() == ID_pointer,
"type of address_of should be ID_pointer");
PRECONDITION(expr.type().id() == ID_pointer);

Choose a reason for hiding this comment

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

This should probably be the first thing that's being checked in this block?

@@ -495,12 +493,9 @@ void goto_symex_statet::rename(
}
else if(expr.id()==ID_address_of)
{
DATA_INVARIANT(
expr.operands().size() == 1, "address_of should have a single operand");
PRECONDITION(expr.operands().size() == 1);

Choose a reason for hiding this comment

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

No, this should be a DATA_INVARIANT.

@@ -76,20 +76,19 @@ bool postconditiont::is_used_address_of(
}
else if(expr.id()==ID_index)
{
assert(expr.operands().size()==2);

PRECONDITION(expr.operands().size() == 2);

Choose a reason for hiding this comment

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

DATA_INVARIANT

PRECONDITION(expr.operands().size()==2);
PRECONDITION(expr.op0().id()==ID_symbol);
PRECONDITION(expr.operands().size() == 2);
PRECONDITION(expr.op0().id() == ID_symbol);

Choose a reason for hiding this comment

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

Also DATA_INVARIANT

DATA_INVARIANT(
code.operands().size() == 2,
"array_copy/array_replace takes two operands");
PRECONDITION(code.operands().size() == 2);

Choose a reason for hiding this comment

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

DATA_INVARIANT

@@ -187,7 +185,7 @@ void goto_symext::symex_other(
// process_array_expr)
// 3. use the type of the resulting array to construct an array_of
// expression
DATA_INVARIANT(code.operands().size() == 2, "array_set takes two operands");
PRECONDITION(code.operands().size() == 2);

Choose a reason for hiding this comment

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

DATA_INVARIANT

DATA_INVARIANT(
code.operands().size() == 3,
"array_equal expected to take three arguments");
PRECONDITION(code.operands().size() == 3);

Choose a reason for hiding this comment

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

DATA_INVARIANT (probably)

@@ -266,8 +262,7 @@ void goto_symext::symex_other(
}
else if(statement==ID_havoc_object)
{
DATA_INVARIANT(code.operands().size()==1,
"havoc_object must have one operand");
PRECONDITION(code.operands().size()==1);

Choose a reason for hiding this comment

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

DATA_INVARIANT

@@ -234,8 +229,7 @@ void goto_symext::symex_gcc_builtin_va_arg_next(
const exprt &lhs,
const side_effect_exprt &code)
{
if(code.operands().size()!=1)
throw "va_arg_next expected to have one operand";
PRECONDITION(code.operands().size() != 1);

Choose a reason for hiding this comment

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

Should this be "=="?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, you are right, I don't know why this wasn't changed.

Copy link

@johnnonweiler johnnonweiler left a comment

Choose a reason for hiding this comment

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

Two questions:

  • The whitespace doesn't appear to match the coding standards in some places. Have you run clang format?
  • What tests do we have to check that there aren't any "!=" where there should be "=="?

// two-operand decl not supported here
// we handle the decl with only one operand
PRECONDITION(code.operands().size()==1);
PRECONDITION(code.op0().id()!=ID_symbol);

Choose a reason for hiding this comment

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

code.op0().id == ID_symbol ?

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, as John says - formatting for both PRECONDITIONS. Could consider these as DATA_INVARIANTS instead too?

Choose a reason for hiding this comment

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

I wasn't actually commenting on the formatting (although I do agree with Chris).
I was suggesting that I think it should be '==' instead of '!='.

Copy link
Owner

Choose a reason for hiding this comment

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

Argh - yes, good catch John.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, these were left in by mistake. This was part of a change I had done while I was quickly prototyping some changes during a discussion with Hannes, and forgot to properly go back and re-edit them.

Copy link
Author

Choose a reason for hiding this comment

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

It's the same thing as with the commented out code below.

g_copy.id() == ID_not ||
g_copy.id() == ID_and ||
g_copy.id() == ID_constant,
"guards should only be and, symbol, constant, or `not'");

Choose a reason for hiding this comment

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

Previously, this was throwing an exception for ID_and (but the comment suggested that guards can be and). Is this a deliberate change?

Copy link
Author

@NlightNFotis NlightNFotis Jul 24, 2018

Choose a reason for hiding this comment

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

@johnnonweiler No it wasn't (https://github.com/diffblue/cbmc/blob/develop/src/goto-symex/slice_by_trace.cpp#L95). What that did is that it checked if the expression's type is ID_symbol or ID_not which was handled in the first if consequent statement, then it checked if the expression's type is ID_and, which was handled in the second consequent statement (else if (g_copy.id()==ID_and)...) and last it was throwing an exception if the expression's type was anything but a constant (else if (!(g_copy.id()==ID_constant)) - based on the idea that it had checked for the other meaningful types and handled them, and left the constant case unhandled but crashed on anything else).

This INVARIANT does the same thing, just earlier, while it also drops the unneeded exception now.

Choose a reason for hiding this comment

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

Sorry @NlightNFotis - I got confused by GitHub hiding a couple of lines in the diff!

@@ -55,8 +55,9 @@ void goto_symext::symex_assign(
symex_allocate(state, lhs, side_effect_expr);
else if(statement==ID_printf)
{
if(lhs.is_not_nil())
throw "printf: unexpected assignment";
PRECONDITION(!lhs.is_not_nil());

Choose a reason for hiding this comment

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

Could this be lhs.is_nil() (instead of the double negative)?

Copy link
Owner

Choose a reason for hiding this comment

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

Agree with John here - don't like the double negative.

throw "printf: unexpected assignment";
PRECONDITION(!lhs.is_not_nil());
// if(lhs.is_not_nil())
// throw "printf: unexpected assignment";

Choose a reason for hiding this comment

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

Why include this commented out code?

Copy link
Owner

Choose a reason for hiding this comment

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

Yep, this code should be removed, not commented.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, again, as stated before, this was part of some quick prototyping during a discussion with Hannes, that somehow manged to creep in.

Copy link
Owner

@chrisr-diffblue chrisr-diffblue left a comment

Choose a reason for hiding this comment

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

There's a lot of work in here, and most of it is very good - but there's a few changes which I don't agree with, and a lot of white space indentation that needs cleaning up.

@@ -92,7 +92,7 @@ exprt build_full_lhs_rec(
id==ID_byte_extract_big_endian)
{
exprt tmp=src_original;
assert(tmp.operands().size()==2);
PRECONDITION(tmp.operands().size() == 2);
Copy link
Owner

Choose a reason for hiding this comment

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

Minor nit: Why not move the PRECONDITION to be the first line in the block?

Copy link
Owner

Choose a reason for hiding this comment

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

Should this be a DATA_INVARIANT ?

@@ -222,7 +222,7 @@ void build_goto_trace(
else if(it->is_atomic_end() && current_time<0)
current_time*=-1;

assert(current_time>=0);
INVARIANT(current_time>=0, "time keeping inconsistency");
Copy link
Owner

Choose a reason for hiding this comment

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

Again, isn't this a DATA_INVARIANT?

Copy link
Author

Choose a reason for hiding this comment

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

No. This is what the DATA_INVARIANT's documentation says:

// This condition should be used to document that assumptions that are
// made on goto_functions, goto_programs, exprts, etc. being well formed.
// "The data structure is corrupt or malformed"

To my understanding this is to assert properties of fundamental data structures representing the program under analysis. In this case current_time is a local variable that is only required to be consistent during the execution of the function.

Copy link
Owner

Choose a reason for hiding this comment

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

Seems reasonable, thanks.

@@ -187,8 +186,7 @@ bool goto_symex_statet::constant_propagation_reference(const exprt &expr) const
}
else if(expr.id()==ID_member)
{
if(expr.operands().size()!=1)
throw "member expects one operand";
PRECONDITION(expr.operands().size() == 1);
Copy link
Owner

Choose a reason for hiding this comment

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

Surely DATA_INVARIANT?

Copy link
Author

Choose a reason for hiding this comment

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

By definition this falls between multiple categories of the invariant checks (it falls both under DATA_INVARIANT because it checks fundamental data structures like an exprt in this case, but it also falls under the PRECONDITION case, in that it's asserting whether an argument of the function is correct and the rest of the computation can be sound based on that assertion.

But I can certainly change it to the DATA_INVARIANT in case it makes things a little bit cleaner.

@@ -495,12 +493,10 @@ void goto_symex_statet::rename(
}
else if(expr.id()==ID_address_of)
{
PRECONDITION(expr.type().id() == ID_pointer);
Copy link
Owner

Choose a reason for hiding this comment

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

Isn't this DATA_INVARIANT?

Copy link
Author

Choose a reason for hiding this comment

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

Same thing as above really, but I can change it to make it clearer.

DATA_INVARIANT(
expr.operands().size() == 1, "address_of should have a single operand");
expr.operands().size() == 1, "address_of should have a single operand");
Copy link
Owner

Choose a reason for hiding this comment

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

Unnecessary (and incorrect) indentation change.

DATA_INVARIANT(code.operands().size()==1,
"havoc_object must have one operand");
DATA_INVARIANT(
code.operands().size() == 1,
Copy link
Owner

Choose a reason for hiding this comment

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

Again, original message was better...

if(state.atomic_section_id!=0)
throw "start_thread in atomic section detected";
INVARIANT(state.atomic_section_id==0,
"start_thread in atomic section detected");
Copy link
Owner

Choose a reason for hiding this comment

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

This could be a PRE_CONDITION, maybe?

Copy link
Owner

Choose a reason for hiding this comment

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

The message might be better phrased as something like "start_thread not supported in atomic section" ?

Copy link
Author

Choose a reason for hiding this comment

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

While I agree with you, I find the message to be useful (in that it contains information that can't be transmitted through the condition checked easily), and making this a PRECONDITION would require the message to be killed. I don't believe this to be a good idea, this is why I kept it as an INVARIANT.

However, I can change this if there are strong feelings about it.

Copy link
Owner

Choose a reason for hiding this comment

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

INVARIANT descriptions should describe what condition we expect to be holding, not what error condition they are detecting - I think the description should be updated.

@@ -10,6 +10,11 @@ Author: Fotis Koutoulakis, [email protected]
#define CPROVER_UTIL_EXCEPTION_UTILS_H

#include <string>
#include <sstream>

#include <goto-symex/symex_target_equation.h>
Copy link
Owner

Choose a reason for hiding this comment

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

I think this is wrong. This means that 'src/util' now has a dependency on 'goto-symex' which I think is wrong. I also don't see why equation_conversion_exceptiont has been moved into this file, since it's so specific to symex....

@@ -1,47 +0,0 @@
/*******************************************************************\
Copy link
Owner

Choose a reason for hiding this comment

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

I don't see why this was removed! (see also my comments where this exception class was moved to....)

@@ -38,4 +43,31 @@ class invalid_user_input_exceptiont
}
};


class equation_conversion_exceptiont : public std::runtime_error
Copy link
Owner

Choose a reason for hiding this comment

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

If the intent of moving this exception class into this file was to make it part of the new exception class hierachy, then this class should be inheriting from something other than std::runtime_error...... I whole heartedly agree that this class should move into the new hierachy, but it shouldn't be in this util directory. It should stay in it's original file but just inherit from a suitable class in the new hierachy.

Copy link
Author

Choose a reason for hiding this comment

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

There's no hierarchy yet, and thinking about how to model one I think it's trickier than it looks to just design one on the spot. I will just revert this commit and note it down as one of the improvements I want to take a look at further down the line.

PRECONDITION(code.operands().size()==1);
PRECONDITION(code.op0().id()!=ID_symbol);
PRECONDITION(code.operands().size() == 1);
PRECONDITION(code.op0().id() == ID_symbol);
Copy link
Owner

Choose a reason for hiding this comment

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

Why has this PRECONDITION changed from != to == ? I don't see why this patch hunk is here as apart from that operator changing, it's just whitespace formatting changing, which is not related to this pr.

Copy link
Author

Choose a reason for hiding this comment

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

The original code for this was:

if(code.op0().id()!=ID_symbol)	
    throw "decl expects symbol as first operand";

When we turn these if conditions into PRECONDITIONs, we are changing from checking if something shouldn't happen, to checking that the opposite should always happen. I hadn't made the reversal of the condition properly in one of the previous commits, and this compensates for that.

PRECONDITION(state.atomic_section_id == goto_state.atomic_section_id);
INVARIANT(
state.atomic_section_id == goto_state.atomic_section_id,
"atomic sections differ across branches");
Copy link
Owner

Choose a reason for hiding this comment

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

The description here is not quite right - the invariant is more a statement that "atomic section should be the same across branches". The description is a description of what invariant we are expecting to hold, not a description of what has gone wrong.

@@ -222,7 +222,7 @@ void build_goto_trace(
else if(it->is_atomic_end() && current_time<0)
current_time*=-1;

assert(current_time>=0);
INVARIANT(current_time>=0, "time keeping inconsistency");
Copy link
Owner

Choose a reason for hiding this comment

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

Seems reasonable, thanks.

assert(dest.operands().size()==1);
DATA_INVARIANT(
dest.operands().size() == 1,
"address_of expression expected to have one operand at this point");
Copy link
Owner

Choose a reason for hiding this comment

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

"at this point" isn't needed - keep them tight.

assert(dest.operands().size()==1);
DATA_INVARIANT(
dest.operands().size() == 1,
"dereference expression expected to have one operand at this point");

Copy link
Owner

Choose a reason for hiding this comment

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

"at this point" isn't needed

DATA_INVARIANT(
g_copy.id() == ID_symbol || g_copy.id() == ID_not ||
g_copy.id() == ID_and || g_copy.id() == ID_constant,
"guards should only be and, symbol, constant, or `not'");
Copy link
Owner

Choose a reason for hiding this comment

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

Either put quote marks around all the items, or none - at the moment you only put quote marks around 'not'

Copy link
Author

Choose a reason for hiding this comment

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

Ahh, I don't. It was like this, and because I've seen this pattern* in a couple of places around cbmc, I'm assuming it is by design. I can change it if you want though.

*: The pattern being something like this always:

``something'`

throw "trace uses symbol not in alphabet: "+event;
PRECONDITION(!alphabet.empty());
INVARIANT((alphabet.count(event) != 0) == alphabet_parity,
"trace uses symbol not in alphabet: " + event);
Copy link
Owner

Choose a reason for hiding this comment

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

Description should describe what is being asserted.... "trace symbol must be in alphabet"

@@ -155,6 +155,9 @@ exprt goto_symext::address_arithmetic(
}
else if(expr.id()==ID_dereference)
{
DATA_INVARIANT(
expr.operands().size() == 1,
"dereference expression expected to have one operand");
Copy link
Owner

Choose a reason for hiding this comment

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

Indentation still...

exprt value=code.op0();
PRECONDITION(
code.operands().size() == 1 ||
frame.return_value.is_nil());
Copy link
Owner

Choose a reason for hiding this comment

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

Indentation still

Copy link
Author

@NlightNFotis NlightNFotis left a comment

Choose a reason for hiding this comment

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

@chrisr-diffblue The indentation errors were very weird this time around, because the last commit was explicitly passing git-clang-format on HEAD. It appeared that what you saw was new lines that had been indented with TAB rather than spaces, and git-clang-format was (and still is) okay with it. I have changed it manually to spaces to see if it makes it better.

DATA_INVARIANT(
g_copy.id() == ID_symbol || g_copy.id() == ID_not ||
g_copy.id() == ID_and || g_copy.id() == ID_constant,
"guards should only be and, symbol, constant, or `not'");
Copy link
Author

Choose a reason for hiding this comment

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

Ahh, I don't. It was like this, and because I've seen this pattern* in a couple of places around cbmc, I'm assuming it is by design. I can change it if you want though.

*: The pattern being something like this always:

``something'`

PRECONDITION(code.operands().size()==1);
PRECONDITION(code.op0().id()!=ID_symbol);
PRECONDITION(code.operands().size() == 1);
PRECONDITION(code.op0().id() == ID_symbol);
Copy link
Author

Choose a reason for hiding this comment

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

The original code for this was:

if(code.op0().id()!=ID_symbol)	
    throw "decl expects symbol as first operand";

When we turn these if conditions into PRECONDITIONs, we are changing from checking if something shouldn't happen, to checking that the opposite should always happen. I hadn't made the reversal of the condition properly in one of the previous commits, and this compensates for that.

if(state.atomic_section_id!=0)
throw "start_thread in atomic section detected";
INVARIANT(state.atomic_section_id==0,
"start_thread in atomic section detected");
Copy link
Owner

Choose a reason for hiding this comment

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

INVARIANT descriptions should describe what condition we expect to be holding, not what error condition they are detecting - I think the description should be updated.

@NlightNFotis NlightNFotis deleted the custom_exception branch September 5, 2018 14:14
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.

5 participants