Skip to content

Commit fcd4e42

Browse files
author
thk123
committed
Corrected test for whether a program loses const-correctness
Previously the check for loss of const-correctness was to look at each assignment and check whether in either the left/right hand sides of the assignment or somewhere in the right hand side expression tree the following condition was met: Any type or any of its subtypes lost const-ness This condition is now replaced by a more subtle condition: if they are both pointers then: there subtypes should not directly lose const-ness if the subtype is a pointer, we recursively apply this check on the subtypes if they are not pointers: we don't care about const-qualification loss at this level since it is a copy This fixes diffblue/cbmc-toyota#127 where an `const int` being assigned to an `int` would trigger disabling the function pointer removal. Pulled out the private access class into a util file Updating New implementation of is_type_at_least_as_const_as only checks one depth so we don't need to pass in naked pointers. Updated the comment to reflect methods new purpose. Added unit tests to document exactly what the method is testing Added tests to demonstrate the does_type_preserve_const_correctness This is useful documentation of how it is different to the is_type_at_least_as_const_as. Added more examples of spurious const loss
1 parent 4b83a91 commit fcd4e42

File tree

9 files changed

+662
-50
lines changed

9 files changed

+662
-50
lines changed

regression/goto-analyzer/precise-const-fp-supurious-const-loss/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ void func()
2323
// Here we 'lose' const-ness except it is a copy so we shouldn't care
2424
int non_const_number=const_number;
2525
const void_fp fp = f2;
26+
27+
28+
// Here also we lose const-ness except it is a copy of pointer so we
29+
// shouldn't care
30+
const void_fp * const p2fp = &f2;
31+
const void_fp * p2fp_non_const = &p2fp;
32+
2633
fp();
2734
}
2835

regression/goto-analyzer/precise-const-fp-supurious-const-loss/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33
--show-goto-functions --verbosity 10 --pointer-check
44
^Removing function pointers and virtual functions$

src/analyses/does_remove_const.cpp

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ bool does_remove_constt::operator()() const
4747

4848
// Compare the types recursively for a point where the rhs is more
4949
// const that the lhs
50-
if(!is_type_at_least_as_const_as(&lhs_type, &rhs_type))
50+
if(!does_type_preserve_const_correctness(&lhs_type, &rhs_type))
5151
{
5252
return true;
5353
}
@@ -78,7 +78,7 @@ bool does_remove_constt::does_expr_lose_const(const exprt &expr) const
7878
if(base_type_eq(op_type, root_type, ns))
7979
{
8080
// Is this child more const-qualified than the root
81-
if(!is_type_at_least_as_const_as(&root_type, &op_type))
81+
if(!does_type_preserve_const_correctness(&root_type, &op_type))
8282
{
8383
return true;
8484
}
@@ -93,37 +93,78 @@ bool does_remove_constt::does_expr_lose_const(const exprt &expr) const
9393
return false;
9494
}
9595

96-
/// A recursive check to check the type_more_const is at least as const as type
97-
/// compare.
96+
/// A recursive check that handles when assigning a source value to a target, is
97+
/// the assignment a loss of const-correctness.
9898
///
99-
/// type_more_const | type_compare || result
100-
/// ----------------------------------------
101-
/// const int * | const int * -> true
102-
/// int * | const int * -> false
103-
/// const int * | int * -> true
104-
/// int * | int * const -> false
99+
/// For primitive types, it always returns true since these are copied
100+
///
101+
/// For pointers we requires that if in the source it's value couldn't
102+
/// be modified, then it still can't be modified in the target
103+
///
104+
/// target_type | source_type || result
105+
/// ----------------------------------------
106+
/// const int | int -> true
107+
/// int | const int -> true
108+
/// const int | const int -> true
109+
/// int | int -> true
110+
///
111+
/// int * | int * const -> true
112+
/// int * | const int * -> false
113+
/// const int * | int * -> true
114+
/// const int * | const int * -> true
115+
/// int * const | int * -> true
116+
///
117+
/// See unit/analyses/does_type_preserve_const_correcness for
118+
/// comprehensive list
119+
/// \param target_type: the resulting type
120+
/// \param source_type: the starting type
121+
/// \return Returns true if a value of type source_type could be assigned into a
122+
/// a value of target_type without losing const-correctness
123+
bool does_remove_constt::does_type_preserve_const_correctness(
124+
const typet *target_type, const typet *source_type) const
125+
{
126+
while(target_type->id()==ID_pointer)
127+
{
128+
bool direct_subtypes_at_least_as_const=
129+
is_type_at_least_as_const_as(
130+
target_type->subtype(), source_type->subtype());
131+
// We have a pointer to something, but the thing it is pointing to can't be
132+
// modified normally, but can through this pointer
133+
if(!direct_subtypes_at_least_as_const)
134+
return false;
135+
// Check the subtypes if they are pointers
136+
target_type=&target_type->subtype();
137+
source_type=&source_type->subtype();
138+
}
139+
return true;
140+
}
141+
142+
/// A simple check to check the type_more_const is at least as const as type
143+
/// compare. This only checks the exact type, use
144+
/// `is_pointer_at_least_as_constant_as` for dealing with nested types
145+
///
146+
/// type_more_const | type_compare || result
147+
/// ----------------------------------------
148+
/// const int | int -> true
149+
/// int | const int -> false
150+
/// const int | const int -> true
151+
/// int | int -> true
152+
/// int * | int * const -> false
153+
/// int * | const int * -> true
154+
/// const int * | int * -> true
155+
/// int * const | int * -> true
156+
///
157+
/// See unit/analyses/is_type_as_least_as_const_as for comprehensive list
105158
/// \param type_more_const: the type we are expecting to be at least as const
106159
/// qualified
107160
/// \param type_compare: the type we are comparing against which may be less
108161
/// const qualified
109162
/// \return Returns true if type_more_const is at least as const as type_compare
110163
bool does_remove_constt::is_type_at_least_as_const_as(
111-
const typet *type_more_const, const typet *type_compare) const
164+
const typet &type_more_const, const typet &type_compare) const
112165
{
113-
while(type_compare->id()!=ID_nil && type_more_const->id()!=ID_nil)
114-
{
115-
const c_qualifierst rhs_qualifiers(*type_compare);
116-
const c_qualifierst lhs_qualifiers(*type_more_const);
117-
if(rhs_qualifiers.is_constant && !lhs_qualifiers.is_constant)
118-
{
119-
return false;
120-
}
121-
122-
type_compare=&type_compare->subtype();
123-
type_more_const=&type_more_const->subtype();
124-
}
125-
126-
// Both the types should have the same number of subtypes
127-
assert(type_compare->id()==ID_nil && type_more_const->id()==ID_nil);
128-
return true;
166+
const c_qualifierst type_compare_qualifiers(type_compare);
167+
const c_qualifierst more_constant_qualifiers(type_more_const);
168+
return !type_compare_qualifiers.is_constant ||
169+
more_constant_qualifiers.is_constant;
129170
}

src/analyses/does_remove_const.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ class does_remove_constt
2626
bool does_expr_lose_const(const exprt &expr) const;
2727

2828
bool is_type_at_least_as_const_as(
29-
const typet *type_more_const, const typet *type_compare) const;
29+
const typet &type_more_const, const typet &type_compare) const;
30+
31+
bool does_type_preserve_const_correctness(
32+
const typet *target_type, const typet *source_type) const;
3033

3134
const goto_programt &goto_program;
3235
const namespacet &ns;

unit/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
SRC = unit_tests.cpp \
44
analyses/does_remove_const/does_expr_lose_const.cpp \
5+
analyses/does_remove_const/does_type_preserve_const_correctness.cpp \
6+
analyses/does_remove_const/is_type_at_least_as_const_as.cpp \
57
catch_example.cpp \
68
# Empty last line
79

unit/analyses/does_remove_const/does_expr_lose_const.cpp

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,8 @@
1616
#include <util/std_types.h>
1717
#include <ansi-c/c_qualifiers.h>
1818
#include <goto-programs/goto_program.h>
19+
#include <analyses/does_remove_const/does_remove_const_util.h>
1920

20-
// This class provides access to private members and functions of
21-
// does_remove_const
22-
class does_remove_const_testt
23-
{
24-
public:
25-
does_remove_const_testt(does_remove_constt does_remove_const):
26-
does_remove_const(does_remove_const)
27-
{}
28-
bool does_expr_lose_const(const exprt &expr) const
29-
{
30-
return does_remove_const.does_expr_lose_const(expr);
31-
}
32-
33-
private:
34-
does_remove_constt does_remove_const;
35-
36-
};
3721

3822
SCENARIO("does_expr_lose_const",
3923
"[core][analyses][does_remove_const][does_expr_remove_const]")
@@ -78,14 +62,10 @@ SCENARIO("does_expr_lose_const",
7862
typet const_pointer_to_const_int_type=pointer_typet(const_primitive_type);
7963
const_qualifier.write(const_pointer_to_const_int_type);
8064

81-
// const int const_primitive;
8265
symbol_exprt const_primitive_symbol(
8366
"const_primitive", const_primitive_type);
84-
85-
// int non_const_primitive;
8667
symbol_exprt non_const_primitive_symbol(
8768
"non_const_primitive", non_const_primitive_type);
88-
8969
symbol_exprt pointer_to_int_symbol(
9070
"pointer_to_int", pointer_to_int_type);
9171
symbol_exprt const_pointer_to_int_symbol(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*******************************************************************\
2+
3+
Module: Does Remove Const Unit Tests
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Does Remove Const Unit Tests
11+
12+
#ifndef CPROVER__ANALYSES_DOES_REMOVE_CONST_DOES_REMOVE_CONST_UTIL_H
13+
#define CPROVER__ANALYSES_DOES_REMOVE_CONST_DOES_REMOVE_CONST_UTIL_H
14+
15+
#include <analyses/does_remove_const.h>
16+
17+
// This class provides access to private members and functions of
18+
// does_remove_const
19+
class does_remove_const_testt
20+
{
21+
public:
22+
explicit does_remove_const_testt(does_remove_constt does_remove_const):
23+
does_remove_const(does_remove_const)
24+
{}
25+
26+
bool does_expr_lose_const(const exprt &expr) const
27+
{
28+
return does_remove_const.does_expr_lose_const(expr);
29+
}
30+
31+
bool is_type_at_least_as_const_as(
32+
const typet &type_more_const, const typet &type_compare) const
33+
{
34+
return does_remove_const.is_type_at_least_as_const_as(
35+
type_more_const, type_compare);
36+
}
37+
38+
bool does_type_preserve_const_correctness(
39+
const typet *target_type, const typet *source_type) const
40+
{
41+
return does_remove_const.does_type_preserve_const_correctness(
42+
target_type, source_type);
43+
}
44+
45+
private:
46+
does_remove_constt does_remove_const;
47+
};
48+
49+
#endif // CPROVER__ANALYSES_DOES_REMOVE_CONST_DOES_REMOVE_CONST_UTIL_H

0 commit comments

Comments
 (0)