Line data Source code
1 : // Copyright (C) 2023-2026 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 32 : FFIVec () : data ((T *) alignof (T)), len (0), cap (0) {}
50 :
51 : FFIVec (const FFIVec &) = delete;
52 : FFIVec &operator= (const FFIVec &) = delete;
53 :
54 64 : FFIVec (FFIVec &&other) : data (other.data), len (other.len), cap (other.cap)
55 : {
56 64 : other.data = (T *) alignof (T);
57 64 : other.len = 0;
58 64 : other.cap = 0;
59 : }
60 :
61 32 : FFIVec &operator= (FFIVec &&other)
62 : {
63 32 : this->~FFIVec ();
64 32 : new (this) FFIVec (std::move (other));
65 32 : return *this;
66 : }
67 :
68 128 : ~FFIVec ()
69 : {
70 : // T can't be zero-sized
71 128 : if (cap)
72 32 : rust_ffi_dealloc ((unsigned char *) data, cap, sizeof (T), alignof (T));
73 128 : }
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 3 : const T *begin () const { return data; }
85 : T *end () { return data + len; }
86 3 : 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 49 : ~FFIOpt ()
119 : {
120 49 : 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 49 : 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 32 : explicit RustHamster (const std::string &str)
189 32 : : 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 45 : 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 64 : 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 61 : struct Pieces
413 : {
414 : static Pieces collect (const std::string &to_parse, bool append_newline,
415 : ffi::ParseMode parse_mode);
416 :
417 32 : const ffi::FFIVec<ffi::Piece> &get_pieces () const { return data->second; }
418 :
419 : private:
420 32 : Pieces (std::string str, ffi::FFIVec<ffi::Piece> pieces)
421 32 : : 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
|