Skip to content

Commit 16ec171

Browse files
committed
PROOF: output-parameter analysis digest support
1 parent 5d24d1f commit 16ec171

9 files changed

+595
-296
lines changed

chc/app/CHVersion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
chcversion: str = "0.2.0-2025-11-18"
1+
chcversion: str = "0.2.0-2025-12-06"

chc/proof/CFunPODictionary.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from typing import Callable, Dict, List, Mapping, TYPE_CHECKING
3737

3838
from chc.proof.AssumptionType import AssumptionType
39+
from chc.proof.OutputParameterStatus import OutputParameterStatus
3940
from chc.proof.CFunPODictionaryRecord import podregistry
4041
from chc.proof.PPOType import PPOType
4142
from chc.proof.SPOType import SPOType
@@ -66,10 +67,13 @@ def __init__(
6667
cfun: "CFunction",
6768
xnode: ET.Element) -> None:
6869
self._cfun = cfun
70+
self.output_parameter_status_table = IndexedTable(
71+
"output-parameter-status-table")
6972
self.assumption_type_table = IndexedTable("assumption-table")
7073
self.ppo_type_table = IndexedTable("ppo-type-table")
7174
self.spo_type_table = IndexedTable("spo-type-table")
7275
self.tables = [
76+
self.output_parameter_status_table,
7377
self.assumption_type_table,
7478
self.ppo_type_table,
7579
self.spo_type_table]
@@ -102,6 +106,12 @@ def interfacedictionary(self) -> "InterfaceDictionary":
102106

103107
# ------------------------ Retrieve items from dictionary ----------------
104108

109+
def get_output_parameter_status(self, ix: int) -> OutputParameterStatus:
110+
return podregistry.mk_instance(
111+
self,
112+
self.output_parameter_status_table.retrieve(ix),
113+
OutputParameterStatus)
114+
105115
def get_assumption_type(self, ix: int) -> AssumptionType:
106116
return podregistry.mk_instance(
107117
self, self.assumption_type_table.retrieve(ix), AssumptionType)
@@ -125,6 +135,14 @@ def get_spo_type_map(self) -> Dict[int, IndexedTableValue]:
125135

126136
# ------------------------- Provide read/write xml service ---------------
127137

138+
def read_xml_output_parameter_status(
139+
self, txnode: ET.Element, tag: str = "icops") -> OutputParameterStatus:
140+
optix = txnode.get(tag)
141+
if optix is not None:
142+
return self.get_output_parameter_status(int(optix))
143+
else:
144+
raise UF.CHCError("Error in reading output-parameter-status")
145+
128146
def read_xml_assumption_type(
129147
self, txnode: ET.Element, tag: str = "iast") -> AssumptionType:
130148
optix = txnode.get(tag)

chc/proof/CFunPODictionaryRecord.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from chc.app.CApplication import CApplication
3737
from chc.app.CContext import ProgramContext
3838
from chc.app.CContextDictionary import CContextDictionary
39+
from chc.app.CDictionary import CDictionary
3940
from chc.app.CFile import CFile
4041
from chc.app.CFileDeclarations import CFileDeclarations
4142
from chc.app.CFunction import CFunction
@@ -75,6 +76,10 @@ def cfile(self) -> "CFile":
7576
def cdecls(self) -> "CFileDeclarations":
7677
return self.cfile.declarations
7778

79+
@property
80+
def cdictionary(self) -> "CDictionary":
81+
return self.cfile.dictionary
82+
7883
@property
7984
def cxd(self) -> "CContextDictionary":
8085
return self.cfile.contextdictionary
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# ------------------------------------------------------------------------------
2+
# CodeHawk C Analyzer
3+
# Author: Henny Sipma
4+
# ------------------------------------------------------------------------------
5+
# The MIT License (MIT)
6+
#
7+
# Copyright (c) 2025 Aarno Labs LLC
8+
#
9+
# Permission is hereby granted, free of charge, to any person obtaining a copy
10+
# of this software and associated documentation files (the "Software"), to deal
11+
# in the Software without restriction, including without limitation the rights
12+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
# copies of the Software, and to permit persons to whom the Software is
14+
# furnished to do so, subject to the following conditions:
15+
#
16+
# The above copyright notice and this permission notice shall be included in all
17+
# copies or substantial portions of the Software.
18+
#
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
# SOFTWARE.
26+
# ------------------------------------------------------------------------------
27+
28+
import xml.etree.ElementTree as ET
29+
30+
from typing import List, Optional, TYPE_CHECKING
31+
32+
from chc.proof.CandidateOutputParameter import CandidateOutputParameter
33+
from chc.proof.OutputParameterCalleeCallsite import OutputParameterCalleeCallsite
34+
35+
36+
if TYPE_CHECKING:
37+
from chc.app.CFile import CFile
38+
from chc.app.CFileDeclarations import CFileDeclarations
39+
from chc.app.CFunction import CFunction
40+
from chc.app.CVarInfo import CVarInfo
41+
from chc.proof.CFunPODictionary import CFunPODictionary
42+
43+
44+
class CFunctionAnalysisDigest:
45+
46+
def __init__(self, cfun: "CFunction", xnode: ET.Element) -> None:
47+
self.xnode = xnode # analysis-digest node in adg file
48+
self._cfun = cfun
49+
50+
@property
51+
def cfun(self) -> "CFunction":
52+
return self._cfun
53+
54+
@property
55+
def cfile(self) -> "CFile":
56+
return self.cfun.cfile
57+
58+
59+
class CFunctionOutputParameterAnalysisDigest(CFunctionAnalysisDigest):
60+
61+
def __init__(self, cfun: "CFunction", xnode: ET.Element) -> None:
62+
CFunctionAnalysisDigest.__init__(self, cfun, xnode)
63+
self._candidate_parameters: Optional[List[CandidateOutputParameter]] = None
64+
self._callee_callsites: Optional[List[OutputParameterCalleeCallsite]] = None
65+
66+
@property
67+
def parameters(self) -> List[CandidateOutputParameter]:
68+
if self._candidate_parameters is None:
69+
self._candidate_parameters = []
70+
xparams = self.xnode.find("candidate-parameters")
71+
if xparams is not None:
72+
for xparam in xparams.findall("param"):
73+
cparam = CandidateOutputParameter(self, xparam)
74+
self._candidate_parameters.append(cparam)
75+
return self._candidate_parameters
76+
77+
@property
78+
def callee_callsites(self) -> List[OutputParameterCalleeCallsite]:
79+
if self._callee_callsites is None:
80+
self._callee_callsites = []
81+
xsites = self.xnode.find("callee-callsites")
82+
if xsites is not None:
83+
for xsite in xsites.findall("ccs"):
84+
ccsite = OutputParameterCalleeCallsite(self, xsite)
85+
self._callee_callsites.append(ccsite)
86+
return self._callee_callsites
87+
88+
def __str__(self) -> str:
89+
lines: List[str] = []
90+
for p in self.parameters:
91+
lines.append(str(p))
92+
if len(self.callee_callsites) > 0:
93+
lines.append("\nCallee-callsites:")
94+
for ccs in self.callee_callsites:
95+
lines.append(" " + str(ccs))
96+
return "\n".join(lines)
97+
98+
99+
class CFunctionAnalysisDigests:
100+
101+
def __init__(self, cfun: "CFunction", xnode: Optional[ET.Element]) -> None:
102+
self._cfun = cfun
103+
self.xnode = xnode # analysis-digests node
104+
self._digests: Optional[List[CFunctionAnalysisDigest]] = None
105+
106+
@property
107+
def cfun(self) -> "CFunction":
108+
return self._cfun
109+
110+
@property
111+
def digests(self) -> List[CFunctionAnalysisDigest]:
112+
if self._digests is None:
113+
self._digests = []
114+
if self.xnode is not None:
115+
for xdigest in self.xnode.findall("analysis-digest"):
116+
digestkind = xdigest.get("name", "none")
117+
if digestkind == "output parameters":
118+
digest = CFunctionOutputParameterAnalysisDigest(
119+
self.cfun, xdigest)
120+
self._digests.append(digest)
121+
else:
122+
print("DEBUG: xnode is none")
123+
return self._digests
124+
125+
def __str__(self) -> str:
126+
lines: List[str] = []
127+
for digest in self.digests:
128+
lines.append(str(digest))
129+
return "\n".join(lines)

chc/proof/CPOPredicate.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#
77
# Copyright (c) 2017-2020 Kestrel Technology LLC
88
# Copyright (c) 2020-2022 Henny B. Sipma
9-
# Copyright (c) 2023-2024 Aarno Labs LLC
9+
# Copyright (c) 2023-2025 Aarno Labs LLC
1010
#
1111
# Permission is hereby granted, free of charge, to any person obtaining a copy
1212
# of this software and associated documentation files (the "Software"), to deal
@@ -80,7 +80,10 @@
8080
'no': 'no-overlap',
8181
'nt': 'null-terminated',
8282
'null': 'null',
83+
"opa": "output_parameter-argument",
8384
'opi': 'output_parameter-initialized',
85+
"opne": "output_parameter-no-escape",
86+
"ops": "output_parameter-scalar",
8487
'opu': 'output_parameter-unaltered',
8588
'pc': 'pointer-cast',
8689
'plb': 'ptr-lower-bound',
@@ -2319,3 +2322,65 @@ def __str__(self) -> str:
23192322
+ ", "
23202323
+ str(self.offset)
23212324
+ ")")
2325+
2326+
2327+
@pdregistry.register_tag("opa", CPOPredicate)
2328+
class CPOOutputParameterArgument(CPOPredicate):
2329+
2330+
def __init__(
2331+
self, pd: "CFilePredicateDictionary", ixval: IT.IndexedTableValue
2332+
) -> None:
2333+
CPOPredicate.__init__(self, pd, ixval)
2334+
2335+
@property
2336+
def exp(self) -> "CExp":
2337+
return self.cd.get_exp(self.args[0])
2338+
2339+
def __str__(self) -> str:
2340+
return "output-parameter-argument(" + str(self.exp) + ")"
2341+
2342+
2343+
@pdregistry.register_tag("ops", CPOPredicate)
2344+
class CPOOutputParameterScalar(CPOPredicate):
2345+
2346+
def __init__(
2347+
self, pd: "CFilePredicateDictionary", ixval: IT.IndexedTableValue
2348+
) -> None:
2349+
CPOPredicate.__init__(self, pd, ixval)
2350+
2351+
@property
2352+
def varinfo(self) -> "CVarInfo":
2353+
return self.cdeclarations.get_varinfo(self.args[0])
2354+
2355+
@property
2356+
def exp(self) -> "CExp":
2357+
return self.cd.get_exp(self.args[1])
2358+
2359+
def __str__(self) -> str:
2360+
return (
2361+
"output-parameter-scalar("
2362+
+ str(self.varinfo) + ", "
2363+
+ str(self.exp) + ")")
2364+
2365+
2366+
@pdregistry.register_tag("opne", CPOPredicate)
2367+
class CPOOutputParameterNoEscape(CPOPredicate):
2368+
2369+
def __init__(
2370+
self, pd: "CFilePredicateDictionary", ixval: IT.IndexedTableValue
2371+
) -> None:
2372+
CPOPredicate.__init__(self, pd, ixval)
2373+
2374+
@property
2375+
def varinfo(self) -> "CVarInfo":
2376+
return self.cdeclarations.get_varinfo(self.args[0])
2377+
2378+
@property
2379+
def exp(self) -> "CExp":
2380+
return self.cd.get_exp(self.args[1])
2381+
2382+
def __str__(self) -> str:
2383+
return (
2384+
"output-parameter-no-escape("
2385+
+ str(self.varinfo) + ", "
2386+
+ str(self.exp) + ")")

0 commit comments

Comments
 (0)