Skip to content

Commit e7c4f43

Browse files
committed
[libc++] Improves type-safety in generator script.
This changes the code to use dataclasses instead of dict entries. It also adds type aliases to use in the typing information and updates the typing information.
1 parent cb1b51f commit e7c4f43

File tree

3 files changed

+155
-50
lines changed

3 files changed

+155
-50
lines changed

libcxx/test/libcxx/feature_test_macro/ftm_metadata.sh.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
import sys
1212

1313
sys.path.append(sys.argv[1])
14-
from generate_feature_test_macro_components import FeatureTestMacros
14+
from generate_feature_test_macro_components import FeatureTestMacros, Metadata
1515

1616

1717
def test(output, expected):
1818
assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
1919

2020

2121
ftm = FeatureTestMacros(sys.argv[2])
22+
2223
test(
2324
ftm.ftm_metadata,
2425
{

libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
del sys.argv[1:3]
1717

1818
sys.path.append(UTILS)
19-
from generate_feature_test_macro_components import FeatureTestMacros
19+
from generate_feature_test_macro_components import FeatureTestMacros, VersionHeader
2020

2121
class Test(unittest.TestCase):
2222
def setUp(self):
@@ -143,3 +143,96 @@ def test_implementation(self):
143143

144144
if __name__ == '__main__':
145145
unittest.main()
146+
ftm.version_header_implementation,
147+
{
148+
"17": [
149+
{
150+
"__cpp_lib_any": VersionHeader(
151+
value="201606L",
152+
implemented=True,
153+
need_undef=False,
154+
condition=None,
155+
),
156+
},
157+
{
158+
"__cpp_lib_parallel_algorithm": VersionHeader(
159+
value="201603L",
160+
implemented=True,
161+
need_undef=False,
162+
condition=None,
163+
),
164+
},
165+
{
166+
"__cpp_lib_variant": VersionHeader(
167+
value="202102L",
168+
implemented=True,
169+
need_undef=False,
170+
condition=None,
171+
),
172+
},
173+
],
174+
"20": [
175+
{
176+
"__cpp_lib_barrier": VersionHeader(
177+
value="201907L",
178+
implemented=True,
179+
need_undef=False,
180+
condition="!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
181+
),
182+
},
183+
{
184+
"__cpp_lib_format": VersionHeader(
185+
value="202110L",
186+
implemented=False,
187+
need_undef=False,
188+
condition=None,
189+
),
190+
},
191+
{
192+
"__cpp_lib_variant": VersionHeader(
193+
value="202106L",
194+
implemented=True,
195+
need_undef=True,
196+
condition=None,
197+
),
198+
},
199+
],
200+
"23": [
201+
{
202+
"__cpp_lib_format": VersionHeader(
203+
value="202207L",
204+
implemented=False,
205+
need_undef=False,
206+
condition=None,
207+
),
208+
},
209+
],
210+
"26": [
211+
{
212+
"__cpp_lib_barrier": VersionHeader(
213+
value="299900L",
214+
implemented=True,
215+
need_undef=True,
216+
condition="!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
217+
),
218+
},
219+
{
220+
"__cpp_lib_format": VersionHeader(
221+
value="202311L",
222+
implemented=False,
223+
need_undef=False,
224+
condition=None,
225+
),
226+
},
227+
{
228+
"__cpp_lib_variant": VersionHeader(
229+
value="202306L",
230+
implemented=True,
231+
need_undef=True,
232+
condition=None,
233+
),
234+
},
235+
],
236+
},
237+
)
238+
>>>>>>> a6a7541b7b8c ([libc++] Improves type-safety in generator script.)

libcxx/utils/generate_feature_test_macro_components.py

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
import os
44
from builtins import range
5+
from dataclasses import dataclass
56
from functools import reduce
6-
from typing import Any, Dict, List # Needed for python 3.8 compatibility.
7+
from typing import (
8+
Any,
9+
Dict,
10+
List, # Needed for python 3.8 compatibility.
11+
NewType,
12+
Optional,
13+
)
714
import functools
815
import json
916

@@ -1944,9 +1951,28 @@ def produce_docs():
19441951
f.write(doc_str)
19451952

19461953

1954+
Std = NewType("Std", str) # Standard version number
1955+
Ftm = NewType("Ftm", str) # The name of a feature test macro
1956+
Value = NewType("Value", str) # The value of a feature test macro including the L suffix
1957+
1958+
@dataclass
1959+
class Metadata:
1960+
headers: list[str] = None
1961+
test_suite_guard: str = None
1962+
libcxx_guard: str = None
1963+
1964+
1965+
@dataclass
1966+
class VersionHeader:
1967+
value: Value = None
1968+
implemented: bool = None
1969+
need_undef: bool = None
1970+
condition: str = None
1971+
1972+
19471973
def get_ftms(
1948-
data, std_dialects: List[str], use_implemented_status: bool
1949-
) -> Dict[str, Dict[str, Any]]:
1974+
data, std_dialects: List[Std], use_implemented_status: bool
1975+
) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
19501976
"""Impementation for FeatureTestMacros.(standard|implemented)_ftms()."""
19511977
result = dict()
19521978
for feature in data:
@@ -1983,7 +2009,7 @@ def get_ftms(
19832009
return result
19842010

19852011

1986-
def generate_version_header_dialect_block(data: Dict[str, Any]) -> str:
2012+
def generate_version_header_dialect_block(data: Dict[Ftm, VersionHeader]) -> str:
19872013
"""Generates the contents of the version header for a dialect.
19882014
19892015
This generates the contents of a
@@ -1994,27 +2020,29 @@ def generate_version_header_dialect_block(data: Dict[str, Any]) -> str:
19942020
result = ""
19952021
for element in data:
19962022
for ftm, entry in element.items():
1997-
if not entry["implemented"]:
2023+
if not entry.implemented:
19982024
# When a FTM is not implemented don't add the guards
19992025
# or undefine the (possibly) defined macro.
2000-
result += f'// define {ftm} {entry["value"]}\n'
2026+
result += f"// define {ftm} {entry.value}\n"
20012027
else:
2002-
need_undef = entry["need_undef"]
2003-
if entry["condition"]:
2004-
result += f'# if {entry["condition"]}\n'
2005-
if entry["need_undef"]:
2028+
need_undef = entry.need_undef
2029+
if entry.condition:
2030+
result += f"# if {entry.condition}\n"
2031+
if entry.need_undef:
20062032
result += f"# undef {ftm}\n"
2007-
result += f'# define {ftm} {entry["value"]}\n'
2033+
result += f"# define {ftm} {entry.value}\n"
20082034
result += f"# endif\n"
20092035
else:
2010-
if entry["need_undef"]:
2036+
if entry.need_undef:
20112037
result += f"# undef {ftm}\n"
2012-
result += f'# define {ftm} {entry["value"]}\n'
2038+
result += f"# define {ftm} {entry.value}\n"
20132039

20142040
return result
20152041

20162042

2017-
def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str:
2043+
def generate_version_header_implementation(
2044+
data: Dict[Std, Dict[Ftm, VersionHeader]]
2045+
) -> str:
20182046
"""Generates the body of the version header."""
20192047

20202048
template = """#if _LIBCPP_STD_VER >= {dialect}
@@ -2132,7 +2160,7 @@ def __init__(self, filename: str):
21322160
self.__data = json.load(f)
21332161

21342162
@functools.cached_property
2135-
def std_dialects(self) -> List[str]:
2163+
def std_dialects(self) -> List[Std]:
21362164
"""Returns the C++ dialects avaiable.
21372165
21382166
The available dialects are based on the 'c++xy' keys found the 'values'
@@ -2151,63 +2179,44 @@ def std_dialects(self) -> List[str]:
21512179
return sorted(list(dialects))
21522180

21532181
@functools.cached_property
2154-
def standard_ftms(self) -> Dict[str, Dict[str, Any]]:
2182+
def standard_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
21552183
"""Returns the FTM versions per dialect in the Standard.
21562184
21572185
This function does not use the 'implemented' flag. The output contains
21582186
the versions used in the Standard. When a FTM in libc++ is not
21592187
implemented according to the Standard to output may opt to show the
21602188
expected value.
2161-
2162-
The result is a dict with the following content
2163-
- key: Name of the feature test macro.
2164-
- value: A dict with the following content:
2165-
* key: The version of the C++ dialect.
2166-
* value: The value of the feature-test macro.
21672189
"""
21682190
return get_ftms(self.__data, self.std_dialects, False)
21692191

21702192
@functools.cached_property
2171-
def implemented_ftms(self) -> Dict[str, Dict[str, Any]]:
2193+
def implemented_ftms(self) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
21722194
"""Returns the FTM versions per dialect implemented in libc++.
21732195
21742196
Unlike `get_std_dialect_versions` this function uses the 'implemented'
21752197
flag. This returns the actual implementation status in libc++.
2176-
2177-
The result is a dict with the following content
2178-
- key: Name of the feature test macro.
2179-
- value: A dict with the following content:
2180-
* key: The version of the C++ dialect.
2181-
* value: The value of the feature-test macro. When a feature-test
2182-
macro is not implemented its value is None.
21832198
"""
21842199

21852200
return get_ftms(self.__data, self.std_dialects, True)
21862201

21872202
@functools.cached_property
2188-
def ftm_metadata(self) -> Dict[str, Dict[str, Any]]:
2203+
def ftm_metadata(self) -> Dict[Ftm, Metadata]:
21892204
"""Returns the metadata of the FTMs defined in the Standard.
21902205
21912206
The metadata does not depend on the C++ dialect used.
2192-
The result is a dict with the following contents:
2193-
- key: Name of the feature test macro.
2194-
- value: A dict with the following content:
2195-
* headers: The list of headers that should provide the FTM
2196-
* test_suite_guard: The condition for testing the FTM in the test suite.
2197-
* test_suite_guard: The condition for testing the FTM in the version header.
21982207
"""
21992208
result = dict()
22002209
for feature in self.__data:
2201-
entry = dict()
2202-
entry["headers"] = feature["headers"]
2203-
entry["test_suite_guard"] = feature.get("test_suite_guard", None)
2204-
entry["libcxx_guard"] = feature.get("libcxx_guard", None)
2205-
result[feature["name"]] = entry
2210+
result[feature["name"]] = Metadata(
2211+
feature["headers"],
2212+
feature.get("test_suite_guard", None),
2213+
feature.get("libcxx_guard", None),
2214+
)
22062215

22072216
return result
22082217

22092218
@property
2210-
def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]:
2219+
def version_header_implementation(self) -> Dict[Std, Dict[Ftm, VersionHeader]]:
22112220
"""Generates the body of the version header."""
22122221
result = dict()
22132222
for std in self.std_dialects:
@@ -2223,13 +2232,15 @@ def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]:
22232232
continue
22242233
last_value = value
22252234

2226-
entry = dict()
2227-
entry["value"] = value
2228-
entry["implemented"] = self.implemented_ftms[ftm][std] == self.standard_ftms[ftm][std]
2229-
entry["need_undef"] = last_entry is not None and last_entry["implemented"] and entry["implemented"]
2230-
entry["condition"] = self.ftm_metadata[ftm]["libcxx_guard"]
2235+
entry = VersionHeader(
2236+
value,
2237+
self.implemented_ftms[ftm][std] != None,
2238+
need_undef,
2239+
self.ftm_metadata[ftm].libcxx_guard,
2240+
)
22312241

22322242
last_entry = entry
2243+
22332244
result[get_std_number(std)].append(dict({ftm: entry}))
22342245

22352246
return result

0 commit comments

Comments
 (0)