Skip to content

Commit a83cb1b

Browse files
authored
Merge pull request #1848 from goblint/yaml-witness-2.1
Add YAML witness 2.1 parsing support
2 parents 2bfbdf9 + 2e856b4 commit a83cb1b

File tree

5 files changed

+234
-44
lines changed

5 files changed

+234
-44
lines changed

src/analyses/unassumeAnalysis.ml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,26 @@ struct
127127
let validate_invariant i (invariant: YamlWitnessType.InvariantSet.Invariant.t) =
128128
let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in
129129
match YamlWitness.invariant_type_enabled target_type, invariant.invariant_type with
130-
| true, LocationInvariant x ->
130+
| true, LocationInvariant ({labels = (None | Some []); _} as x) ->
131131
unassume_location_invariant ~i x
132-
| true, LoopInvariant x ->
132+
| true, LoopInvariant ({labels = (None | Some []); _} as x) ->
133133
unassume_loop_invariant ~i x
134134
| false, (LocationInvariant _ | LoopInvariant _) ->
135135
M.info_noloc ~category:Witness "disabled invariant of type %s" target_type
136136
| _ ->
137137
M.warn_noloc ~category:Witness "cannot unassume invariant of type %s" target_type
138138
in
139139

140-
List.iteri validate_invariant invariant_set.content
140+
let validate_invariant_kind i (invariant_kind: YamlWitnessType.InvariantSet.InvariantKind.t) =
141+
let target_type = YamlWitnessType.InvariantSet.InvariantKind.invariant_kind invariant_kind in
142+
match invariant_kind with
143+
| Invariant x ->
144+
validate_invariant i x
145+
| _ ->
146+
M.warn_noloc ~category:Witness "cannot validate invariant of kind %s" target_type
147+
in
148+
149+
List.iteri validate_invariant_kind invariant_set.content
141150
in
142151

143152
match YamlWitness.entry_type_enabled target_type, entry.entry_type with

src/witness/yamlWitness.ml

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,34 @@ struct
5757
function_ = Some location_function;
5858
}
5959

60-
let location_invariant ~location ~(invariant): InvariantSet.Invariant.t = {
61-
invariant_type = LocationInvariant {
62-
location;
63-
value = invariant;
64-
format = "c_expression";
65-
};
66-
}
60+
let location_invariant ~location ~(invariant): InvariantSet.InvariantKind.t =
61+
Invariant {
62+
invariant_type = LocationInvariant {
63+
location;
64+
value = invariant;
65+
format = "c_expression";
66+
labels = None;
67+
};
68+
}
6769

68-
let loop_invariant ~location ~(invariant): InvariantSet.Invariant.t = {
69-
invariant_type = LoopInvariant {
70-
location;
71-
value = invariant;
72-
format = "c_expression";
73-
};
74-
}
70+
let loop_invariant ~location ~(invariant): InvariantSet.InvariantKind.t =
71+
Invariant {
72+
invariant_type = LoopInvariant {
73+
location;
74+
value = invariant;
75+
format = "c_expression";
76+
labels = None;
77+
};
78+
}
7579

7680
(* non-standard extension *)
77-
let flow_insensitive_invariant ~(invariant): InvariantSet.Invariant.t = {
78-
invariant_type = FlowInsensitiveInvariant {
79-
value = invariant;
80-
format = "c_expression";
81-
};
82-
}
81+
let flow_insensitive_invariant ~(invariant): InvariantSet.InvariantKind.t =
82+
Invariant {
83+
invariant_type = FlowInsensitiveInvariant {
84+
value = invariant;
85+
format = "c_expression";
86+
};
87+
}
8388

8489
let invariant_set ~task ~(invariants): Entry.t = {
8590
entry_type = InvariantSet {
@@ -569,9 +574,9 @@ struct
569574
let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) =
570575
let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in
571576
match invariant_type_enabled target_type, invariant.invariant_type with
572-
| true, LocationInvariant x ->
577+
| true, LocationInvariant ({labels = (None | Some []); _} as x) ->
573578
validate_location_invariant x
574-
| true, LoopInvariant x ->
579+
| true, LoopInvariant ({labels = (None | Some []); _} as x) ->
575580
validate_loop_invariant x
576581
| false, (LocationInvariant _ | LoopInvariant _) ->
577582
incr cnt_disabled;
@@ -581,7 +586,17 @@ struct
581586
M.warn_noloc ~category:Witness "cannot validate invariant of type %s" target_type
582587
in
583588

584-
List.iter validate_invariant invariant_set.content
589+
let validate_invariant_kind (invariant_kind: YamlWitnessType.InvariantSet.InvariantKind.t) =
590+
let target_type = YamlWitnessType.InvariantSet.InvariantKind.invariant_kind invariant_kind in
591+
match invariant_kind with
592+
| Invariant x ->
593+
validate_invariant x
594+
| _ ->
595+
incr cnt_unsupported;
596+
M.warn_noloc ~category:Witness "cannot validate invariant of kind %s" target_type
597+
in
598+
599+
List.iter validate_invariant_kind invariant_set.content
585600
in
586601

587602
let validate_violation_sequence (violation_sequence: YamlWitnessType.ViolationSequence.t) =

src/witness/yamlWitnessType.ml

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,24 +159,30 @@ struct
159159
location: Location.t;
160160
value: string;
161161
format: string;
162+
labels: string list option;
162163
}
163164
[@@deriving eq, ord, hash]
164165

165166
let invariant_type = "loop_invariant"
166167

167-
let to_yaml' {location; value; format} =
168+
let to_yaml' {location; value; format; labels} =
168169
[
169170
("location", Location.to_yaml location);
170171
("value", `String value);
171172
("format", `String format);
172-
]
173+
] @ match labels with
174+
| Some labels -> [
175+
("labels", `A (List.map (fun label -> `String label) labels));
176+
]
177+
| None -> []
173178

174179
let of_yaml y =
175180
let open GobYaml in
176181
let+ location = y |> find "location" >>= Location.of_yaml
177182
and+ value = y |> find "value" >>= to_string
178-
and+ format = y |> find "format" >>= to_string in
179-
{location; value; format}
183+
and+ format = y |> find "format" >>= to_string
184+
and+ labels = y |> Yaml.Util.find "labels" >>= option_map (fun y -> y |> list >>= list_map to_string) in
185+
{location; value; format; labels}
180186
end
181187

182188
module LocationInvariant =
@@ -186,6 +192,20 @@ struct
186192
let invariant_type = "location_invariant"
187193
end
188194

195+
module LoopTransitionInvariant =
196+
struct
197+
include LoopInvariant
198+
199+
let invariant_type = "loop_transition_invariant"
200+
end
201+
202+
module LocationTransitionInvariant =
203+
struct
204+
include LoopTransitionInvariant
205+
206+
let invariant_type = "location_transition_invariant"
207+
end
208+
189209
module FlowInsensitiveInvariant =
190210
struct
191211
type t = {
@@ -215,17 +235,23 @@ struct
215235
type t =
216236
| LocationInvariant of LocationInvariant.t
217237
| LoopInvariant of LoopInvariant.t
238+
| LoopTransitionInvariant of LoopTransitionInvariant.t
239+
| LocationTransitionInvariant of LocationTransitionInvariant.t
218240
| FlowInsensitiveInvariant of FlowInsensitiveInvariant.t
219241
[@@deriving eq, ord, hash]
220242

221243
let invariant_type = function
222244
| LocationInvariant _ -> LocationInvariant.invariant_type
223245
| LoopInvariant _ -> LoopInvariant.invariant_type
246+
| LoopTransitionInvariant _ -> LoopTransitionInvariant.invariant_type
247+
| LocationTransitionInvariant _ -> LocationTransitionInvariant.invariant_type
224248
| FlowInsensitiveInvariant _ -> FlowInsensitiveInvariant.invariant_type
225249

226250
let to_yaml' = function
227251
| LocationInvariant x -> LocationInvariant.to_yaml' x
228252
| LoopInvariant x -> LoopInvariant.to_yaml' x
253+
| LoopTransitionInvariant x -> LoopTransitionInvariant.to_yaml' x
254+
| LocationTransitionInvariant x -> LocationTransitionInvariant.to_yaml' x
229255
| FlowInsensitiveInvariant x -> FlowInsensitiveInvariant.to_yaml' x
230256

231257
let of_yaml y =
@@ -237,6 +263,12 @@ struct
237263
else if invariant_type = LoopInvariant.invariant_type then
238264
let+ x = y |> LoopInvariant.of_yaml in
239265
LoopInvariant x
266+
else if invariant_type = LoopTransitionInvariant.invariant_type then
267+
let+ x = y |> LoopTransitionInvariant.of_yaml in
268+
LoopTransitionInvariant x
269+
else if invariant_type = LocationTransitionInvariant.invariant_type then
270+
let+ x = y |> LocationTransitionInvariant.of_yaml in
271+
LocationTransitionInvariant x
240272
else if invariant_type = FlowInsensitiveInvariant.invariant_type then
241273
let+ x = y |> FlowInsensitiveInvariant.of_yaml in
242274
FlowInsensitiveInvariant x
@@ -251,6 +283,8 @@ struct
251283
}
252284
[@@deriving eq, ord, hash]
253285

286+
let invariant_kind = "invariant"
287+
254288
let to_yaml {invariant_type} =
255289
`O [
256290
("invariant", `O ([
@@ -265,19 +299,132 @@ struct
265299
{invariant_type}
266300
end
267301

302+
module FunctionContract =
303+
struct
304+
type t = {
305+
location: Location.t;
306+
requires: string;
307+
ensures: string;
308+
format: string;
309+
labels: string list option;
310+
}
311+
[@@deriving eq, ord, hash]
312+
313+
let contract_type = "function_contract"
314+
315+
let to_yaml' {location; requires; ensures; format; labels} =
316+
[
317+
("location", Location.to_yaml location);
318+
("requires", `String requires);
319+
("ensures", `String ensures);
320+
("format", `String format);
321+
] @ match labels with
322+
| Some labels -> [
323+
("labels", `A (List.map (fun label -> `String label) labels));
324+
]
325+
| None -> []
326+
327+
let of_yaml y =
328+
let open GobYaml in
329+
let+ location = y |> find "location" >>= Location.of_yaml
330+
and+ requires = y |> find "requires" >>= to_string
331+
and+ ensures = y |> find "ensures" >>= to_string
332+
and+ format = y |> find "format" >>= to_string
333+
and+ labels = y |> Yaml.Util.find "labels" >>= option_map (fun y -> y |> list >>= list_map to_string) in
334+
{location; requires; ensures; format; labels}
335+
end
336+
337+
(* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *)
338+
module ContractType =
339+
struct
340+
type t =
341+
| FunctionContract of FunctionContract.t
342+
[@@deriving eq, ord, hash]
343+
344+
let contract_type = function
345+
| FunctionContract _ -> FunctionContract.contract_type
346+
347+
let to_yaml' = function
348+
| FunctionContract x -> FunctionContract.to_yaml' x
349+
350+
let of_yaml y =
351+
let open GobYaml in
352+
let* contract_type = y |> find "type" >>= to_string in
353+
if contract_type = FunctionContract.contract_type then
354+
let+ x = y |> FunctionContract.of_yaml in
355+
FunctionContract x
356+
else
357+
Error (`Msg "type")
358+
end
359+
360+
module Contract =
361+
struct
362+
type t = {
363+
contract_type: ContractType.t;
364+
}
365+
[@@deriving eq, ord, hash]
366+
367+
let invariant_kind = "contract"
368+
369+
let to_yaml {contract_type} =
370+
`O [
371+
("contract", `O ([
372+
("type", `String (ContractType.contract_type contract_type));
373+
] @ ContractType.to_yaml' contract_type)
374+
)
375+
]
376+
377+
let of_yaml y =
378+
let open GobYaml in
379+
let+ contract_type = y |> find "contract" >>= ContractType.of_yaml in
380+
{contract_type}
381+
end
382+
383+
module InvariantKind =
384+
struct
385+
type t =
386+
| Invariant of Invariant.t
387+
| Contract of Contract.t
388+
[@@deriving eq, ord, hash]
389+
390+
let invariant_kind = function
391+
| Invariant _ -> Invariant.invariant_kind
392+
| Contract _ -> Contract.invariant_kind
393+
394+
let to_yaml = function
395+
| Invariant x -> Invariant.to_yaml x
396+
| Contract x -> Contract.to_yaml x
397+
398+
let of_yaml y =
399+
let open GobYaml in
400+
let* entries = y |> entries in
401+
match entries with
402+
| [(invariant_kind, _)] ->
403+
if invariant_kind = Invariant.invariant_kind then
404+
let+ x = y |> Invariant.of_yaml in
405+
Invariant x
406+
else if invariant_kind = Contract.invariant_kind then
407+
let+ x = y |> Contract.of_yaml in
408+
Contract x
409+
else
410+
Error (`Msg "kind")
411+
| _ ->
412+
Error (`Msg "kind")
413+
end
414+
268415
type t = {
269-
content: Invariant.t list;
416+
content: InvariantKind.t list;
270417
}
271418
[@@deriving eq, ord, hash]
272419

273420
let entry_type = "invariant_set"
274421

275422
let to_yaml' {content} =
276-
[("content", `A (List.map Invariant.to_yaml content))]
423+
[("content", `A (List.map InvariantKind.to_yaml content))]
277424

278425
let of_yaml y =
279426
let open GobYaml in
280-
let+ content = y |> find "content" >>= list >>= list_map Invariant.of_yaml in
427+
let+ content = y |> find "content" >>= list >>= list_map InvariantKind.of_yaml in
281428
{content}
282429
end
283430

tests/util/yamlWitnessStripCommon.ml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,30 @@ struct
2525
LocationInvariant {x with location = location_strip_file_hash x.location}
2626
| LoopInvariant x ->
2727
LoopInvariant {x with location = location_strip_file_hash x.location}
28+
| LoopTransitionInvariant x ->
29+
LoopTransitionInvariant {x with location = location_strip_file_hash x.location}
30+
| LocationTransitionInvariant x ->
31+
LocationTransitionInvariant {x with location = location_strip_file_hash x.location}
2832
| FlowInsensitiveInvariant x ->
2933
FlowInsensitiveInvariant x (* no location to strip *)
3034
in
3135
{invariant_type}
3236
in
37+
let contract_strip_file_hash ({contract_type}: InvariantSet.Contract.t): InvariantSet.Contract.t =
38+
let contract_type: InvariantSet.ContractType.t =
39+
match contract_type with
40+
| FunctionContract x ->
41+
FunctionContract {x with location = location_strip_file_hash x.location}
42+
in
43+
{contract_type}
44+
in
45+
let invariant_kind_strip_file_hash (invariant_kind: InvariantSet.InvariantKind.t): InvariantSet.InvariantKind.t =
46+
match invariant_kind with
47+
| Invariant x ->
48+
Invariant (invariant_strip_file_hash x)
49+
| Contract x ->
50+
Contract (contract_strip_file_hash x)
51+
in
3352
let waypoint_strip_file_hash ({waypoint_type}: ViolationSequence.Waypoint.t): ViolationSequence.Waypoint.t =
3453
let waypoint_type: ViolationSequence.WaypointType.t =
3554
match waypoint_type with
@@ -58,7 +77,7 @@ struct
5877
let entry_type: EntryType.t =
5978
match entry_type with
6079
| InvariantSet x ->
61-
InvariantSet {content = List.sort InvariantSet.Invariant.compare (List.map invariant_strip_file_hash x.content)} (* Sort, so order is deterministic regardless of Goblint. *)
80+
InvariantSet {content = List.sort InvariantSet.InvariantKind.compare (List.map invariant_kind_strip_file_hash x.content)} (* Sort, so order is deterministic regardless of Goblint. *)
6281
| ViolationSequence x ->
6382
ViolationSequence {content = List.map segment_strip_file_hash x.content}
6483
| GhostInstrumentation x ->

0 commit comments

Comments
 (0)