Branch data Line data Source code
1 : : // Copyright (C) 2023-2025 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #ifndef RUST_FMT_H
20 : : #define RUST_FMT_H
21 : :
22 : : #include "rust-system.h"
23 : : #include "optional.h"
24 : :
25 : : // PR122498 "rust-enabled bootstrap is broken after r16-4897"
26 : : #pragma GCC diagnostic warning "-Warray-bounds"
27 : :
28 : : namespace Rust {
29 : : namespace Fmt {
30 : :
31 : : namespace ffi {
32 : :
33 : : extern "C" {
34 : :
35 : : unsigned char *rust_ffi_alloc (size_t count, size_t elem_size, size_t align);
36 : :
37 : : void rust_ffi_dealloc (unsigned char *data, size_t count, size_t elem_size,
38 : : size_t align);
39 : :
40 : : } // extern "C"
41 : :
42 : : template <typename T> class FFIVec
43 : : {
44 : : T *data;
45 : : size_t len;
46 : : size_t cap;
47 : :
48 : : public:
49 : 105 : FFIVec () : data ((T *) alignof (T)), len (0), cap (0) {}
50 : :
51 : : FFIVec (const FFIVec &) = delete;
52 : : FFIVec &operator= (const FFIVec &) = delete;
53 : :
54 : 210 : FFIVec (FFIVec &&other) : data (other.data), len (other.len), cap (other.cap)
55 : : {
56 : 210 : other.data = (T *) alignof (T);
57 : 210 : other.len = 0;
58 : 210 : other.cap = 0;
59 : : }
60 : :
61 : 105 : FFIVec &operator= (FFIVec &&other)
62 : : {
63 : 105 : this->~FFIVec ();
64 : 105 : new (this) FFIVec (std::move (other));
65 : 105 : return *this;
66 : : }
67 : :
68 : 420 : ~FFIVec ()
69 : : {
70 : : // T can't be zero-sized
71 : 420 : if (cap)
72 : 105 : rust_ffi_dealloc ((unsigned char *) data, cap, sizeof (T), alignof (T));
73 : 420 : }
74 : :
75 : 97 : size_t size () const { return len; }
76 : :
77 : 68 : const T &operator[] (size_t idx) const
78 : : {
79 : 68 : rust_assert (idx <= len);
80 : 68 : return data[idx];
81 : : }
82 : :
83 : : T *begin () { return data; }
84 : 76 : const T *begin () const { return data; }
85 : : T *end () { return data + len; }
86 : 76 : const T *end () const { return data + len; }
87 : : };
88 : :
89 : : // https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md
90 : : template <typename T,
91 : : typename =
92 : : typename std::enable_if<std::is_standard_layout<T>::value>::type>
93 : : class FFIOpt
94 : : {
95 : : public:
96 : : template <typename U>
97 : : FFIOpt (U &&val) : some{Some::KIND, std::forward<U> (val)}
98 : : {}
99 : :
100 : : FFIOpt () : none{None::KIND} {}
101 : :
102 : : FFIOpt (const FFIOpt &other)
103 : : {
104 : : if (other.has_value ())
105 : : new (&some) Some{Some::KIND, other.some.val};
106 : : else
107 : : new (&none) None{None::KIND};
108 : : }
109 : :
110 : : FFIOpt (FFIOpt &&other)
111 : : {
112 : : if (other.has_value ())
113 : : new (&some) Some{Some::KIND, std::move (other.some.val)};
114 : : else
115 : : new (&none) None{None::KIND};
116 : : }
117 : :
118 : 687 : ~FFIOpt ()
119 : : {
120 : 687 : if (has_value ())
121 : : some.~Some ();
122 : : else
123 : : none.~None ();
124 : : }
125 : :
126 : : FFIOpt &operator= (const FFIOpt &other)
127 : : {
128 : : this->~FFIOpt ();
129 : : new (this) FFIOpt (other);
130 : : return *this;
131 : : }
132 : :
133 : : FFIOpt &operator= (FFIOpt &&other)
134 : : {
135 : : this->~FFIOpt ();
136 : : new (this) FFIOpt (std::move (other));
137 : : return *this;
138 : : }
139 : :
140 : : tl::optional<std::reference_wrapper<T>> get_opt ()
141 : : {
142 : : if (has_value ())
143 : : return std::ref (some.val);
144 : : else
145 : : return tl::nullopt;
146 : : }
147 : :
148 : : tl::optional<std::reference_wrapper<const T>> get_opt () const
149 : : {
150 : : if (has_value ())
151 : : return std::ref (some.val);
152 : : else
153 : : return tl::nullopt;
154 : : }
155 : :
156 : 687 : bool has_value () const { return some.kind == Some::KIND; }
157 : :
158 : : operator bool () const { return has_value (); }
159 : :
160 : : private:
161 : : struct Some
162 : : {
163 : : static constexpr uint8_t KIND = 0;
164 : : uint8_t kind;
165 : : T val;
166 : : };
167 : :
168 : : struct None
169 : : {
170 : : static constexpr uint8_t KIND = 1;
171 : : uint8_t kind;
172 : : };
173 : :
174 : : union
175 : : {
176 : : Some some;
177 : : None none;
178 : : };
179 : : };
180 : :
181 : : struct RustHamster
182 : : {
183 : : const char *ptr;
184 : : size_t len;
185 : :
186 : : std::string to_string () const;
187 : :
188 : 105 : explicit RustHamster (const std::string &str)
189 : 105 : : ptr (str.data ()), len (str.size ())
190 : : {}
191 : : };
192 : :
193 : : /// Enum of alignments which are supported.
194 : : enum class Alignment
195 : : {
196 : : /// The value will be aligned to the left.
197 : : AlignLeft,
198 : : /// The value will be aligned to the right.
199 : : AlignRight,
200 : : /// The value will be aligned in the center.
201 : : AlignCenter,
202 : : /// The value will take on a default alignment.
203 : : AlignUnknown,
204 : : };
205 : :
206 : : /// Enum for the debug hex flags.
207 : : enum class DebugHex
208 : : {
209 : : /// The `x` flag in `{:x?}`.
210 : : Lower,
211 : : /// The `X` flag in `{:X?}`.
212 : : Upper,
213 : : };
214 : :
215 : : /// Enum for the sign flags.
216 : : enum class Sign
217 : : {
218 : : /// The `+` flag.
219 : : Plus,
220 : : /// The `-` flag.
221 : : Minus,
222 : : };
223 : :
224 : : /// Enum describing where an argument for a format can be located.
225 : : struct Position
226 : : {
227 : : enum class Tag
228 : : {
229 : : /// The argument is implied to be located at an index
230 : : ArgumentImplicitlyIs,
231 : : /// The argument is located at a specific index given in the format,
232 : : ArgumentIs,
233 : : /// The argument has a name.
234 : : ArgumentNamed,
235 : : };
236 : :
237 : : struct ArgumentImplicitlyIs_Body
238 : : {
239 : : size_t _0;
240 : : };
241 : :
242 : : struct ArgumentIs_Body
243 : : {
244 : : size_t _0;
245 : : };
246 : :
247 : : struct ArgumentNamed_Body
248 : : {
249 : : RustHamster _0;
250 : : };
251 : :
252 : : Tag tag;
253 : : union
254 : : {
255 : : ArgumentImplicitlyIs_Body argument_implicitly_is;
256 : : ArgumentIs_Body argument_is;
257 : : ArgumentNamed_Body argument_named;
258 : : };
259 : : };
260 : :
261 : : /// Range inside of a `Span` used for diagnostics when we only have access to
262 : : /// relative positions.
263 : : struct InnerSpan
264 : : {
265 : : size_t start;
266 : : size_t end;
267 : : };
268 : :
269 : : /// A count is used for the precision and width parameters of an integer, and
270 : : /// can reference either an argument or a literal integer.
271 : : struct Count
272 : : {
273 : : enum class Tag
274 : : {
275 : : /// The count is specified explicitly.
276 : : CountIs,
277 : : /// The count is specified by the argument with the given name.
278 : : CountIsName,
279 : : /// The count is specified by the argument at the given index.
280 : : CountIsParam,
281 : : /// The count is specified by a star (like in `{:.*}`) that refers to the
282 : : /// argument at the given index.
283 : : CountIsStar,
284 : : /// The count is implied and cannot be explicitly specified.
285 : : CountImplied,
286 : : };
287 : :
288 : : struct CountIs_Body
289 : : {
290 : : size_t _0;
291 : : };
292 : :
293 : : struct CountIsName_Body
294 : : {
295 : : RustHamster _0;
296 : : InnerSpan _1;
297 : : };
298 : :
299 : : struct CountIsParam_Body
300 : : {
301 : : size_t _0;
302 : : };
303 : :
304 : : struct CountIsStar_Body
305 : : {
306 : : size_t _0;
307 : : };
308 : :
309 : : Tag tag;
310 : : union
311 : : {
312 : : CountIs_Body count_is;
313 : : CountIsName_Body count_is_name;
314 : : CountIsParam_Body count_is_param;
315 : : CountIsStar_Body count_is_star;
316 : : };
317 : : };
318 : :
319 : : /// Specification for the formatting of an argument in the format string.
320 : 568 : struct FormatSpec
321 : : {
322 : : /// Optionally specified character to fill alignment with.
323 : : FFIOpt<uint32_t> fill;
324 : : /// Span of the optionally specified fill character.
325 : : FFIOpt<InnerSpan> fill_span;
326 : : /// Optionally specified alignment.
327 : : Alignment align;
328 : : /// The `+` or `-` flag.
329 : : FFIOpt<Sign> sign;
330 : : /// The `#` flag.
331 : : bool alternate;
332 : : /// The `0` flag.
333 : : bool zero_pad;
334 : : /// The `x` or `X` flag. (Only for `Debug`.)
335 : : FFIOpt<DebugHex> debug_hex;
336 : : /// The integer precision to use.
337 : : Count precision;
338 : : /// The span of the precision formatting flag (for diagnostics).
339 : : FFIOpt<InnerSpan> precision_span;
340 : : /// The string width requested for the resulting format.
341 : : Count width;
342 : : /// The span of the width formatting flag (for diagnostics).
343 : : FFIOpt<InnerSpan> width_span;
344 : : /// The descriptor string representing the name of the format desired for
345 : : /// this argument, this can be empty or any number of characters, although
346 : : /// it is required to be one word.
347 : : RustHamster ty;
348 : : /// The span of the descriptor string (for diagnostics).
349 : : FFIOpt<InnerSpan> ty_span;
350 : : };
351 : :
352 : : /// Representation of an argument specification.
353 : 294 : struct Argument
354 : : {
355 : : /// Where to find this argument
356 : : Position position;
357 : : /// The span of the position indicator. Includes any whitespace in implicit
358 : : /// positions (`{ }`).
359 : : InnerSpan position_span;
360 : : /// How to format the argument
361 : : FormatSpec format;
362 : : };
363 : :
364 : : /// A piece is a portion of the format string which represents the next part
365 : : /// to emit. These are emitted as a stream by the `Parser` class.
366 : : struct Piece
367 : : {
368 : : enum class Tag
369 : : {
370 : : /// A literal string which should directly be emitted
371 : : String,
372 : : /// This describes that formatting should process the next argument (as
373 : : /// specified inside) for emission.
374 : : NextArgument,
375 : : };
376 : :
377 : : struct String_Body
378 : : {
379 : : RustHamster _0;
380 : : };
381 : :
382 : : struct NextArgument_Body
383 : : {
384 : : Argument _0;
385 : : };
386 : :
387 : : Tag tag;
388 : : union
389 : : {
390 : : String_Body string;
391 : : NextArgument_Body next_argument;
392 : : };
393 : : };
394 : :
395 : : enum ParseMode
396 : : {
397 : : Format = 0,
398 : : InlineAsm,
399 : : };
400 : :
401 : : extern "C" {
402 : :
403 : : FFIVec<Piece> collect_pieces (RustHamster input, bool append_newline,
404 : : ParseMode parse_mode);
405 : :
406 : : FFIVec<Piece> clone_pieces (const FFIVec<Piece> &);
407 : :
408 : : } // extern "C"
409 : :
410 : : } // namespace ffi
411 : :
412 : 134 : struct Pieces
413 : : {
414 : : static Pieces collect (const std::string &to_parse, bool append_newline,
415 : : ffi::ParseMode parse_mode);
416 : :
417 : 105 : const ffi::FFIVec<ffi::Piece> &get_pieces () const { return data->second; }
418 : :
419 : : private:
420 : 105 : Pieces (std::string str, ffi::FFIVec<ffi::Piece> pieces)
421 : 105 : : data (
422 : : std::make_shared<decltype (data)::element_type> (std::move (str),
423 : : std::move (pieces)))
424 : : {}
425 : :
426 : : // makes copying simpler
427 : : // also, we'd need to keep the parsed string in a shared_ptr anyways
428 : : // since we store pointers into the parsed string
429 : : std::shared_ptr<std::pair<std::string, ffi::FFIVec<ffi::Piece>>> data;
430 : : };
431 : :
432 : : } // namespace Fmt
433 : : } // namespace Rust
434 : :
435 : : #endif // !RUST_FMT_H
|