/src/llvm-project/clang/lib/Parse/ParseOpenACC.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- ParseOpenACC.cpp - OpenACC-specific parsing support --------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file implements the parsing logic for OpenACC language features. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/Basic/OpenACCKinds.h" |
14 | | #include "clang/Parse/ParseDiagnostic.h" |
15 | | #include "clang/Parse/Parser.h" |
16 | | #include "clang/Parse/RAIIObjectsForParser.h" |
17 | | #include "llvm/ADT/StringRef.h" |
18 | | #include "llvm/ADT/StringSwitch.h" |
19 | | |
20 | | using namespace clang; |
21 | | using namespace llvm; |
22 | | |
23 | | namespace { |
24 | | // An enum that contains the extended 'partial' parsed variants. This type |
25 | | // should never escape the initial parse functionality, but is useful for |
26 | | // simplifying the implementation. |
27 | | enum class OpenACCDirectiveKindEx { |
28 | | Invalid = static_cast<int>(OpenACCDirectiveKind::Invalid), |
29 | | // 'enter data' and 'exit data' |
30 | | Enter, |
31 | | Exit, |
32 | | }; |
33 | | |
34 | | // Translate single-token string representations to the OpenACC Directive Kind. |
35 | | // This doesn't completely comprehend 'Compound Constructs' (as it just |
36 | | // identifies the first token), and doesn't fully handle 'enter data', 'exit |
37 | | // data', nor any of the 'atomic' variants, just the first token of each. So |
38 | | // this should only be used by `ParseOpenACCDirectiveKind`. |
39 | 0 | OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) { |
40 | 0 | if (!Tok.is(tok::identifier)) |
41 | 0 | return OpenACCDirectiveKindEx::Invalid; |
42 | 0 | OpenACCDirectiveKind DirKind = |
43 | 0 | llvm::StringSwitch<OpenACCDirectiveKind>( |
44 | 0 | Tok.getIdentifierInfo()->getName()) |
45 | 0 | .Case("parallel", OpenACCDirectiveKind::Parallel) |
46 | 0 | .Case("serial", OpenACCDirectiveKind::Serial) |
47 | 0 | .Case("kernels", OpenACCDirectiveKind::Kernels) |
48 | 0 | .Case("data", OpenACCDirectiveKind::Data) |
49 | 0 | .Case("host_data", OpenACCDirectiveKind::HostData) |
50 | 0 | .Case("loop", OpenACCDirectiveKind::Loop) |
51 | 0 | .Case("cache", OpenACCDirectiveKind::Cache) |
52 | 0 | .Case("atomic", OpenACCDirectiveKind::Atomic) |
53 | 0 | .Case("routine", OpenACCDirectiveKind::Routine) |
54 | 0 | .Case("declare", OpenACCDirectiveKind::Declare) |
55 | 0 | .Case("init", OpenACCDirectiveKind::Init) |
56 | 0 | .Case("shutdown", OpenACCDirectiveKind::Shutdown) |
57 | 0 | .Case("set", OpenACCDirectiveKind::Shutdown) |
58 | 0 | .Case("update", OpenACCDirectiveKind::Update) |
59 | 0 | .Case("wait", OpenACCDirectiveKind::Wait) |
60 | 0 | .Default(OpenACCDirectiveKind::Invalid); |
61 | |
|
62 | 0 | if (DirKind != OpenACCDirectiveKind::Invalid) |
63 | 0 | return static_cast<OpenACCDirectiveKindEx>(DirKind); |
64 | | |
65 | 0 | return llvm::StringSwitch<OpenACCDirectiveKindEx>( |
66 | 0 | Tok.getIdentifierInfo()->getName()) |
67 | 0 | .Case("enter", OpenACCDirectiveKindEx::Enter) |
68 | 0 | .Case("exit", OpenACCDirectiveKindEx::Exit) |
69 | 0 | .Default(OpenACCDirectiveKindEx::Invalid); |
70 | 0 | } |
71 | | |
72 | | // Translate single-token string representations to the OpenCC Clause Kind. |
73 | 0 | OpenACCClauseKind getOpenACCClauseKind(Token Tok) { |
74 | | // auto is a keyword in some language modes, so make sure we parse it |
75 | | // correctly. |
76 | 0 | if (Tok.is(tok::kw_auto)) |
77 | 0 | return OpenACCClauseKind::Auto; |
78 | | |
79 | | // default is a keyword, so make sure we parse it correctly. |
80 | 0 | if (Tok.is(tok::kw_default)) |
81 | 0 | return OpenACCClauseKind::Default; |
82 | | |
83 | | // if is also a keyword, make sure we parse it correctly. |
84 | 0 | if (Tok.is(tok::kw_if)) |
85 | 0 | return OpenACCClauseKind::If; |
86 | | |
87 | 0 | if (!Tok.is(tok::identifier)) |
88 | 0 | return OpenACCClauseKind::Invalid; |
89 | | |
90 | 0 | return llvm::StringSwitch<OpenACCClauseKind>( |
91 | 0 | Tok.getIdentifierInfo()->getName()) |
92 | 0 | .Case("attach", OpenACCClauseKind::Attach) |
93 | 0 | .Case("auto", OpenACCClauseKind::Auto) |
94 | 0 | .Case("create", OpenACCClauseKind::Create) |
95 | 0 | .Case("copy", OpenACCClauseKind::Copy) |
96 | 0 | .Case("copyin", OpenACCClauseKind::CopyIn) |
97 | 0 | .Case("copyout", OpenACCClauseKind::CopyOut) |
98 | 0 | .Case("default", OpenACCClauseKind::Default) |
99 | 0 | .Case("delete", OpenACCClauseKind::Delete) |
100 | 0 | .Case("detach", OpenACCClauseKind::Detach) |
101 | 0 | .Case("device", OpenACCClauseKind::Device) |
102 | 0 | .Case("device_resident", OpenACCClauseKind::DeviceResident) |
103 | 0 | .Case("deviceptr", OpenACCClauseKind::DevicePtr) |
104 | 0 | .Case("finalize", OpenACCClauseKind::Finalize) |
105 | 0 | .Case("firstprivate", OpenACCClauseKind::FirstPrivate) |
106 | 0 | .Case("host", OpenACCClauseKind::Host) |
107 | 0 | .Case("if", OpenACCClauseKind::If) |
108 | 0 | .Case("if_present", OpenACCClauseKind::IfPresent) |
109 | 0 | .Case("independent", OpenACCClauseKind::Independent) |
110 | 0 | .Case("link", OpenACCClauseKind::Link) |
111 | 0 | .Case("no_create", OpenACCClauseKind::NoCreate) |
112 | 0 | .Case("nohost", OpenACCClauseKind::NoHost) |
113 | 0 | .Case("present", OpenACCClauseKind::Present) |
114 | 0 | .Case("private", OpenACCClauseKind::Private) |
115 | 0 | .Case("reduction", OpenACCClauseKind::Reduction) |
116 | 0 | .Case("self", OpenACCClauseKind::Self) |
117 | 0 | .Case("seq", OpenACCClauseKind::Seq) |
118 | 0 | .Case("use_device", OpenACCClauseKind::UseDevice) |
119 | 0 | .Case("vector", OpenACCClauseKind::Vector) |
120 | 0 | .Case("worker", OpenACCClauseKind::Worker) |
121 | 0 | .Default(OpenACCClauseKind::Invalid); |
122 | 0 | } |
123 | | |
124 | | // Since 'atomic' is effectively a compound directive, this will decode the |
125 | | // second part of the directive. |
126 | 0 | OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) { |
127 | 0 | if (!Tok.is(tok::identifier)) |
128 | 0 | return OpenACCAtomicKind::Invalid; |
129 | 0 | return llvm::StringSwitch<OpenACCAtomicKind>( |
130 | 0 | Tok.getIdentifierInfo()->getName()) |
131 | 0 | .Case("read", OpenACCAtomicKind::Read) |
132 | 0 | .Case("write", OpenACCAtomicKind::Write) |
133 | 0 | .Case("update", OpenACCAtomicKind::Update) |
134 | 0 | .Case("capture", OpenACCAtomicKind::Capture) |
135 | 0 | .Default(OpenACCAtomicKind::Invalid); |
136 | 0 | } |
137 | | |
138 | 0 | OpenACCDefaultClauseKind getOpenACCDefaultClauseKind(Token Tok) { |
139 | 0 | if (!Tok.is(tok::identifier)) |
140 | 0 | return OpenACCDefaultClauseKind::Invalid; |
141 | | |
142 | 0 | return llvm::StringSwitch<OpenACCDefaultClauseKind>( |
143 | 0 | Tok.getIdentifierInfo()->getName()) |
144 | 0 | .Case("none", OpenACCDefaultClauseKind::None) |
145 | 0 | .Case("present", OpenACCDefaultClauseKind::Present) |
146 | 0 | .Default(OpenACCDefaultClauseKind::Invalid); |
147 | 0 | } |
148 | | |
149 | | enum class OpenACCSpecialTokenKind { |
150 | | ReadOnly, |
151 | | DevNum, |
152 | | Queues, |
153 | | Zero, |
154 | | }; |
155 | | |
156 | 0 | bool isOpenACCSpecialToken(OpenACCSpecialTokenKind Kind, Token Tok) { |
157 | 0 | if (!Tok.is(tok::identifier)) |
158 | 0 | return false; |
159 | | |
160 | 0 | switch (Kind) { |
161 | 0 | case OpenACCSpecialTokenKind::ReadOnly: |
162 | 0 | return Tok.getIdentifierInfo()->isStr("readonly"); |
163 | 0 | case OpenACCSpecialTokenKind::DevNum: |
164 | 0 | return Tok.getIdentifierInfo()->isStr("devnum"); |
165 | 0 | case OpenACCSpecialTokenKind::Queues: |
166 | 0 | return Tok.getIdentifierInfo()->isStr("queues"); |
167 | 0 | case OpenACCSpecialTokenKind::Zero: |
168 | 0 | return Tok.getIdentifierInfo()->isStr("zero"); |
169 | 0 | } |
170 | 0 | llvm_unreachable("Unknown 'Kind' Passed"); |
171 | 0 | } |
172 | | |
173 | | /// Used for cases where we have a token we want to check against an |
174 | | /// 'identifier-like' token, but don't want to give awkward error messages in |
175 | | /// cases where it is accidentially a keyword. |
176 | 0 | bool isTokenIdentifierOrKeyword(Parser &P, Token Tok) { |
177 | 0 | if (Tok.is(tok::identifier)) |
178 | 0 | return true; |
179 | | |
180 | 0 | if (!Tok.isAnnotation() && Tok.getIdentifierInfo() && |
181 | 0 | Tok.getIdentifierInfo()->isKeyword(P.getLangOpts())) |
182 | 0 | return true; |
183 | | |
184 | 0 | return false; |
185 | 0 | } |
186 | | |
187 | | /// Parses and consumes an identifer followed immediately by a single colon, and |
188 | | /// diagnoses if it is not the 'special token' kind that we require. Used when |
189 | | /// the tag is the only valid value. |
190 | | /// Return 'true' if the special token was matched, false if no special token, |
191 | | /// or an invalid special token was found. |
192 | | template <typename DirOrClauseTy> |
193 | | bool tryParseAndConsumeSpecialTokenKind(Parser &P, OpenACCSpecialTokenKind Kind, |
194 | 0 | DirOrClauseTy DirOrClause) { |
195 | 0 | Token IdentTok = P.getCurToken(); |
196 | | // If this is an identifier-like thing followed by ':', it is one of the |
197 | | // OpenACC 'special' name tags, so consume it. |
198 | 0 | if (isTokenIdentifierOrKeyword(P, IdentTok) && P.NextToken().is(tok::colon)) { |
199 | 0 | P.ConsumeToken(); |
200 | 0 | P.ConsumeToken(); |
201 | |
|
202 | 0 | if (!isOpenACCSpecialToken(Kind, IdentTok)) { |
203 | 0 | P.Diag(IdentTok, diag::err_acc_invalid_tag_kind) |
204 | 0 | << IdentTok.getIdentifierInfo() << DirOrClause |
205 | 0 | << std::is_same_v<DirOrClauseTy, OpenACCClauseKind>; |
206 | 0 | return false; |
207 | 0 | } |
208 | | |
209 | 0 | return true; |
210 | 0 | } |
211 | | |
212 | 0 | return false; |
213 | 0 | } Unexecuted instantiation: ParseOpenACC.cpp:bool (anonymous namespace)::tryParseAndConsumeSpecialTokenKind<clang::OpenACCClauseKind>(clang::Parser&, (anonymous namespace)::OpenACCSpecialTokenKind, clang::OpenACCClauseKind) Unexecuted instantiation: ParseOpenACC.cpp:bool (anonymous namespace)::tryParseAndConsumeSpecialTokenKind<clang::OpenACCDirectiveKind>(clang::Parser&, (anonymous namespace)::OpenACCSpecialTokenKind, clang::OpenACCDirectiveKind) |
214 | | |
215 | 0 | bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) { |
216 | 0 | if (!Tok.is(tok::identifier)) |
217 | 0 | return false; |
218 | | |
219 | 0 | switch (Kind) { |
220 | 0 | case OpenACCDirectiveKind::Parallel: |
221 | 0 | return Tok.getIdentifierInfo()->isStr("parallel"); |
222 | 0 | case OpenACCDirectiveKind::Serial: |
223 | 0 | return Tok.getIdentifierInfo()->isStr("serial"); |
224 | 0 | case OpenACCDirectiveKind::Kernels: |
225 | 0 | return Tok.getIdentifierInfo()->isStr("kernels"); |
226 | 0 | case OpenACCDirectiveKind::Data: |
227 | 0 | return Tok.getIdentifierInfo()->isStr("data"); |
228 | 0 | case OpenACCDirectiveKind::HostData: |
229 | 0 | return Tok.getIdentifierInfo()->isStr("host_data"); |
230 | 0 | case OpenACCDirectiveKind::Loop: |
231 | 0 | return Tok.getIdentifierInfo()->isStr("loop"); |
232 | 0 | case OpenACCDirectiveKind::Cache: |
233 | 0 | return Tok.getIdentifierInfo()->isStr("cache"); |
234 | | |
235 | 0 | case OpenACCDirectiveKind::ParallelLoop: |
236 | 0 | case OpenACCDirectiveKind::SerialLoop: |
237 | 0 | case OpenACCDirectiveKind::KernelsLoop: |
238 | 0 | case OpenACCDirectiveKind::EnterData: |
239 | 0 | case OpenACCDirectiveKind::ExitData: |
240 | 0 | return false; |
241 | | |
242 | 0 | case OpenACCDirectiveKind::Atomic: |
243 | 0 | return Tok.getIdentifierInfo()->isStr("atomic"); |
244 | 0 | case OpenACCDirectiveKind::Routine: |
245 | 0 | return Tok.getIdentifierInfo()->isStr("routine"); |
246 | 0 | case OpenACCDirectiveKind::Declare: |
247 | 0 | return Tok.getIdentifierInfo()->isStr("declare"); |
248 | 0 | case OpenACCDirectiveKind::Init: |
249 | 0 | return Tok.getIdentifierInfo()->isStr("init"); |
250 | 0 | case OpenACCDirectiveKind::Shutdown: |
251 | 0 | return Tok.getIdentifierInfo()->isStr("shutdown"); |
252 | 0 | case OpenACCDirectiveKind::Set: |
253 | 0 | return Tok.getIdentifierInfo()->isStr("set"); |
254 | 0 | case OpenACCDirectiveKind::Update: |
255 | 0 | return Tok.getIdentifierInfo()->isStr("update"); |
256 | 0 | case OpenACCDirectiveKind::Wait: |
257 | 0 | return Tok.getIdentifierInfo()->isStr("wait"); |
258 | 0 | case OpenACCDirectiveKind::Invalid: |
259 | 0 | return false; |
260 | 0 | } |
261 | 0 | llvm_unreachable("Unknown 'Kind' Passed"); |
262 | 0 | } |
263 | | |
264 | 0 | OpenACCReductionOperator ParseReductionOperator(Parser &P) { |
265 | | // If there is no colon, treat as if the reduction operator was missing, else |
266 | | // we probably will not recover from it in the case where an expression starts |
267 | | // with one of the operator tokens. |
268 | 0 | if (P.NextToken().isNot(tok::colon)) { |
269 | 0 | P.Diag(P.getCurToken(), diag::err_acc_expected_reduction_operator); |
270 | 0 | return OpenACCReductionOperator::Invalid; |
271 | 0 | } |
272 | 0 | Token ReductionKindTok = P.getCurToken(); |
273 | | // Consume both the kind and the colon. |
274 | 0 | P.ConsumeToken(); |
275 | 0 | P.ConsumeToken(); |
276 | |
|
277 | 0 | switch (ReductionKindTok.getKind()) { |
278 | 0 | case tok::plus: |
279 | 0 | return OpenACCReductionOperator::Addition; |
280 | 0 | case tok::star: |
281 | 0 | return OpenACCReductionOperator::Multiplication; |
282 | 0 | case tok::amp: |
283 | 0 | return OpenACCReductionOperator::BitwiseAnd; |
284 | 0 | case tok::pipe: |
285 | 0 | return OpenACCReductionOperator::BitwiseOr; |
286 | 0 | case tok::caret: |
287 | 0 | return OpenACCReductionOperator::BitwiseXOr; |
288 | 0 | case tok::ampamp: |
289 | 0 | return OpenACCReductionOperator::And; |
290 | 0 | case tok::pipepipe: |
291 | 0 | return OpenACCReductionOperator::Or; |
292 | 0 | case tok::identifier: |
293 | 0 | if (ReductionKindTok.getIdentifierInfo()->isStr("max")) |
294 | 0 | return OpenACCReductionOperator::Max; |
295 | 0 | if (ReductionKindTok.getIdentifierInfo()->isStr("min")) |
296 | 0 | return OpenACCReductionOperator::Min; |
297 | 0 | LLVM_FALLTHROUGH; |
298 | 0 | default: |
299 | 0 | P.Diag(ReductionKindTok, diag::err_acc_invalid_reduction_operator); |
300 | 0 | return OpenACCReductionOperator::Invalid; |
301 | 0 | } |
302 | 0 | llvm_unreachable("Reduction op token kind not caught by 'default'?"); |
303 | 0 | } |
304 | | |
305 | | /// Used for cases where we expect an identifier-like token, but don't want to |
306 | | /// give awkward error messages in cases where it is accidentially a keyword. |
307 | 0 | bool expectIdentifierOrKeyword(Parser &P) { |
308 | 0 | Token Tok = P.getCurToken(); |
309 | |
|
310 | 0 | if (isTokenIdentifierOrKeyword(P, Tok)) |
311 | 0 | return false; |
312 | | |
313 | 0 | P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier; |
314 | 0 | return true; |
315 | 0 | } |
316 | | |
317 | | OpenACCDirectiveKind |
318 | | ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok, |
319 | 0 | OpenACCDirectiveKindEx ExtDirKind) { |
320 | 0 | Token SecondTok = P.getCurToken(); |
321 | |
|
322 | 0 | if (SecondTok.isAnnotation()) { |
323 | 0 | P.Diag(FirstTok, diag::err_acc_invalid_directive) |
324 | 0 | << 0 << FirstTok.getIdentifierInfo(); |
325 | 0 | return OpenACCDirectiveKind::Invalid; |
326 | 0 | } |
327 | | |
328 | | // Consume the second name anyway, this way we can continue on without making |
329 | | // this oddly look like a clause. |
330 | 0 | P.ConsumeAnyToken(); |
331 | |
|
332 | 0 | if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTok)) { |
333 | 0 | if (!SecondTok.is(tok::identifier)) |
334 | 0 | P.Diag(SecondTok, diag::err_expected) << tok::identifier; |
335 | 0 | else |
336 | 0 | P.Diag(FirstTok, diag::err_acc_invalid_directive) |
337 | 0 | << 1 << FirstTok.getIdentifierInfo()->getName() |
338 | 0 | << SecondTok.getIdentifierInfo()->getName(); |
339 | 0 | return OpenACCDirectiveKind::Invalid; |
340 | 0 | } |
341 | | |
342 | 0 | return ExtDirKind == OpenACCDirectiveKindEx::Enter |
343 | 0 | ? OpenACCDirectiveKind::EnterData |
344 | 0 | : OpenACCDirectiveKind::ExitData; |
345 | 0 | } |
346 | | |
347 | 0 | OpenACCAtomicKind ParseOpenACCAtomicKind(Parser &P) { |
348 | 0 | Token AtomicClauseToken = P.getCurToken(); |
349 | | |
350 | | // #pragma acc atomic is equivilent to update: |
351 | 0 | if (AtomicClauseToken.isAnnotation()) |
352 | 0 | return OpenACCAtomicKind::Update; |
353 | | |
354 | 0 | OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseToken); |
355 | | |
356 | | // If we don't know what this is, treat it as 'nothing', and treat the rest of |
357 | | // this as a clause list, which, despite being invalid, is likely what the |
358 | | // user was trying to do. |
359 | 0 | if (AtomicKind == OpenACCAtomicKind::Invalid) |
360 | 0 | return OpenACCAtomicKind::Update; |
361 | | |
362 | 0 | P.ConsumeToken(); |
363 | 0 | return AtomicKind; |
364 | 0 | } |
365 | | |
366 | | // Parse and consume the tokens for OpenACC Directive/Construct kinds. |
367 | 0 | OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) { |
368 | 0 | Token FirstTok = P.getCurToken(); |
369 | | |
370 | | // Just #pragma acc can get us immediately to the end, make sure we don't |
371 | | // introspect on the spelling before then. |
372 | 0 | if (FirstTok.isNot(tok::identifier)) { |
373 | 0 | P.Diag(FirstTok, diag::err_acc_missing_directive); |
374 | |
|
375 | 0 | if (P.getCurToken().isNot(tok::annot_pragma_openacc_end)) |
376 | 0 | P.ConsumeAnyToken(); |
377 | |
|
378 | 0 | return OpenACCDirectiveKind::Invalid; |
379 | 0 | } |
380 | | |
381 | 0 | P.ConsumeToken(); |
382 | |
|
383 | 0 | OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTok); |
384 | | |
385 | | // OpenACCDirectiveKindEx is meant to be an extended list |
386 | | // over OpenACCDirectiveKind, so any value below Invalid is one of the |
387 | | // OpenACCDirectiveKind values. This switch takes care of all of the extra |
388 | | // parsing required for the Extended values. At the end of this block, |
389 | | // ExDirKind can be assumed to be a valid OpenACCDirectiveKind, so we can |
390 | | // immediately cast it and use it as that. |
391 | 0 | if (ExDirKind >= OpenACCDirectiveKindEx::Invalid) { |
392 | 0 | switch (ExDirKind) { |
393 | 0 | case OpenACCDirectiveKindEx::Invalid: { |
394 | 0 | P.Diag(FirstTok, diag::err_acc_invalid_directive) |
395 | 0 | << 0 << FirstTok.getIdentifierInfo(); |
396 | 0 | return OpenACCDirectiveKind::Invalid; |
397 | 0 | } |
398 | 0 | case OpenACCDirectiveKindEx::Enter: |
399 | 0 | case OpenACCDirectiveKindEx::Exit: |
400 | 0 | return ParseOpenACCEnterExitDataDirective(P, FirstTok, ExDirKind); |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | 0 | OpenACCDirectiveKind DirKind = static_cast<OpenACCDirectiveKind>(ExDirKind); |
405 | | |
406 | | // Combined Constructs allows parallel loop, serial loop, or kernels loop. Any |
407 | | // other attempt at a combined construct will be diagnosed as an invalid |
408 | | // clause. |
409 | 0 | Token SecondTok = P.getCurToken(); |
410 | 0 | if (!SecondTok.isAnnotation() && |
411 | 0 | isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop, SecondTok)) { |
412 | 0 | switch (DirKind) { |
413 | 0 | default: |
414 | | // Nothing to do except in the below cases, as they should be diagnosed as |
415 | | // a clause. |
416 | 0 | break; |
417 | 0 | case OpenACCDirectiveKind::Parallel: |
418 | 0 | P.ConsumeToken(); |
419 | 0 | return OpenACCDirectiveKind::ParallelLoop; |
420 | 0 | case OpenACCDirectiveKind::Serial: |
421 | 0 | P.ConsumeToken(); |
422 | 0 | return OpenACCDirectiveKind::SerialLoop; |
423 | 0 | case OpenACCDirectiveKind::Kernels: |
424 | 0 | P.ConsumeToken(); |
425 | 0 | return OpenACCDirectiveKind::KernelsLoop; |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | 0 | return DirKind; |
430 | 0 | } |
431 | | |
432 | | enum ClauseParensKind { |
433 | | None, |
434 | | Optional, |
435 | | Required |
436 | | }; |
437 | | |
438 | | ClauseParensKind getClauseParensKind(OpenACCDirectiveKind DirKind, |
439 | 0 | OpenACCClauseKind Kind) { |
440 | 0 | switch (Kind) { |
441 | 0 | case OpenACCClauseKind::Self: |
442 | 0 | return DirKind == OpenACCDirectiveKind::Update ? ClauseParensKind::Required |
443 | 0 | : ClauseParensKind::Optional; |
444 | | |
445 | 0 | case OpenACCClauseKind::Default: |
446 | 0 | case OpenACCClauseKind::If: |
447 | 0 | case OpenACCClauseKind::Create: |
448 | 0 | case OpenACCClauseKind::Copy: |
449 | 0 | case OpenACCClauseKind::CopyIn: |
450 | 0 | case OpenACCClauseKind::CopyOut: |
451 | 0 | case OpenACCClauseKind::UseDevice: |
452 | 0 | case OpenACCClauseKind::NoCreate: |
453 | 0 | case OpenACCClauseKind::Present: |
454 | 0 | case OpenACCClauseKind::DevicePtr: |
455 | 0 | case OpenACCClauseKind::Attach: |
456 | 0 | case OpenACCClauseKind::Detach: |
457 | 0 | case OpenACCClauseKind::Private: |
458 | 0 | case OpenACCClauseKind::FirstPrivate: |
459 | 0 | case OpenACCClauseKind::Delete: |
460 | 0 | case OpenACCClauseKind::DeviceResident: |
461 | 0 | case OpenACCClauseKind::Device: |
462 | 0 | case OpenACCClauseKind::Link: |
463 | 0 | case OpenACCClauseKind::Host: |
464 | 0 | case OpenACCClauseKind::Reduction: |
465 | 0 | return ClauseParensKind::Required; |
466 | | |
467 | 0 | case OpenACCClauseKind::Auto: |
468 | 0 | case OpenACCClauseKind::Finalize: |
469 | 0 | case OpenACCClauseKind::IfPresent: |
470 | 0 | case OpenACCClauseKind::Independent: |
471 | 0 | case OpenACCClauseKind::Invalid: |
472 | 0 | case OpenACCClauseKind::NoHost: |
473 | 0 | case OpenACCClauseKind::Seq: |
474 | 0 | case OpenACCClauseKind::Worker: |
475 | 0 | case OpenACCClauseKind::Vector: |
476 | 0 | return ClauseParensKind::None; |
477 | 0 | } |
478 | 0 | llvm_unreachable("Unhandled clause kind"); |
479 | 0 | } |
480 | | |
481 | | bool ClauseHasOptionalParens(OpenACCDirectiveKind DirKind, |
482 | 0 | OpenACCClauseKind Kind) { |
483 | 0 | return getClauseParensKind(DirKind, Kind) == ClauseParensKind::Optional; |
484 | 0 | } |
485 | | |
486 | | bool ClauseHasRequiredParens(OpenACCDirectiveKind DirKind, |
487 | 0 | OpenACCClauseKind Kind) { |
488 | 0 | return getClauseParensKind(DirKind, Kind) == ClauseParensKind::Required; |
489 | 0 | } |
490 | | |
491 | 0 | ExprResult ParseOpenACCConditionalExpr(Parser &P) { |
492 | | // FIXME: It isn't clear if the spec saying 'condition' means the same as |
493 | | // it does in an if/while/etc (See ParseCXXCondition), however as it was |
494 | | // written with Fortran/C in mind, we're going to assume it just means an |
495 | | // 'expression evaluating to boolean'. |
496 | 0 | return P.getActions().CorrectDelayedTyposInExpr(P.ParseExpression()); |
497 | 0 | } |
498 | | |
499 | | // Skip until we see the end of pragma token, but don't consume it. This is us |
500 | | // just giving up on the rest of the pragma so we can continue executing. We |
501 | | // have to do this because 'SkipUntil' considers paren balancing, which isn't |
502 | | // what we want. |
503 | 0 | void SkipUntilEndOfDirective(Parser &P) { |
504 | 0 | while (P.getCurToken().isNot(tok::annot_pragma_openacc_end)) |
505 | 0 | P.ConsumeAnyToken(); |
506 | 0 | } |
507 | | |
508 | | } // namespace |
509 | | |
510 | | // OpenACC 3.3, section 1.7: |
511 | | // To simplify the specification and convey appropriate constraint information, |
512 | | // a pqr-list is a comma-separated list of pdr items. The one exception is a |
513 | | // clause-list, which is a list of one or more clauses optionally separated by |
514 | | // commas. |
515 | 0 | void Parser::ParseOpenACCClauseList(OpenACCDirectiveKind DirKind) { |
516 | 0 | bool FirstClause = true; |
517 | 0 | while (getCurToken().isNot(tok::annot_pragma_openacc_end)) { |
518 | | // Comma is optional in a clause-list. |
519 | 0 | if (!FirstClause && getCurToken().is(tok::comma)) |
520 | 0 | ConsumeToken(); |
521 | 0 | FirstClause = false; |
522 | | |
523 | | // Recovering from a bad clause is really difficult, so we just give up on |
524 | | // error. |
525 | 0 | if (ParseOpenACCClause(DirKind)) { |
526 | 0 | SkipUntilEndOfDirective(*this); |
527 | 0 | return; |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | 0 | bool Parser::ParseOpenACCClauseVarList(OpenACCClauseKind Kind) { |
533 | | // FIXME: Future clauses will require 'special word' parsing, check for one, |
534 | | // then parse it based on whether it is a clause that requires a 'special |
535 | | // word'. |
536 | 0 | (void)Kind; |
537 | | |
538 | | // If the var parsing fails, skip until the end of the directive as this is |
539 | | // an expression and gets messy if we try to continue otherwise. |
540 | 0 | if (ParseOpenACCVar()) |
541 | 0 | return true; |
542 | | |
543 | 0 | while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { |
544 | 0 | ExpectAndConsume(tok::comma); |
545 | | |
546 | | // If the var parsing fails, skip until the end of the directive as this is |
547 | | // an expression and gets messy if we try to continue otherwise. |
548 | 0 | if (ParseOpenACCVar()) |
549 | 0 | return true; |
550 | 0 | } |
551 | 0 | return false; |
552 | 0 | } |
553 | | // The OpenACC Clause List is a comma or space-delimited list of clauses (see |
554 | | // the comment on ParseOpenACCClauseList). The concept of a 'clause' doesn't |
555 | | // really have its owner grammar and each individual one has its own definition. |
556 | | // However, they all are named with a single-identifier (or auto/default!) |
557 | | // token, followed in some cases by either braces or parens. |
558 | 0 | bool Parser::ParseOpenACCClause(OpenACCDirectiveKind DirKind) { |
559 | | // A number of clause names are actually keywords, so accept a keyword that |
560 | | // can be converted to a name. |
561 | 0 | if (expectIdentifierOrKeyword(*this)) |
562 | 0 | return true; |
563 | | |
564 | 0 | OpenACCClauseKind Kind = getOpenACCClauseKind(getCurToken()); |
565 | |
|
566 | 0 | if (Kind == OpenACCClauseKind::Invalid) |
567 | 0 | return Diag(getCurToken(), diag::err_acc_invalid_clause) |
568 | 0 | << getCurToken().getIdentifierInfo(); |
569 | | |
570 | | // Consume the clause name. |
571 | 0 | ConsumeToken(); |
572 | |
|
573 | 0 | return ParseOpenACCClauseParams(DirKind, Kind); |
574 | 0 | } |
575 | | |
576 | | bool Parser::ParseOpenACCClauseParams(OpenACCDirectiveKind DirKind, |
577 | 0 | OpenACCClauseKind Kind) { |
578 | 0 | BalancedDelimiterTracker Parens(*this, tok::l_paren, |
579 | 0 | tok::annot_pragma_openacc_end); |
580 | |
|
581 | 0 | if (ClauseHasRequiredParens(DirKind, Kind)) { |
582 | 0 | if (Parens.expectAndConsume()) { |
583 | | // We are missing a paren, so assume that the person just forgot the |
584 | | // parameter. Return 'false' so we try to continue on and parse the next |
585 | | // clause. |
586 | 0 | SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end, |
587 | 0 | Parser::StopBeforeMatch); |
588 | 0 | return false; |
589 | 0 | } |
590 | | |
591 | 0 | switch (Kind) { |
592 | 0 | case OpenACCClauseKind::Default: { |
593 | 0 | Token DefKindTok = getCurToken(); |
594 | |
|
595 | 0 | if (expectIdentifierOrKeyword(*this)) |
596 | 0 | break; |
597 | | |
598 | 0 | ConsumeToken(); |
599 | |
|
600 | 0 | if (getOpenACCDefaultClauseKind(DefKindTok) == |
601 | 0 | OpenACCDefaultClauseKind::Invalid) |
602 | 0 | Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind); |
603 | |
|
604 | 0 | break; |
605 | 0 | } |
606 | 0 | case OpenACCClauseKind::If: { |
607 | 0 | ExprResult CondExpr = ParseOpenACCConditionalExpr(*this); |
608 | | // An invalid expression can be just about anything, so just give up on |
609 | | // this clause list. |
610 | 0 | if (CondExpr.isInvalid()) |
611 | 0 | return true; |
612 | 0 | break; |
613 | 0 | } |
614 | 0 | case OpenACCClauseKind::CopyIn: |
615 | 0 | tryParseAndConsumeSpecialTokenKind( |
616 | 0 | *this, OpenACCSpecialTokenKind::ReadOnly, Kind); |
617 | 0 | if (ParseOpenACCClauseVarList(Kind)) |
618 | 0 | return true; |
619 | 0 | break; |
620 | 0 | case OpenACCClauseKind::Create: |
621 | 0 | case OpenACCClauseKind::CopyOut: |
622 | 0 | tryParseAndConsumeSpecialTokenKind(*this, OpenACCSpecialTokenKind::Zero, |
623 | 0 | Kind); |
624 | 0 | if (ParseOpenACCClauseVarList(Kind)) |
625 | 0 | return true; |
626 | 0 | break; |
627 | 0 | case OpenACCClauseKind::Reduction: |
628 | | // If we're missing a clause-kind (or it is invalid), see if we can parse |
629 | | // the var-list anyway. |
630 | 0 | ParseReductionOperator(*this); |
631 | 0 | if (ParseOpenACCClauseVarList(Kind)) |
632 | 0 | return true; |
633 | 0 | break; |
634 | 0 | case OpenACCClauseKind::Self: |
635 | | // The 'self' clause is a var-list instead of a 'condition' in the case of |
636 | | // the 'update' clause, so we have to handle it here. U se an assert to |
637 | | // make sure we get the right differentiator. |
638 | 0 | assert(DirKind == OpenACCDirectiveKind::Update); |
639 | 0 | LLVM_FALLTHROUGH; |
640 | 0 | case OpenACCClauseKind::Attach: |
641 | 0 | case OpenACCClauseKind::Copy: |
642 | 0 | case OpenACCClauseKind::Delete: |
643 | 0 | case OpenACCClauseKind::Detach: |
644 | 0 | case OpenACCClauseKind::Device: |
645 | 0 | case OpenACCClauseKind::DeviceResident: |
646 | 0 | case OpenACCClauseKind::DevicePtr: |
647 | 0 | case OpenACCClauseKind::FirstPrivate: |
648 | 0 | case OpenACCClauseKind::Host: |
649 | 0 | case OpenACCClauseKind::Link: |
650 | 0 | case OpenACCClauseKind::NoCreate: |
651 | 0 | case OpenACCClauseKind::Present: |
652 | 0 | case OpenACCClauseKind::Private: |
653 | 0 | case OpenACCClauseKind::UseDevice: |
654 | 0 | if (ParseOpenACCClauseVarList(Kind)) |
655 | 0 | return true; |
656 | 0 | break; |
657 | 0 | default: |
658 | 0 | llvm_unreachable("Not a required parens type?"); |
659 | 0 | } |
660 | | |
661 | 0 | return Parens.consumeClose(); |
662 | 0 | } else if (ClauseHasOptionalParens(DirKind, Kind)) { |
663 | 0 | if (!Parens.consumeOpen()) { |
664 | 0 | switch (Kind) { |
665 | 0 | case OpenACCClauseKind::Self: { |
666 | 0 | assert(DirKind != OpenACCDirectiveKind::Update); |
667 | 0 | ExprResult CondExpr = ParseOpenACCConditionalExpr(*this); |
668 | | // An invalid expression can be just about anything, so just give up on |
669 | | // this clause list. |
670 | 0 | if (CondExpr.isInvalid()) |
671 | 0 | return true; |
672 | 0 | break; |
673 | 0 | } |
674 | 0 | default: |
675 | 0 | llvm_unreachable("Not an optional parens type?"); |
676 | 0 | } |
677 | 0 | Parens.consumeClose(); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | return false; |
681 | 0 | } |
682 | | |
683 | | /// OpenACC 3.3, section 2.16: |
684 | | /// In this section and throughout the specification, the term wait-argument |
685 | | /// means: |
686 | | /// [ devnum : int-expr : ] [ queues : ] async-argument-list |
687 | 0 | bool Parser::ParseOpenACCWaitArgument() { |
688 | | // [devnum : int-expr : ] |
689 | 0 | if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::DevNum, Tok) && |
690 | 0 | NextToken().is(tok::colon)) { |
691 | | // Consume devnum. |
692 | 0 | ConsumeToken(); |
693 | | // Consume colon. |
694 | 0 | ConsumeToken(); |
695 | |
|
696 | 0 | ExprResult IntExpr = |
697 | 0 | getActions().CorrectDelayedTyposInExpr(ParseAssignmentExpression()); |
698 | 0 | if (IntExpr.isInvalid()) |
699 | 0 | return true; |
700 | | |
701 | 0 | if (ExpectAndConsume(tok::colon)) |
702 | 0 | return true; |
703 | 0 | } |
704 | | |
705 | | // [ queues : ] |
706 | 0 | if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Queues, Tok) && |
707 | 0 | NextToken().is(tok::colon)) { |
708 | | // Consume queues. |
709 | 0 | ConsumeToken(); |
710 | | // Consume colon. |
711 | 0 | ConsumeToken(); |
712 | 0 | } |
713 | | |
714 | | // OpenACC 3.3, section 2.16: |
715 | | // the term 'async-argument' means a nonnegative scalar integer expression, or |
716 | | // one of the special values 'acc_async_noval' or 'acc_async_sync', as defined |
717 | | // in the C header file and the Fortran opacc module. |
718 | | // |
719 | | // We are parsing this simply as list of assignment expressions (to avoid |
720 | | // comma being troublesome), and will ensure it is an integral type. The |
721 | | // 'special' types are defined as macros, so we can't really check those |
722 | | // (other than perhaps as values at one point?), but the standard does say it |
723 | | // is implementation-defined to use any other negative value. |
724 | | // |
725 | | // |
726 | 0 | bool FirstArg = true; |
727 | 0 | while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { |
728 | 0 | if (!FirstArg) { |
729 | 0 | if (ExpectAndConsume(tok::comma)) |
730 | 0 | return true; |
731 | 0 | } |
732 | 0 | FirstArg = false; |
733 | |
|
734 | 0 | ExprResult CurArg = |
735 | 0 | getActions().CorrectDelayedTyposInExpr(ParseAssignmentExpression()); |
736 | |
|
737 | 0 | if (CurArg.isInvalid()) |
738 | 0 | return true; |
739 | 0 | } |
740 | | |
741 | 0 | return false; |
742 | 0 | } |
743 | | |
744 | 0 | ExprResult Parser::ParseOpenACCIDExpression() { |
745 | 0 | ExprResult Res; |
746 | 0 | if (getLangOpts().CPlusPlus) { |
747 | 0 | Res = ParseCXXIdExpression(/*isAddressOfOperand=*/false); |
748 | 0 | } else { |
749 | | // There isn't anything quite the same as ParseCXXIdExpression for C, so we |
750 | | // need to get the identifier, then call into Sema ourselves. |
751 | |
|
752 | 0 | if (Tok.isNot(tok::identifier)) { |
753 | 0 | Diag(Tok, diag::err_expected) << tok::identifier; |
754 | 0 | return ExprError(); |
755 | 0 | } |
756 | | |
757 | 0 | Token FuncName = getCurToken(); |
758 | 0 | UnqualifiedId Name; |
759 | 0 | CXXScopeSpec ScopeSpec; |
760 | 0 | SourceLocation TemplateKWLoc; |
761 | 0 | Name.setIdentifier(FuncName.getIdentifierInfo(), ConsumeToken()); |
762 | | |
763 | | // Ensure this is a valid identifier. We don't accept causing implicit |
764 | | // function declarations per the spec, so always claim to not have trailing |
765 | | // L Paren. |
766 | 0 | Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, TemplateKWLoc, |
767 | 0 | Name, /*HasTrailingLParen=*/false, |
768 | 0 | /*isAddressOfOperand=*/false); |
769 | 0 | } |
770 | | |
771 | 0 | return getActions().CorrectDelayedTyposInExpr(Res); |
772 | 0 | } |
773 | | |
774 | | /// OpenACC 3.3, section 1.6: |
775 | | /// In this spec, a 'var' (in italics) is one of the following: |
776 | | /// - a variable name (a scalar, array, or compisite variable name) |
777 | | /// - a subarray specification with subscript ranges |
778 | | /// - an array element |
779 | | /// - a member of a composite variable |
780 | | /// - a common block name between slashes (fortran only) |
781 | 0 | bool Parser::ParseOpenACCVar() { |
782 | 0 | OpenACCArraySectionRAII ArraySections(*this); |
783 | 0 | ExprResult Res = |
784 | 0 | getActions().CorrectDelayedTyposInExpr(ParseAssignmentExpression()); |
785 | 0 | return Res.isInvalid(); |
786 | 0 | } |
787 | | |
788 | | /// OpenACC 3.3, section 2.10: |
789 | | /// In C and C++, the syntax of the cache directive is: |
790 | | /// |
791 | | /// #pragma acc cache ([readonly:]var-list) new-line |
792 | 0 | void Parser::ParseOpenACCCacheVarList() { |
793 | | // If this is the end of the line, just return 'false' and count on the close |
794 | | // paren diagnostic to catch the issue. |
795 | 0 | if (getCurToken().isAnnotation()) |
796 | 0 | return; |
797 | | |
798 | | // The VarList is an optional `readonly:` followed by a list of a variable |
799 | | // specifications. Consume something that looks like a 'tag', and diagnose if |
800 | | // it isn't 'readonly'. |
801 | 0 | if (tryParseAndConsumeSpecialTokenKind(*this, |
802 | 0 | OpenACCSpecialTokenKind::ReadOnly, |
803 | 0 | OpenACCDirectiveKind::Cache)) { |
804 | | // FIXME: Record that this is a 'readonly' so that we can use that during |
805 | | // Sema/AST generation. |
806 | 0 | } |
807 | |
|
808 | 0 | bool FirstArray = true; |
809 | 0 | while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { |
810 | 0 | if (!FirstArray) |
811 | 0 | ExpectAndConsume(tok::comma); |
812 | 0 | FirstArray = false; |
813 | | |
814 | | // OpenACC 3.3, section 2.10: |
815 | | // A 'var' in a cache directive must be a single array element or a simple |
816 | | // subarray. In C and C++, a simple subarray is an array name followed by |
817 | | // an extended array range specification in brackets, with a start and |
818 | | // length such as: |
819 | | // |
820 | | // arr[lower:length] |
821 | | // |
822 | 0 | if (ParseOpenACCVar()) |
823 | 0 | SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, tok::comma, |
824 | 0 | StopBeforeMatch); |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | 0 | void Parser::ParseOpenACCDirective() { |
829 | 0 | OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this); |
830 | | |
831 | | // Once we've parsed the construct/directive name, some have additional |
832 | | // specifiers that need to be taken care of. Atomic has an 'atomic-clause' |
833 | | // that needs to be parsed. |
834 | 0 | if (DirKind == OpenACCDirectiveKind::Atomic) |
835 | 0 | ParseOpenACCAtomicKind(*this); |
836 | | |
837 | | // We've successfully parsed the construct/directive name, however a few of |
838 | | // the constructs have optional parens that contain further details. |
839 | 0 | BalancedDelimiterTracker T(*this, tok::l_paren, |
840 | 0 | tok::annot_pragma_openacc_end); |
841 | |
|
842 | 0 | if (!T.consumeOpen()) { |
843 | 0 | switch (DirKind) { |
844 | 0 | default: |
845 | 0 | Diag(T.getOpenLocation(), diag::err_acc_invalid_open_paren); |
846 | 0 | T.skipToEnd(); |
847 | 0 | break; |
848 | 0 | case OpenACCDirectiveKind::Routine: { |
849 | | // Routine has an optional paren-wrapped name of a function in the local |
850 | | // scope. We parse the name, emitting any diagnostics |
851 | 0 | ExprResult RoutineName = ParseOpenACCIDExpression(); |
852 | | // If the routine name is invalid, just skip until the closing paren to |
853 | | // recover more gracefully. |
854 | 0 | if (RoutineName.isInvalid()) |
855 | 0 | T.skipToEnd(); |
856 | 0 | else |
857 | 0 | T.consumeClose(); |
858 | 0 | break; |
859 | 0 | } |
860 | 0 | case OpenACCDirectiveKind::Cache: |
861 | 0 | ParseOpenACCCacheVarList(); |
862 | | // The ParseOpenACCCacheVarList function manages to recover from failures, |
863 | | // so we can always consume the close. |
864 | 0 | T.consumeClose(); |
865 | 0 | break; |
866 | 0 | case OpenACCDirectiveKind::Wait: |
867 | | // OpenACC has an optional paren-wrapped 'wait-argument'. |
868 | 0 | if (ParseOpenACCWaitArgument()) |
869 | 0 | T.skipToEnd(); |
870 | 0 | else |
871 | 0 | T.consumeClose(); |
872 | 0 | break; |
873 | 0 | } |
874 | 0 | } else if (DirKind == OpenACCDirectiveKind::Cache) { |
875 | | // Cache's paren var-list is required, so error here if it isn't provided. |
876 | | // We know that the consumeOpen above left the first non-paren here, so |
877 | | // diagnose, then continue as if it was completely omitted. |
878 | 0 | Diag(Tok, diag::err_expected) << tok::l_paren; |
879 | 0 | } |
880 | | |
881 | | // Parses the list of clauses, if present. |
882 | 0 | ParseOpenACCClauseList(DirKind); |
883 | |
|
884 | 0 | Diag(getCurToken(), diag::warn_pragma_acc_unimplemented); |
885 | 0 | assert(Tok.is(tok::annot_pragma_openacc_end) && |
886 | 0 | "Didn't parse all OpenACC Clauses"); |
887 | 0 | ConsumeAnnotationToken(); |
888 | 0 | } |
889 | | |
890 | | // Parse OpenACC directive on a declaration. |
891 | 0 | Parser::DeclGroupPtrTy Parser::ParseOpenACCDirectiveDecl() { |
892 | 0 | assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token"); |
893 | | |
894 | 0 | ParsingOpenACCDirectiveRAII DirScope(*this); |
895 | 0 | ConsumeAnnotationToken(); |
896 | |
|
897 | 0 | ParseOpenACCDirective(); |
898 | |
|
899 | 0 | return nullptr; |
900 | 0 | } |
901 | | |
902 | | // Parse OpenACC Directive on a Statement. |
903 | 0 | StmtResult Parser::ParseOpenACCDirectiveStmt() { |
904 | 0 | assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token"); |
905 | | |
906 | 0 | ParsingOpenACCDirectiveRAII DirScope(*this); |
907 | 0 | ConsumeAnnotationToken(); |
908 | |
|
909 | 0 | ParseOpenACCDirective(); |
910 | |
|
911 | 0 | return StmtEmpty(); |
912 | 0 | } |