MLIR 24.0.0git
TypeParser.cpp
Go to the documentation of this file.
1//===- TypeParser.cpp - MLIR Type Parser Implementation -------------------===//
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 parser for the MLIR Types.
10//
11//===----------------------------------------------------------------------===//
12
13#include "Parser.h"
14#include "mlir/IR/AffineMap.h"
20#include "mlir/IR/Types.h"
21#include "mlir/Support/LLVM.h"
22#include <cassert>
23#include <cstdint>
24#include <limits>
25#include <optional>
26
27using namespace mlir;
28using namespace mlir::detail;
29
30/// Optionally parse a type.
32 // There are many different starting tokens for a type, check them here.
33 switch (getToken().getKind()) {
34 case Token::l_paren:
35 case Token::kw_memref:
36 case Token::kw_tensor:
37 case Token::kw_complex:
38 case Token::kw_tuple:
39 case Token::kw_vector:
40 case Token::inttype:
41 case Token::kw_f4E2M1FN:
42 case Token::kw_f6E2M3FN:
43 case Token::kw_f6E3M2FN:
44 case Token::kw_f8E5M2:
45 case Token::kw_f8E4M3:
46 case Token::kw_f8E4M3FN:
47 case Token::kw_f8E5M2FNUZ:
48 case Token::kw_f8E4M3FNUZ:
49 case Token::kw_f8E4M3B11FNUZ:
50 case Token::kw_f8E3M4:
51 case Token::kw_f8E8M0FNU:
52 case Token::kw_f8E5M3FNU:
53 case Token::kw_bf16:
54 case Token::kw_f16:
55 case Token::kw_tf32:
56 case Token::kw_f32:
57 case Token::kw_f64:
58 case Token::kw_f80:
59 case Token::kw_f128:
60 case Token::kw_index:
61 case Token::kw_mxi8:
62 case Token::kw_none:
63 case Token::kw_token:
64 case Token::exclamation_identifier:
65 return failure(!(type = parseType()));
66
67 default:
68 return std::nullopt;
69 }
70}
71
72/// Parse an arbitrary type.
73///
74/// type ::= function-type
75/// | non-function-type
76///
78 if (getToken().is(Token::l_paren))
79 return parseFunctionType();
80 return parseNonFunctionType();
81}
82
83/// Parse a function result type.
84///
85/// function-result-type ::= type-list-parens
86/// | non-function-type
87///
89 if (getToken().is(Token::l_paren))
90 return parseTypeListParens(elements);
91
93 if (!t)
94 return failure();
95 elements.push_back(t);
96 return success();
97}
98
99/// Parse a list of types without an enclosing parenthesis. The list must have
100/// at least one member.
101///
102/// type-list-no-parens ::= type (`,` type)*
103///
105 auto parseElt = [&]() -> ParseResult {
106 auto elt = parseType();
107 elements.push_back(elt);
108 return elt ? success() : failure();
109 };
110
111 return parseCommaSeparatedList(parseElt);
112}
113
114/// Parse a parenthesized list of types.
115///
116/// type-list-parens ::= `(` `)`
117/// | `(` type-list-no-parens `)`
118///
120 if (parseToken(Token::l_paren, "expected '('"))
121 return failure();
122
123 // Handle empty lists.
124 if (getToken().is(Token::r_paren))
125 return consumeToken(), success();
126
127 if (parseTypeListNoParens(elements) ||
128 parseToken(Token::r_paren, "expected ')'"))
129 return failure();
130 return success();
131}
132
133/// Parse a complex type.
134///
135/// complex-type ::= `complex` `<` type `>`
136///
138 consumeToken(Token::kw_complex);
139
140 // Parse the '<'.
141 if (parseToken(Token::less, "expected '<' in complex type"))
142 return nullptr;
143
144 SMLoc elementTypeLoc = getToken().getLoc();
145 auto elementType = parseType();
146 if (!elementType ||
147 parseToken(Token::greater, "expected '>' in complex type"))
148 return nullptr;
149 if (!isa<FloatType>(elementType) && !isa<IntegerType>(elementType))
150 return emitError(elementTypeLoc, "invalid element type for complex"),
151 nullptr;
152
153 return ComplexType::get(elementType);
154}
155
156/// Parse a function type.
157///
158/// function-type ::= type-list-parens `->` function-result-type
159///
161 assert(getToken().is(Token::l_paren));
162
163 SmallVector<Type, 4> arguments, results;
164 if (parseTypeListParens(arguments) ||
165 parseToken(Token::arrow, "expected '->' in function type") ||
167 return nullptr;
168
169 return builder.getFunctionType(arguments, results);
170}
171
172/// Parse a memref type.
173///
174/// memref-type ::= ranked-memref-type | unranked-memref-type
175///
176/// ranked-memref-type ::= `memref` `<` dimension-list-ranked type
177/// (`,` layout-specification)? (`,` memory-space)? `>`
178///
179/// unranked-memref-type ::= `memref` `<*x` type (`,` memory-space)? `>`
180///
181/// stride-list ::= `[` (dimension (`,` dimension)*)? `]`
182/// strided-layout ::= `offset:` dimension `,` `strides: ` stride-list
183/// layout-specification ::= semi-affine-map | strided-layout | attribute
184/// memory-space ::= integer-literal | attribute
185///
187 SMLoc loc = getToken().getLoc();
188 consumeToken(Token::kw_memref);
189
190 if (parseToken(Token::less, "expected '<' in memref type"))
191 return nullptr;
192
193 bool isUnranked;
194 SmallVector<int64_t, 4> dimensions;
195
196 if (consumeIf(Token::star)) {
197 // This is an unranked memref type.
198 isUnranked = true;
200 return nullptr;
201
202 } else {
203 isUnranked = false;
204 if (parseDimensionListRanked(dimensions))
205 return nullptr;
206 }
207
208 // Parse the element type.
209 auto typeLoc = getToken().getLoc();
210 auto elementType = parseType();
211 if (!elementType)
212 return nullptr;
213
214 // Check that memref is formed from allowed types.
215 if (!BaseMemRefType::isValidElementType(elementType))
216 return emitError(typeLoc, "invalid memref element type"), nullptr;
217
218 MemRefLayoutAttrInterface layout;
219 Attribute memorySpace;
220
221 auto parseElt = [&]() -> ParseResult {
222 // Either it is MemRefLayoutAttrInterface or memory space attribute.
223 Attribute attr = parseAttribute();
224 if (!attr)
225 return failure();
226
227 if (isa<MemRefLayoutAttrInterface>(attr)) {
228 layout = cast<MemRefLayoutAttrInterface>(attr);
229 } else if (memorySpace) {
230 return emitError("multiple memory spaces specified in memref type");
231 } else {
232 memorySpace = attr;
233 return success();
234 }
235
236 if (isUnranked)
237 return emitError("cannot have affine map for unranked memref type");
238 if (memorySpace)
239 return emitError("expected memory space to be last in memref type");
240
241 return success();
242 };
243
244 // Parse a list of mappings and address space if present.
245 if (!consumeIf(Token::greater)) {
246 // Parse comma separated list of affine maps, followed by memory space.
247 if (parseToken(Token::comma, "expected ',' or '>' in memref type") ||
248 parseCommaSeparatedListUntil(Token::greater, parseElt,
249 /*allowEmptyList=*/false)) {
250 return nullptr;
251 }
252 }
253
254 if (isUnranked)
255 return getChecked<UnrankedMemRefType>(loc, elementType, memorySpace);
256
257 return getChecked<MemRefType>(loc, dimensions, elementType, layout,
258 memorySpace);
259}
260
261/// Parse any type except the function type.
262///
263/// non-function-type ::= integer-type
264/// | index-type
265/// | float-type
266/// | extended-type
267/// | vector-type
268/// | tensor-type
269/// | memref-type
270/// | complex-type
271/// | tuple-type
272/// | none-type
273///
274/// index-type ::= `index`
275/// float-type ::= `f16` | `bf16` | `f32` | `f64` | `f80` | `f128`
276/// none-type ::= `none`
277///
279 switch (getToken().getKind()) {
280 default:
281 return (emitWrongTokenError("expected non-function type"), nullptr);
282 case Token::kw_memref:
283 return parseMemRefType();
284 case Token::kw_tensor:
285 return parseTensorType();
286 case Token::kw_complex:
287 return parseComplexType();
288 case Token::kw_tuple:
289 return parseTupleType();
290 case Token::kw_vector:
291 return parseVectorType();
292 // integer-type
293 case Token::inttype: {
294 auto width = getToken().getIntTypeBitwidth();
295 if (!width.has_value())
296 return (emitError("invalid integer width"), nullptr);
297 if (*width > IntegerType::kMaxWidth) {
298 emitError(getToken().getLoc(), "integer bitwidth is limited to ")
299 << IntegerType::kMaxWidth << " bits";
300 return nullptr;
301 }
302
303 IntegerType::SignednessSemantics signSemantics = IntegerType::Signless;
304 if (std::optional<bool> signedness = getToken().getIntTypeSignedness())
305 signSemantics = *signedness ? IntegerType::Signed : IntegerType::Unsigned;
306
307 consumeToken(Token::inttype);
308 return IntegerType::get(getContext(), *width, signSemantics);
309 }
310
311 // float-type
312 case Token::kw_f4E2M1FN:
313 consumeToken(Token::kw_f4E2M1FN);
314 return builder.getType<Float4E2M1FNType>();
315 case Token::kw_f6E2M3FN:
316 consumeToken(Token::kw_f6E2M3FN);
317 return builder.getType<Float6E2M3FNType>();
318 case Token::kw_f6E3M2FN:
319 consumeToken(Token::kw_f6E3M2FN);
320 return builder.getType<Float6E3M2FNType>();
321 case Token::kw_f8E5M2:
322 consumeToken(Token::kw_f8E5M2);
323 return builder.getType<Float8E5M2Type>();
324 case Token::kw_f8E4M3:
325 consumeToken(Token::kw_f8E4M3);
326 return builder.getType<Float8E4M3Type>();
327 case Token::kw_f8E4M3FN:
328 consumeToken(Token::kw_f8E4M3FN);
329 return builder.getType<Float8E4M3FNType>();
330 case Token::kw_f8E5M2FNUZ:
331 consumeToken(Token::kw_f8E5M2FNUZ);
332 return builder.getType<Float8E5M2FNUZType>();
333 case Token::kw_f8E4M3FNUZ:
334 consumeToken(Token::kw_f8E4M3FNUZ);
335 return builder.getType<Float8E4M3FNUZType>();
336 case Token::kw_f8E4M3B11FNUZ:
337 consumeToken(Token::kw_f8E4M3B11FNUZ);
338 return builder.getType<Float8E4M3B11FNUZType>();
339 case Token::kw_f8E3M4:
340 consumeToken(Token::kw_f8E3M4);
341 return builder.getType<Float8E3M4Type>();
342 case Token::kw_f8E8M0FNU:
343 consumeToken(Token::kw_f8E8M0FNU);
344 return builder.getType<Float8E8M0FNUType>();
345 case Token::kw_f8E5M3FNU:
346 consumeToken(Token::kw_f8E5M3FNU);
347 return builder.getType<Float8E5M3FNUType>();
348 case Token::kw_bf16:
349 consumeToken(Token::kw_bf16);
350 return builder.getType<BFloat16Type>();
351 case Token::kw_f16:
352 consumeToken(Token::kw_f16);
353 return builder.getType<Float16Type>();
354 case Token::kw_tf32:
355 consumeToken(Token::kw_tf32);
356 return builder.getType<FloatTF32Type>();
357 case Token::kw_f32:
358 consumeToken(Token::kw_f32);
359 return builder.getType<Float32Type>();
360 case Token::kw_f64:
361 consumeToken(Token::kw_f64);
362 return builder.getType<Float64Type>();
363 case Token::kw_f80:
364 consumeToken(Token::kw_f80);
365 return builder.getType<Float80Type>();
366 case Token::kw_f128:
367 consumeToken(Token::kw_f128);
368 return builder.getType<Float128Type>();
369
370 // index-type
371 case Token::kw_index:
372 consumeToken(Token::kw_index);
373 return builder.getIndexType();
374
375 case Token::kw_mxi8:
376 consumeToken(Token::kw_mxi8);
377 return builder.getType<MXI8Type>();
378
379 // none-type
380 case Token::kw_none:
381 consumeToken(Token::kw_none);
382 return builder.getNoneType();
383
384 // token-type
385 case Token::kw_token:
386 consumeToken(Token::kw_token);
387 return builder.getType<TokenType>();
388
389 // extended type
390 case Token::exclamation_identifier:
391 return parseExtendedType();
392
393 // Handle completion of a dialect type.
394 case Token::code_complete:
395 if (getToken().isCodeCompletionFor(Token::exclamation_identifier))
396 return parseExtendedType();
397 return codeCompleteType();
398 }
399}
400
401/// Parse a tensor type.
402///
403/// tensor-type ::= `tensor` `<` dimension-list type `>`
404/// dimension-list ::= dimension-list-ranked | `*x`
405///
407 consumeToken(Token::kw_tensor);
408
409 if (parseToken(Token::less, "expected '<' in tensor type"))
410 return nullptr;
411
412 bool isUnranked;
413 SmallVector<int64_t, 4> dimensions;
414
415 if (consumeIf(Token::star)) {
416 // This is an unranked tensor type.
417 isUnranked = true;
418
420 return nullptr;
421
422 } else {
423 isUnranked = false;
424 if (parseDimensionListRanked(dimensions))
425 return nullptr;
426 }
427
428 // Parse the element type.
429 auto elementTypeLoc = getToken().getLoc();
430 auto elementType = parseType();
431
432 // Parse an optional encoding attribute.
433 Attribute encoding;
434 if (consumeIf(Token::comma)) {
435 auto parseResult = parseOptionalAttribute(encoding);
436 if (parseResult.has_value()) {
437 if (failed(parseResult.value()))
438 return nullptr;
439 if (auto v = dyn_cast_or_null<VerifiableTensorEncoding>(encoding)) {
440 if (failed(v.verifyEncoding(dimensions, elementType,
441 [&] { return emitError(); })))
442 return nullptr;
443 }
444 }
445 }
446
447 if (!elementType || parseToken(Token::greater, "expected '>' in tensor type"))
448 return nullptr;
449 if (!TensorType::isValidElementType(elementType))
450 return emitError(elementTypeLoc, "invalid tensor element type"), nullptr;
451
452 if (isUnranked) {
453 if (encoding)
454 return emitError("cannot apply encoding to unranked tensor"), nullptr;
455 return UnrankedTensorType::get(elementType);
456 }
457 return RankedTensorType::get(dimensions, elementType, encoding);
458}
459
460/// Parse a tuple type.
461///
462/// tuple-type ::= `tuple` `<` (type (`,` type)*)? `>`
463///
465 consumeToken(Token::kw_tuple);
466
467 // Parse the '<'.
468 if (parseToken(Token::less, "expected '<' in tuple type"))
469 return nullptr;
470
471 // Check for an empty tuple by directly parsing '>'.
472 if (consumeIf(Token::greater))
473 return TupleType::get(getContext());
474
475 // Parse the element types and the '>'.
477 if (parseTypeListNoParens(types) ||
478 parseToken(Token::greater, "expected '>' in tuple type"))
479 return nullptr;
480
481 return TupleType::get(getContext(), types);
482}
483
484/// Parse a vector type.
485///
486/// vector-type ::= `vector` `<` vector-dim-list vector-element-type `>`
487/// vector-dim-list := (static-dim-list `x`)? (`[` static-dim-list `]` `x`)?
488/// static-dim-list ::= decimal-literal (`x` decimal-literal)*
489///
491 SMLoc loc = getToken().getLoc();
492 consumeToken(Token::kw_vector);
493
494 if (parseToken(Token::less, "expected '<' in vector type"))
495 return nullptr;
496
497 // Parse the dimensions.
498 SmallVector<int64_t, 4> dimensions;
499 SmallVector<bool, 4> scalableDims;
500 if (parseVectorDimensionList(dimensions, scalableDims))
501 return nullptr;
502
503 // Parse the element type.
504 auto elementType = parseType();
505 if (!elementType || parseToken(Token::greater, "expected '>' in vector type"))
506 return nullptr;
507
508 return getChecked<VectorType>(loc, dimensions, elementType, scalableDims);
509}
510
511/// Parse a dimension list in a vector type. This populates the dimension list.
512/// For i-th dimension, `scalableDims[i]` contains either:
513/// * `false` for a non-scalable dimension (e.g. `4`),
514/// * `true` for a scalable dimension (e.g. `[4]`).
515///
516/// vector-dim-list := (static-dim-list `x`)?
517/// static-dim-list ::= static-dim (`x` static-dim)*
518/// static-dim ::= (decimal-literal | `[` decimal-literal `]`)
519///
520ParseResult
522 SmallVectorImpl<bool> &scalableDims) {
523 // If there is a set of fixed-length dimensions, consume it
524 while (getToken().is(Token::integer) || getToken().is(Token::l_square)) {
525 int64_t value;
526 bool scalable = consumeIf(Token::l_square);
528 return failure();
529 dimensions.push_back(value);
530 if (scalable) {
531 if (!consumeIf(Token::r_square))
532 return emitWrongTokenError("missing ']' closing scalable dimension");
533 }
534 scalableDims.push_back(scalable);
535 // Make sure we have an 'x' or something like 'xbf32'.
537 return failure();
538 }
539
540 return success();
541}
542
543/// Parse a dimension list of a tensor or memref type. This populates the
544/// dimension list, using ShapedType::kDynamic for the `?` dimensions if
545/// `allowDynamic` is set and errors out on `?` otherwise. Parsing the trailing
546/// `x` is configurable.
547///
548/// dimension-list ::= eps | dimension (`x` dimension)*
549/// dimension-list-with-trailing-x ::= (dimension `x`)*
550/// dimension ::= `?` | decimal-literal
551///
552/// When `allowDynamic` is not set, this is used to parse:
553///
554/// static-dimension-list ::= eps | decimal-literal (`x` decimal-literal)*
555/// static-dimension-list-with-trailing-x ::= (dimension `x`)*
556ParseResult
558 bool allowDynamic, bool withTrailingX) {
559 auto parseDim = [&]() -> LogicalResult {
560 auto loc = getToken().getLoc();
561 if (consumeIf(Token::question)) {
562 if (!allowDynamic)
563 return emitError(loc, "expected static shape");
564 dimensions.push_back(ShapedType::kDynamic);
565 } else {
566 int64_t value;
567 if (failed(parseIntegerInDimensionList(value)))
568 return failure();
569 dimensions.push_back(value);
570 }
571 return success();
572 };
573
574 if (withTrailingX) {
575 while (getToken().isAny(Token::integer, Token::question)) {
576 if (failed(parseDim()) || failed(parseXInDimensionList()))
577 return failure();
578 }
579 return success();
580 }
581
582 if (getToken().isAny(Token::integer, Token::question)) {
583 if (failed(parseDim()))
584 return failure();
585 while (getToken().is(Token::bare_identifier) &&
586 getTokenSpelling()[0] == 'x') {
587 if (failed(parseXInDimensionList()) || failed(parseDim()))
588 return failure();
589 }
590 }
591 return success();
592}
593
595 // Hexadecimal integer literals (starting with `0x`) are not allowed in
596 // aggregate type declarations. Therefore, `0xf32` should be processed as
597 // a sequence of separate elements `0`, `x`, `f32`.
598 if (getTokenSpelling().size() > 1 && getTokenSpelling()[1] == 'x') {
599 // We can get here only if the token is an integer literal. Hexadecimal
600 // integer literals can only start with `0x` (`1x` wouldn't lex as a
601 // literal, just `1` would, at which point we don't get into this
602 // branch).
603 assert(getTokenSpelling()[0] == '0' && "invalid integer literal");
604 value = 0;
605 state.lex.resetPointer(getTokenSpelling().data() + 1);
606 consumeToken();
607 } else {
608 // Make sure this integer value is in bound and valid.
609 std::optional<uint64_t> dimension = getToken().getUInt64IntegerValue();
610 if (!dimension ||
611 *dimension > (uint64_t)std::numeric_limits<int64_t>::max())
612 return emitError("invalid dimension");
613 value = (int64_t)*dimension;
614 consumeToken(Token::integer);
615 }
616 return success();
617}
618
619/// Parse an 'x' token in a dimension list, handling the case where the x is
620/// juxtaposed with an element type, as in "xf32", leaving the "f32" as the next
621/// token.
623 if (getToken().isNot(Token::bare_identifier) || getTokenSpelling()[0] != 'x')
624 return emitWrongTokenError("expected 'x' in dimension list");
625
626 // If we had a prefix of 'x', lex the next token immediately after the 'x'.
627 if (getTokenSpelling().size() != 1)
628 state.lex.resetPointer(getTokenSpelling().data() + 1);
629
630 // Consume the 'x'.
631 consumeToken(Token::bare_identifier);
632
633 return success();
634}
return success()
Attributes are known-constant values of operations.
Definition Attributes.h:25
static bool isValidElementType(Type type)
Return true if the specified element type is ok in a memref.
This class implements Optional functionality for ParseResult.
static bool isValidElementType(Type type)
Return true if the specified element type is ok in a tensor.
SMLoc getLoc() const
Definition Token.cpp:24
static std::optional< uint64_t > getUInt64IntegerValue(StringRef spelling)
For an integer token, return its value as an uint64_t.
Definition Token.cpp:45
std::optional< unsigned > getIntTypeBitwidth() const
For an inttype token, return its bitwidth.
Definition Token.cpp:55
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
ParseResult parseXInDimensionList()
Parse an 'x' token in a dimension list, handling the case where the x is juxtaposed with an element t...
T getChecked(SMLoc loc, ParamsT &&...params)
Invoke the getChecked method of the given Attribute or Type class, using the provided location to emi...
Definition Parser.h:198
OptionalParseResult parseOptionalType(Type &type)
Optionally parse a type.
ParseResult parseToken(Token::Kind expectedToken, const Twine &message)
Consume the specified token if present and return success.
Definition Parser.cpp:306
ParseResult parseCommaSeparatedListUntil(Token::Kind rightToken, function_ref< ParseResult()> parseElement, bool allowEmptyList=true)
Parse a comma-separated list of elements up until the specified end token.
Definition Parser.cpp:174
Type parseType()
Parse an arbitrary type.
ParseResult parseTypeListParens(SmallVectorImpl< Type > &elements)
Parse a parenthesized list of types.
ParseResult parseVectorDimensionList(SmallVectorImpl< int64_t > &dimensions, SmallVectorImpl< bool > &scalableDims)
Parse a dimension list in a vector type.
Type parseMemRefType()
Parse a memref type.
Type parseNonFunctionType()
Parse a non function type.
Type parseExtendedType()
Parse an extended type.
Type parseTupleType()
Parse a tuple type.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error and return failure.
Definition Parser.cpp:193
ParserState & state
The Parser is subclassed and reinstantiated.
Definition Parser.h:372
Attribute parseAttribute(Type type={})
Parse an arbitrary attribute with an optional type.
StringRef getTokenSpelling() const
Definition Parser.h:104
void consumeToken()
Advance the current lexer onto the next token.
Definition Parser.h:119
ParseResult parseIntegerInDimensionList(int64_t &value)
Type parseComplexType()
Parse a complex type.
ParseResult parseDimensionListRanked(SmallVectorImpl< int64_t > &dimensions, bool allowDynamic=true, bool withTrailingX=true)
Parse a dimension list of a tensor or memref type.
ParseResult parseFunctionResultTypes(SmallVectorImpl< Type > &elements)
Parse a function result type.
InFlightDiagnostic emitWrongTokenError(const Twine &message={})
Emit an error about a "wrong token".
Definition Parser.cpp:255
ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())
Parse a list of comma-separated items with an optional delimiter.
Definition Parser.cpp:85
VectorType parseVectorType()
Parse a vector type.
Type parseFunctionType()
Parse a function type.
OptionalParseResult parseOptionalAttribute(Attribute &attribute, Type type={})
Parse an optional attribute with the provided type.
ParseResult parseTypeListNoParens(SmallVectorImpl< Type > &elements)
Parse a list of types without an enclosing parenthesis.
const Token & getToken() const
Return the current token the parser is inspecting.
Definition Parser.h:103
MLIRContext * getContext() const
Definition Parser.h:38
Type parseTensorType()
Parse a tensor type.
bool consumeIf(Token::Kind kind)
If the current token has the specified kind, consume it and return true.
Definition Parser.h:111
AttrTypeReplacer.
Include the generated interface declarations.