Skip to content

Commit 7a81ea5

Browse files
committed
Value set: handle with, array and struct expressions more accurately
We may have a suffix indicating that the result of a get_value_set_rec query would effectively be projected down to particular struct member or array indices on completion (and so we in fact do this eagerly at the bottom of the recursion, rather than at the top as one might expect). However, the ID_array, ID_struct and ID_with expressions are widening -- one or more of their operands is integrated into a larger type -- so we should consume suffix entries introduced by ID_member expressions if available.
1 parent f3630f0 commit 7a81ea5

File tree

7 files changed

+452
-43
lines changed

7 files changed

+452
-43
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
public class Cart {
2+
3+
class Product {
4+
public String size;
5+
public Category cat;
6+
}
7+
8+
class Category {
9+
public String name;
10+
}
11+
12+
public boolean checkTax4(Product product, String s) {
13+
product.size="abc";
14+
return s.startsWith(product.cat.name);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
Cart.class
3+
--cp `../../../../scripts/format_classpath.sh . ../../../src/java_bytecode/library/core-models.jar` --trace --cover location --java-max-vla-length 96 --refine-strings --java-unwind-enum-static --max-nondet-string-length 200 --unwind 4 --lazy-methods Cart.class --function Cart.checkTax4 --string-printable
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
checkTax4:.*: SATISFIED
7+
--
8+
checkTax4:.*: FAILED
9+
dec_solve: current index set is empty, this should not happen
10+
^warning: ignoring
11+
--
12+
This checks that assigning to a field of an object (which generates a WITH expression in this case) doesn't result
13+
in conflating the String-typed and non-String-typed fields of the Cart class. When they are conflated we get
14+
warnings from the string solver and missing coverage.

src/pointer-analysis/value_set.cpp

+108-43
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,30 @@ void value_sett::get_value_set(
344344
get_value_set_rec(tmp, dest, "", tmp.type(), ns);
345345
}
346346

347+
/// Check if 'suffix' starts with 'field'.
348+
/// Suffix is delimited by periods and '[]' (array access) tokens, so we're
349+
/// looking for ".field($|[]|.)"
350+
static bool suffix_starts_with_field(
351+
const std::string &suffix, const std::string &field)
352+
{
353+
return
354+
!suffix.empty() &&
355+
suffix[0] == '.' &&
356+
suffix.compare(1, field.length(), field) == 0 &&
357+
(suffix.length() == field.length() + 1 ||
358+
suffix[field.length() + 1] == '.' ||
359+
suffix[field.length() + 1] == '[');
360+
}
361+
362+
static std::string strip_first_field_from_suffix(
363+
const std::string &suffix, const std::string &field)
364+
{
365+
INVARIANT(
366+
suffix_starts_with_field(suffix, field),
367+
"suffix should start with " + field);
368+
return suffix.substr(field.length() + 1);
369+
}
370+
347371
void value_sett::get_value_set_rec(
348372
const exprt &expr,
349373
object_mapt &dest,
@@ -711,72 +735,113 @@ void value_sett::get_value_set_rec(
711735
}
712736
else if(expr.id()==ID_struct)
713737
{
738+
const auto &struct_components = to_struct_type(expr_type).components();
739+
INVARIANT(
740+
struct_components.size() == expr.operands().size(),
741+
"struct expression should have an operand per component");
742+
auto component_iter = struct_components.begin();
743+
bool found_component = false;
744+
714745
// a struct constructor, which may contain addresses
746+
715747
forall_operands(it, expr)
716-
get_value_set_rec(*it, dest, suffix, original_type, ns);
748+
{
749+
const std::string &component_name =
750+
id2string(component_iter->get_name());
751+
if(suffix_starts_with_field(suffix, component_name))
752+
{
753+
std::string remaining_suffix =
754+
strip_first_field_from_suffix(suffix, component_name);
755+
get_value_set_rec(*it, dest, remaining_suffix, original_type, ns);
756+
found_component = true;
757+
}
758+
++component_iter;
759+
}
760+
761+
if(!found_component)
762+
{
763+
// Struct field doesn't appear as expected -- this has probably been
764+
// cast from an incompatible type. Conservatively assume all fields may
765+
// be of interest.
766+
forall_operands(it, expr)
767+
get_value_set_rec(*it, dest, suffix, original_type, ns);
768+
}
717769
}
718770
else if(expr.id()==ID_with)
719771
{
720-
assert(expr.operands().size()==3);
721-
722-
// this is the array/struct
723-
object_mapt tmp_map0;
724-
get_value_set_rec(expr.op0(), tmp_map0, suffix, original_type, ns);
772+
const with_exprt &with_expr = to_with_expr(expr);
725773

726-
// this is the update value -- note NO SUFFIX
727-
object_mapt tmp_map2;
728-
get_value_set_rec(expr.op2(), tmp_map2, "", original_type, ns);
729-
730-
if(expr_type.id()==ID_struct)
774+
// If the suffix is empty we're looking for the whole struct:
775+
// default to combining both options as below.
776+
if(expr_type.id() == ID_struct && !suffix.empty())
731777
{
732-
#if 0
733-
const object_map_dt &object_map0=tmp_map0.read();
734-
irep_idt component_name=expr.op1().get(ID_component_name);
735-
736-
bool insert=true;
737-
738-
for(object_map_dt::const_iterator
739-
it=object_map0.begin();
740-
it!=object_map0.end();
741-
it++)
778+
irep_idt component_name = with_expr.where().get(ID_component_name);
779+
if(suffix_starts_with_field(suffix, id2string(component_name)))
742780
{
743-
const exprt &e=to_expr(it);
744-
745-
if(e.id()==ID_member &&
746-
e.get(ID_component_name)==component_name)
747-
{
748-
if(insert)
749-
{
750-
dest.write().insert(tmp_map2.read().begin(), tmp_map2.read().end());
751-
insert=false;
752-
}
753-
}
754-
else
755-
dest.write().insert(*it);
781+
// Looking for the member overwritten by this WITH expression
782+
std::string remaining_suffix =
783+
strip_first_field_from_suffix(suffix, id2string(component_name));
784+
get_value_set_rec(
785+
with_expr.new_value(), dest, remaining_suffix, original_type, ns);
786+
}
787+
else if(to_struct_type(expr_type).has_component(component_name))
788+
{
789+
// Looking for a non-overwritten member, look through this expression
790+
get_value_set_rec(with_expr.old(), dest, suffix, original_type, ns);
791+
}
792+
else
793+
{
794+
// Member we're looking for is not defined in this struct -- this
795+
// must be a reinterpret cast of some sort. Default to conservatively
796+
// assuming either operand might be involved.
797+
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
798+
get_value_set_rec(expr.op2(), dest, "", original_type, ns);
756799
}
757-
#else
758-
// Should be more precise! We only want "suffix"
759-
make_union(dest, tmp_map0);
760-
make_union(dest, tmp_map2);
761-
#endif
800+
}
801+
else if(expr_type.id() == ID_array && !suffix.empty())
802+
{
803+
std::string new_value_suffix;
804+
if(has_prefix(suffix, "[]"))
805+
new_value_suffix = suffix.substr(2);
806+
807+
// Otherwise use a blank suffix on the assumption anything involved with
808+
// the new value might be interesting.
809+
810+
get_value_set_rec(with_expr.old(), dest, suffix, original_type, ns);
811+
get_value_set_rec(
812+
with_expr.new_value(), dest, new_value_suffix, original_type, ns);
762813
}
763814
else
764815
{
765-
make_union(dest, tmp_map0);
766-
make_union(dest, tmp_map2);
816+
// Something else-- the suffixes used here are a rough guess at best,
817+
// so this is imprecise.
818+
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
819+
get_value_set_rec(expr.op2(), dest, "", original_type, ns);
767820
}
768821
}
769822
else if(expr.id()==ID_array)
770823
{
771824
// an array constructor, possibly containing addresses
825+
std::string new_suffix = suffix;
826+
if(has_prefix(suffix, "[]"))
827+
new_suffix = suffix.substr(2);
828+
// Otherwise we're probably reinterpreting some other type -- try persisting
829+
// with the current suffix for want of a better idea.
830+
772831
forall_operands(it, expr)
773-
get_value_set_rec(*it, dest, suffix, original_type, ns);
832+
get_value_set_rec(*it, dest, new_suffix, original_type, ns);
774833
}
775834
else if(expr.id()==ID_array_of)
776835
{
777836
// an array constructor, possibly containing an address
837+
std::string new_suffix = suffix;
838+
if(has_prefix(suffix, "[]"))
839+
new_suffix = suffix.substr(2);
840+
// Otherwise we're probably reinterpreting some other type -- try persisting
841+
// with the current suffix for want of a better idea.
842+
778843
assert(expr.operands().size()==1);
779-
get_value_set_rec(expr.op0(), dest, suffix, original_type, ns);
844+
get_value_set_rec(expr.op0(), dest, new_suffix, original_type, ns);
780845
}
781846
else if(expr.id()==ID_dynamic_object)
782847
{

0 commit comments

Comments
 (0)