tree-optimization/113385 - wrong loop father with early exit vectorization
[official-gcc.git] / gcc / rust / parse / rust-parse.cc
blobf1e2caa258b0ca6869255aecc385a9e905dd6663
1 /* This file is part of GCC.
3 GCC is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 3, or (at your option)
6 any later version.
8 GCC is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with GCC; see the file COPYING3. If not see
15 <http://www.gnu.org/licenses/>. */
17 #include "rust-parse.h"
18 #include "rust-linemap.h"
19 #include "rust-diagnostics.h"
21 namespace Rust {
23 std::string
24 extract_module_path (const AST::AttrVec &inner_attrs,
25 const AST::AttrVec &outer_attrs, const std::string &name)
27 AST::Attribute path_attr = AST::Attribute::create_empty ();
28 for (const auto &attr : inner_attrs)
30 if (attr.get_path ().as_string () == "path")
32 path_attr = attr;
33 break;
37 // Here, we found a path attribute, but it has no associated string. This is
38 // invalid
39 if (!path_attr.is_empty () && !path_attr.has_attr_input ())
41 rust_error_at (
42 path_attr.get_locus (),
43 // Split the format string so that -Wformat-diag does not complain...
44 "path attributes must contain a filename: '%s'", "#![path = \"file\"]");
45 return name;
48 for (const auto &attr : outer_attrs)
50 if (attr.get_path ().as_string () == "path")
52 path_attr = attr;
53 break;
57 // We didn't find a path attribute. This is not an error, there simply isn't
58 // one present
59 if (path_attr.is_empty ())
60 return name;
62 // Here, we found a path attribute, but it has no associated string. This is
63 // invalid
64 if (!path_attr.has_attr_input ())
66 rust_error_at (
67 path_attr.get_locus (),
68 // Split the format string so that -Wformat-diag does not complain...
69 "path attributes must contain a filename: '%s'", "#[path = \"file\"]");
70 return name;
73 auto path_value = path_attr.get_attr_input ().as_string ();
75 // At this point, the 'path' is of the following format: '= "<file.rs>"'
76 // We need to remove the equal sign and only keep the actual filename.
77 // In order to do this, we can simply go through the string until we find
78 // a character that is not an equal sign or whitespace
79 auto filename_begin = path_value.find_first_not_of ("=\t ");
81 auto path = path_value.substr (filename_begin);
83 // On windows, the path might mix '/' and '\' separators. Replace the
84 // UNIX-like separators by MSDOS separators to make sure the path will resolve
85 // properly.
87 // Source: rustc compiler
88 // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
89 #if defined(HAVE_DOS_BASED_FILE_SYSTEM)
90 path.replace ('/', '\\');
91 #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
93 return path;
96 template <typename T>
97 static bool
98 contains (std::vector<T> &vec, T elm)
100 return std::find (vec.begin (), vec.end (), elm) != vec.end ();
104 * Avoid UB by calling .front() and .back() on empty containers...
107 template <typename T>
108 static const T *
109 get_back_ptr (const std::vector<std::unique_ptr<T>> &values)
111 if (values.empty ())
112 return nullptr;
114 return values.back ().get ();
117 template <typename T>
118 static const T *
119 get_front_ptr (const std::vector<std::unique_ptr<T>> &values)
121 if (values.empty ())
122 return nullptr;
124 return values.front ().get ();
127 static bool
128 peculiar_fragment_match_compatible_fragment (
129 const AST::MacroFragSpec &last_spec, const AST::MacroFragSpec &spec,
130 Location match_locus)
132 static std::unordered_map<AST::MacroFragSpec::Kind,
133 std::vector<AST::MacroFragSpec::Kind>>
134 fragment_follow_set
135 = {{AST::MacroFragSpec::PATH, {AST::MacroFragSpec::BLOCK}},
136 {AST::MacroFragSpec::TY, {AST::MacroFragSpec::BLOCK}},
137 {AST::MacroFragSpec::VIS,
138 {AST::MacroFragSpec::IDENT, AST::MacroFragSpec::TY,
139 AST::MacroFragSpec::PATH}}};
141 auto is_valid
142 = contains (fragment_follow_set[last_spec.get_kind ()], spec.get_kind ());
144 if (!is_valid)
145 rust_error_at (
146 match_locus,
147 "fragment specifier %<%s%> is not allowed after %<%s%> fragments",
148 spec.as_string ().c_str (), last_spec.as_string ().c_str ());
150 return is_valid;
153 static bool
154 peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
155 const AST::MacroMatch &match)
157 static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
158 follow_set
159 = {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
160 {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
161 {AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
162 {AST::MacroFragSpec::PATH,
163 {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
164 RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
165 {AST::MacroFragSpec::TY,
166 {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
167 RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
168 {AST::MacroFragSpec::VIS,
170 COMMA,
171 IDENTIFIER /* FIXME: Other than `priv` */,
172 LEFT_PAREN,
173 LEFT_SQUARE,
174 EXCLAM,
175 ASTERISK,
176 AMP,
177 LOGICAL_AND,
178 QUESTION_MARK,
179 LIFETIME,
180 LEFT_ANGLE,
181 LEFT_SHIFT,
182 SUPER,
183 SELF,
184 SELF_ALIAS,
185 EXTERN_TOK,
186 CRATE,
187 UNDERSCORE,
188 FOR,
189 IMPL,
190 FN_TOK,
191 UNSAFE,
192 TYPEOF,
194 // FIXME: Add Non kw identifiers
195 // FIXME: Add $crate as valid
196 }}};
198 Location error_locus = match.get_match_locus ();
199 std::string kind_str = "fragment";
200 auto &allowed_toks = follow_set[last_match.get_frag_spec ().get_kind ()];
202 // There are two behaviors to handle here: If the follow-up match is a token,
203 // we want to check if it is allowed.
204 // If it is a fragment, repetition or matcher then we know that it will be
205 // an error.
206 // For repetitions and matchers we want to extract a proper location to report
207 // the error.
208 switch (match.get_macro_match_type ())
210 case AST::MacroMatch::Tok: {
211 auto tok = static_cast<const AST::Token *> (&match);
212 if (contains (allowed_toks, tok->get_id ()))
213 return true;
214 kind_str = "token `"
215 + std::string (get_token_description (tok->get_id ())) + "`";
216 error_locus = tok->get_match_locus ();
217 break;
219 break;
220 case AST::MacroMatch::Repetition: {
221 auto repetition
222 = static_cast<const AST::MacroMatchRepetition *> (&match);
223 auto &matches = repetition->get_matches ();
224 auto first_frag = get_front_ptr (matches);
225 if (first_frag)
226 return peculiar_fragment_match_compatible (last_match, *first_frag);
227 break;
229 case AST::MacroMatch::Matcher: {
230 auto matcher = static_cast<const AST::MacroMatcher *> (&match);
231 auto first_token = matcher->get_delim_type ();
232 TokenId delim_id;
233 switch (first_token)
235 case AST::PARENS:
236 delim_id = LEFT_PAREN;
237 break;
238 case AST::SQUARE:
239 delim_id = LEFT_SQUARE;
240 break;
241 case AST::CURLY:
242 delim_id = LEFT_CURLY;
243 break;
244 default:
245 gcc_unreachable ();
246 break;
248 if (contains (allowed_toks, delim_id))
249 return true;
250 kind_str = "token `" + std::string (get_token_description (delim_id))
251 + "` at start of matcher";
252 error_locus = matcher->get_match_locus ();
253 break;
255 case AST::MacroMatch::Fragment: {
256 auto last_spec = last_match.get_frag_spec ();
257 auto fragment = static_cast<const AST::MacroMatchFragment *> (&match);
258 if (last_spec.has_follow_set_fragment_restrictions ())
259 return peculiar_fragment_match_compatible_fragment (
260 last_spec, fragment->get_frag_spec (), match.get_match_locus ());
262 break;
265 rust_error_at (error_locus, "%s is not allowed after %<%s%> fragment",
266 kind_str.c_str (),
267 last_match.get_frag_spec ().as_string ().c_str ());
268 auto allowed_toks_str
269 = "`" + std::string (get_token_description (allowed_toks[0])) + "`";
270 for (size_t i = 1; i < allowed_toks.size (); i++)
271 allowed_toks_str
272 += ", `" + std::string (get_token_description (allowed_toks[i])) + "`";
274 rust_inform (error_locus, "allowed tokens are %s", allowed_toks_str.c_str ());
276 return false;
279 bool
280 is_match_compatible (const AST::MacroMatch &last_match,
281 const AST::MacroMatch &match)
283 const AST::MacroMatch *new_last = nullptr;
285 // We want to "extract" the concerning matches. In cases such as matchers and
286 // repetitions, we actually store multiple matchers, but are only concerned
287 // about the follow-set ambiguities of certain elements.
288 // There are some cases where we can short-circuit the algorithm: There will
289 // never be restrictions on token literals, or on certain fragments which do
290 // not have a set of follow-restrictions.
292 switch (last_match.get_macro_match_type ())
294 // This is our main stop condition: When we are finally looking at the
295 // last match (or its actual last component), and it is a fragment, it
296 // may contain some follow up restrictions.
297 case AST::MacroMatch::Fragment: {
298 auto fragment
299 = static_cast<const AST::MacroMatchFragment *> (&last_match);
300 if (fragment->get_frag_spec ().has_follow_set_restrictions ())
301 return peculiar_fragment_match_compatible (*fragment, match);
302 else
303 return true;
305 case AST::MacroMatch::Repetition: {
306 // A repetition on the left hand side means we want to make sure the
307 // last match of the repetition is compatible with the new match
308 auto repetition
309 = static_cast<const AST::MacroMatchRepetition *> (&last_match);
310 new_last = get_back_ptr (repetition->get_matches ());
311 // If there are no matches in the matcher, then it can be followed by
312 // anything
313 if (!new_last)
314 return true;
315 break;
317 case AST::MacroMatch::Matcher:
318 case AST::MacroMatch::Tok:
319 return true;
322 rust_assert (new_last);
324 // We check recursively until we find a terminating condition
325 // FIXME: Does expansion depth/limit matter here?
326 return is_match_compatible (*new_last, match);
328 } // namespace Rust