|
11 | 11 |
|
12 | 12 | #include "c_typecheck_base.h"
|
13 | 13 |
|
| 14 | +#include <util/arith_tools.h> |
14 | 15 | #include <util/c_types.h>
|
15 | 16 | #include <util/config.h>
|
16 | 17 | #include <util/expr_util.h>
|
@@ -842,35 +843,158 @@ void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
|
842 | 843 | }
|
843 | 844 | }
|
844 | 845 |
|
| 846 | +void c_typecheck_baset::typecheck_spec_assigns_condition(exprt &condition) |
| 847 | +{ |
| 848 | + // compute type |
| 849 | + typecheck_expr(condition); |
| 850 | + |
| 851 | + // non-fatal warnings |
| 852 | + if(has_subexpr(condition, [&](const exprt &subexpr) { |
| 853 | + return can_cast_expr<side_effect_expr_function_callt>(subexpr); |
| 854 | + })) |
| 855 | + { |
| 856 | + // Remark: we allow function calls without further checks for now because |
| 857 | + // they are mandatory for some applications. |
| 858 | + // The next step must be to check that the called functions have a contract |
| 859 | + // with an empty assigns clause and that they indeed satisfy their contract |
| 860 | + // using a CI check. |
| 861 | + warning().source_location = condition.source_location(); |
| 862 | + warning() |
| 863 | + << "function with possible side-effect called in target's condition" |
| 864 | + << eom; |
| 865 | + } |
| 866 | + |
| 867 | + // fatal errors |
| 868 | + bool must_throw = false; |
| 869 | + |
| 870 | + if(condition.type().id() == ID_empty) |
| 871 | + { |
| 872 | + must_throw = true; |
| 873 | + error().source_location = condition.source_location(); |
| 874 | + error() << "void-typed expressions not allowed as assigns clause conditions" |
| 875 | + << eom; |
| 876 | + } |
| 877 | + |
| 878 | + if(has_subexpr(condition, [&](const exprt &subexpr) { |
| 879 | + return can_cast_expr<side_effect_exprt>(subexpr) && |
| 880 | + !can_cast_expr<side_effect_expr_function_callt>(subexpr); |
| 881 | + })) |
| 882 | + { |
| 883 | + must_throw = true; |
| 884 | + error().source_location = condition.source_location(); |
| 885 | + error() << "side-effects not allowed in assigns clause conditions" << eom; |
| 886 | + } |
| 887 | + |
| 888 | + if(has_subexpr(condition, ID_if)) |
| 889 | + { |
| 890 | + must_throw = true; |
| 891 | + error().source_location = condition.source_location(); |
| 892 | + error() << "ternary expressions not allowed in assigns " |
| 893 | + "clause conditions" |
| 894 | + << eom; |
| 895 | + } |
| 896 | + |
| 897 | + if(must_throw) |
| 898 | + throw 0; |
| 899 | +} |
| 900 | + |
| 901 | +void c_typecheck_baset::typecheck_spec_assigns_target(exprt &target) |
| 902 | +{ |
| 903 | + // compute type |
| 904 | + typecheck_expr(target); |
| 905 | + |
| 906 | + // fatal errors |
| 907 | + bool must_throw = false; |
| 908 | + if(target.type().id() == ID_empty) |
| 909 | + { |
| 910 | + must_throw = true; |
| 911 | + error().source_location = target.source_location(); |
| 912 | + error() << "void-typed expressions not allowed as assigns clause targets" |
| 913 | + << eom; |
| 914 | + } |
| 915 | + |
| 916 | + if(!target.get_bool(ID_C_lvalue) && target.id() != ID_pointer_object) |
| 917 | + { |
| 918 | + must_throw = true; |
| 919 | + error().source_location = target.source_location(); |
| 920 | + error() << "assigns clause target must be an lvalue or " |
| 921 | + "__CPROVER_POINTER_OBJECT expression" |
| 922 | + << eom; |
| 923 | + } |
| 924 | + |
| 925 | + // Remark: an expression can be an lvalue and still contain side effects |
| 926 | + // or ternary expressions. We detect them in a second step. |
| 927 | + if(has_subexpr(target, ID_side_effect)) |
| 928 | + { |
| 929 | + must_throw = true; |
| 930 | + error().source_location = target.source_location(); |
| 931 | + error() << "side-effects not allowed in assigns clause targets" << eom; |
| 932 | + } |
| 933 | + |
| 934 | + if(has_subexpr(target, ID_if)) |
| 935 | + { |
| 936 | + must_throw = true; |
| 937 | + error().source_location = target.source_location(); |
| 938 | + error() << "ternary expressions not allowed in assigns " |
| 939 | + "clause targets" |
| 940 | + << eom; |
| 941 | + } |
| 942 | + |
| 943 | + if(must_throw) |
| 944 | + throw 0; |
| 945 | +} |
| 946 | + |
845 | 947 | void c_typecheck_baset::typecheck_spec_assigns(exprt::operandst &targets)
|
846 | 948 | {
|
| 949 | + exprt::operandst tmp; |
847 | 950 | for(auto &target : targets)
|
848 | 951 | {
|
849 |
| - typecheck_expr(target); |
850 |
| - |
851 |
| - if(target.type().id() == ID_empty) |
| 952 | + // conditional target |
| 953 | + if(target.id() == ID_conditional_assigns_target) |
852 | 954 | {
|
853 |
| - error().source_location = target.source_location(); |
854 |
| - error() << "void-typed targets not permitted in assigns clause" << eom; |
855 |
| - throw 0; |
856 |
| - } |
857 |
| - else if(target.id() == ID_pointer_object) |
858 |
| - { |
859 |
| - // skip |
860 |
| - } |
861 |
| - else if(!target.get_bool(ID_C_lvalue)) |
862 |
| - { |
863 |
| - error().source_location = target.source_location(); |
864 |
| - error() << "non-lvalue target in assigns clause" << eom; |
865 |
| - throw 0; |
| 955 | + // typecheck condition |
| 956 | + auto &condition = target.operands().at(0); |
| 957 | + typecheck_spec_assigns_condition(condition); |
| 958 | + |
| 959 | + // make it boolean if needed |
| 960 | + if(!can_cast_type<bool_typet>(condition.type())) |
| 961 | + { |
| 962 | + if(can_cast_type<pointer_typet>(condition.type())) |
| 963 | + condition = notequal_exprt{ |
| 964 | + condition, null_pointer_exprt(to_pointer_type(condition.type()))}; |
| 965 | + else |
| 966 | + condition = |
| 967 | + notequal_exprt{condition, from_integer(0, condition.type())}; |
| 968 | + } |
| 969 | + |
| 970 | + // typecheck target(s) |
| 971 | + auto &conditional_target = target.operands().at(1); |
| 972 | + if(conditional_target.id() == ID_expression_list) |
| 973 | + { |
| 974 | + // distribute condition over all targets |
| 975 | + for(auto &each : conditional_target.operands()) |
| 976 | + { |
| 977 | + typecheck_spec_assigns_target(each); |
| 978 | + exprt new_target{ID_conditional_assigns_target}; |
| 979 | + new_target.add_to_operands(condition, std::move(each)); |
| 980 | + tmp.push_back(std::move(new_target)); |
| 981 | + } |
| 982 | + } |
| 983 | + else |
| 984 | + { |
| 985 | + typecheck_spec_assigns_target(conditional_target); |
| 986 | + tmp.push_back(std::move(target)); |
| 987 | + } |
866 | 988 | }
|
867 |
| - else if(has_subexpr(target, ID_side_effect)) |
| 989 | + else // unconditional target |
868 | 990 | {
|
869 |
| - error().source_location = target.source_location(); |
870 |
| - error() << "assigns clause is not side-effect free" << eom; |
871 |
| - throw 0; |
| 991 | + typecheck_spec_assigns_target(target); |
| 992 | + tmp.push_back(std::move(target)); |
872 | 993 | }
|
873 | 994 | }
|
| 995 | + targets.clear(); |
| 996 | + for(auto &each : tmp) |
| 997 | + targets.push_back(std::move(each)); |
874 | 998 | }
|
875 | 999 |
|
876 | 1000 | void c_typecheck_baset::typecheck_spec_loop_invariant(codet &code)
|
|
0 commit comments