Bug Summary

File:build/source/mlir/lib/AsmParser/TypeParser.cpp
Warning:line 522, column 7
1st function call argument is an uninitialized value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name TypeParser.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-16/lib/clang/16 -I tools/mlir/lib/AsmParser -I /build/source/mlir/lib/AsmParser -I include -I /build/source/llvm/include -I /build/source/mlir/include -I tools/mlir/include -D MLIR_CUDA_CONVERSIONS_ENABLED=1 -D MLIR_ROCM_CONVERSIONS_ENABLED=1 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1671487667 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-12-20-010714-16201-1 -x c++ /build/source/mlir/lib/AsmParser/TypeParser.cpp
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"
15#include "mlir/IR/BuiltinTypes.h"
16#include "mlir/IR/OpDefinition.h"
17#include "mlir/IR/TensorEncoding.h"
18
19using namespace mlir;
20using namespace mlir::detail;
21
22/// Optionally parse a type.
23OptionalParseResult Parser::parseOptionalType(Type &type) {
24 // There are many different starting tokens for a type, check them here.
25 switch (getToken().getKind()) {
26 case Token::l_paren:
27 case Token::kw_memref:
28 case Token::kw_tensor:
29 case Token::kw_complex:
30 case Token::kw_tuple:
31 case Token::kw_vector:
32 case Token::inttype:
33 case Token::kw_f8E5M2:
34 case Token::kw_f8E4M3FN:
35 case Token::kw_bf16:
36 case Token::kw_f16:
37 case Token::kw_f32:
38 case Token::kw_f64:
39 case Token::kw_f80:
40 case Token::kw_f128:
41 case Token::kw_index:
42 case Token::kw_none:
43 case Token::exclamation_identifier:
44 return failure(!(type = parseType()));
45
46 default:
47 return std::nullopt;
48 }
49}
50
51/// Parse an arbitrary type.
52///
53/// type ::= function-type
54/// | non-function-type
55///
56Type Parser::parseType() {
57 if (getToken().is(Token::l_paren))
58 return parseFunctionType();
59 return parseNonFunctionType();
60}
61
62/// Parse a function result type.
63///
64/// function-result-type ::= type-list-parens
65/// | non-function-type
66///
67ParseResult Parser::parseFunctionResultTypes(SmallVectorImpl<Type> &elements) {
68 if (getToken().is(Token::l_paren))
69 return parseTypeListParens(elements);
70
71 Type t = parseNonFunctionType();
72 if (!t)
73 return failure();
74 elements.push_back(t);
75 return success();
76}
77
78/// Parse a list of types without an enclosing parenthesis. The list must have
79/// at least one member.
80///
81/// type-list-no-parens ::= type (`,` type)*
82///
83ParseResult Parser::parseTypeListNoParens(SmallVectorImpl<Type> &elements) {
84 auto parseElt = [&]() -> ParseResult {
85 auto elt = parseType();
86 elements.push_back(elt);
87 return elt ? success() : failure();
88 };
89
90 return parseCommaSeparatedList(parseElt);
91}
92
93/// Parse a parenthesized list of types.
94///
95/// type-list-parens ::= `(` `)`
96/// | `(` type-list-no-parens `)`
97///
98ParseResult Parser::parseTypeListParens(SmallVectorImpl<Type> &elements) {
99 if (parseToken(Token::l_paren, "expected '('"))
100 return failure();
101
102 // Handle empty lists.
103 if (getToken().is(Token::r_paren))
104 return consumeToken(), success();
105
106 if (parseTypeListNoParens(elements) ||
107 parseToken(Token::r_paren, "expected ')'"))
108 return failure();
109 return success();
110}
111
112/// Parse a complex type.
113///
114/// complex-type ::= `complex` `<` type `>`
115///
116Type Parser::parseComplexType() {
117 consumeToken(Token::kw_complex);
118
119 // Parse the '<'.
120 if (parseToken(Token::less, "expected '<' in complex type"))
121 return nullptr;
122
123 SMLoc elementTypeLoc = getToken().getLoc();
124 auto elementType = parseType();
125 if (!elementType ||
126 parseToken(Token::greater, "expected '>' in complex type"))
127 return nullptr;
128 if (!elementType.isa<FloatType>() && !elementType.isa<IntegerType>())
129 return emitError(elementTypeLoc, "invalid element type for complex"),
130 nullptr;
131
132 return ComplexType::get(elementType);
133}
134
135/// Parse a function type.
136///
137/// function-type ::= type-list-parens `->` function-result-type
138///
139Type Parser::parseFunctionType() {
140 assert(getToken().is(Token::l_paren))(static_cast <bool> (getToken().is(Token::l_paren)) ? void
(0) : __assert_fail ("getToken().is(Token::l_paren)", "mlir/lib/AsmParser/TypeParser.cpp"
, 140, __extension__ __PRETTY_FUNCTION__))
;
141
142 SmallVector<Type, 4> arguments, results;
143 if (parseTypeListParens(arguments) ||
144 parseToken(Token::arrow, "expected '->' in function type") ||
145 parseFunctionResultTypes(results))
146 return nullptr;
147
148 return builder.getFunctionType(arguments, results);
149}
150
151/// Parse a memref type.
152///
153/// memref-type ::= ranked-memref-type | unranked-memref-type
154///
155/// ranked-memref-type ::= `memref` `<` dimension-list-ranked type
156/// (`,` layout-specification)? (`,` memory-space)? `>`
157///
158/// unranked-memref-type ::= `memref` `<*x` type (`,` memory-space)? `>`
159///
160/// stride-list ::= `[` (dimension (`,` dimension)*)? `]`
161/// strided-layout ::= `offset:` dimension `,` `strides: ` stride-list
162/// layout-specification ::= semi-affine-map | strided-layout | attribute
163/// memory-space ::= integer-literal | attribute
164///
165Type Parser::parseMemRefType() {
166 SMLoc loc = getToken().getLoc();
167 consumeToken(Token::kw_memref);
168
169 if (parseToken(Token::less, "expected '<' in memref type"))
170 return nullptr;
171
172 bool isUnranked;
173 SmallVector<int64_t, 4> dimensions;
174
175 if (consumeIf(Token::star)) {
176 // This is an unranked memref type.
177 isUnranked = true;
178 if (parseXInDimensionList())
179 return nullptr;
180
181 } else {
182 isUnranked = false;
183 if (parseDimensionListRanked(dimensions))
184 return nullptr;
185 }
186
187 // Parse the element type.
188 auto typeLoc = getToken().getLoc();
189 auto elementType = parseType();
190 if (!elementType)
191 return nullptr;
192
193 // Check that memref is formed from allowed types.
194 if (!BaseMemRefType::isValidElementType(elementType))
195 return emitError(typeLoc, "invalid memref element type"), nullptr;
196
197 MemRefLayoutAttrInterface layout;
198 Attribute memorySpace;
199
200 auto parseElt = [&]() -> ParseResult {
201 // Either it is MemRefLayoutAttrInterface or memory space attribute.
202 Attribute attr = parseAttribute();
203 if (!attr)
204 return failure();
205
206 if (attr.isa<MemRefLayoutAttrInterface>()) {
207 layout = attr.cast<MemRefLayoutAttrInterface>();
208 } else if (memorySpace) {
209 return emitError("multiple memory spaces specified in memref type");
210 } else {
211 memorySpace = attr;
212 return success();
213 }
214
215 if (isUnranked)
216 return emitError("cannot have affine map for unranked memref type");
217 if (memorySpace)
218 return emitError("expected memory space to be last in memref type");
219
220 return success();
221 };
222
223 // Parse a list of mappings and address space if present.
224 if (!consumeIf(Token::greater)) {
225 // Parse comma separated list of affine maps, followed by memory space.
226 if (parseToken(Token::comma, "expected ',' or '>' in memref type") ||
227 parseCommaSeparatedListUntil(Token::greater, parseElt,
228 /*allowEmptyList=*/false)) {
229 return nullptr;
230 }
231 }
232
233 if (isUnranked)
234 return getChecked<UnrankedMemRefType>(loc, elementType, memorySpace);
235
236 return getChecked<MemRefType>(loc, dimensions, elementType, layout,
237 memorySpace);
238}
239
240/// Parse any type except the function type.
241///
242/// non-function-type ::= integer-type
243/// | index-type
244/// | float-type
245/// | extended-type
246/// | vector-type
247/// | tensor-type
248/// | memref-type
249/// | complex-type
250/// | tuple-type
251/// | none-type
252///
253/// index-type ::= `index`
254/// float-type ::= `f16` | `bf16` | `f32` | `f64` | `f80` | `f128`
255/// none-type ::= `none`
256///
257Type Parser::parseNonFunctionType() {
258 switch (getToken().getKind()) {
259 default:
260 return (emitWrongTokenError("expected non-function type"), nullptr);
261 case Token::kw_memref:
262 return parseMemRefType();
263 case Token::kw_tensor:
264 return parseTensorType();
265 case Token::kw_complex:
266 return parseComplexType();
267 case Token::kw_tuple:
268 return parseTupleType();
269 case Token::kw_vector:
270 return parseVectorType();
271 // integer-type
272 case Token::inttype: {
273 auto width = getToken().getIntTypeBitwidth();
274 if (!width.has_value())
275 return (emitError("invalid integer width"), nullptr);
276 if (*width > IntegerType::kMaxWidth) {
277 emitError(getToken().getLoc(), "integer bitwidth is limited to ")
278 << IntegerType::kMaxWidth << " bits";
279 return nullptr;
280 }
281
282 IntegerType::SignednessSemantics signSemantics = IntegerType::Signless;
283 if (Optional<bool> signedness = getToken().getIntTypeSignedness())
284 signSemantics = *signedness ? IntegerType::Signed : IntegerType::Unsigned;
285
286 consumeToken(Token::inttype);
287 return IntegerType::get(getContext(), *width, signSemantics);
288 }
289
290 // float-type
291 case Token::kw_f8E5M2:
292 consumeToken(Token::kw_f8E5M2);
293 return builder.getFloat8E5M2Type();
294 case Token::kw_f8E4M3FN:
295 consumeToken(Token::kw_f8E4M3FN);
296 return builder.getFloat8E4M3FNType();
297 case Token::kw_bf16:
298 consumeToken(Token::kw_bf16);
299 return builder.getBF16Type();
300 case Token::kw_f16:
301 consumeToken(Token::kw_f16);
302 return builder.getF16Type();
303 case Token::kw_f32:
304 consumeToken(Token::kw_f32);
305 return builder.getF32Type();
306 case Token::kw_f64:
307 consumeToken(Token::kw_f64);
308 return builder.getF64Type();
309 case Token::kw_f80:
310 consumeToken(Token::kw_f80);
311 return builder.getF80Type();
312 case Token::kw_f128:
313 consumeToken(Token::kw_f128);
314 return builder.getF128Type();
315
316 // index-type
317 case Token::kw_index:
318 consumeToken(Token::kw_index);
319 return builder.getIndexType();
320
321 // none-type
322 case Token::kw_none:
323 consumeToken(Token::kw_none);
324 return builder.getNoneType();
325
326 // extended type
327 case Token::exclamation_identifier:
328 return parseExtendedType();
329
330 // Handle completion of a dialect type.
331 case Token::code_complete:
332 if (getToken().isCodeCompletionFor(Token::exclamation_identifier))
333 return parseExtendedType();
334 return codeCompleteType();
335 }
336}
337
338/// Parse a tensor type.
339///
340/// tensor-type ::= `tensor` `<` dimension-list type `>`
341/// dimension-list ::= dimension-list-ranked | `*x`
342///
343Type Parser::parseTensorType() {
344 consumeToken(Token::kw_tensor);
345
346 if (parseToken(Token::less, "expected '<' in tensor type"))
347 return nullptr;
348
349 bool isUnranked;
350 SmallVector<int64_t, 4> dimensions;
351
352 if (consumeIf(Token::star)) {
353 // This is an unranked tensor type.
354 isUnranked = true;
355
356 if (parseXInDimensionList())
357 return nullptr;
358
359 } else {
360 isUnranked = false;
361 if (parseDimensionListRanked(dimensions))
362 return nullptr;
363 }
364
365 // Parse the element type.
366 auto elementTypeLoc = getToken().getLoc();
367 auto elementType = parseType();
368
369 // Parse an optional encoding attribute.
370 Attribute encoding;
371 if (consumeIf(Token::comma)) {
372 encoding = parseAttribute();
373 if (auto v = encoding.dyn_cast_or_null<VerifiableTensorEncoding>()) {
374 if (failed(v.verifyEncoding(dimensions, elementType,
375 [&] { return emitError(); })))
376 return nullptr;
377 }
378 }
379
380 if (!elementType || parseToken(Token::greater, "expected '>' in tensor type"))
381 return nullptr;
382 if (!TensorType::isValidElementType(elementType))
383 return emitError(elementTypeLoc, "invalid tensor element type"), nullptr;
384
385 if (isUnranked) {
386 if (encoding)
387 return emitError("cannot apply encoding to unranked tensor"), nullptr;
388 return UnrankedTensorType::get(elementType);
389 }
390 return RankedTensorType::get(dimensions, elementType, encoding);
391}
392
393/// Parse a tuple type.
394///
395/// tuple-type ::= `tuple` `<` (type (`,` type)*)? `>`
396///
397Type Parser::parseTupleType() {
398 consumeToken(Token::kw_tuple);
399
400 // Parse the '<'.
401 if (parseToken(Token::less, "expected '<' in tuple type"))
402 return nullptr;
403
404 // Check for an empty tuple by directly parsing '>'.
405 if (consumeIf(Token::greater))
406 return TupleType::get(getContext());
407
408 // Parse the element types and the '>'.
409 SmallVector<Type, 4> types;
410 if (parseTypeListNoParens(types) ||
411 parseToken(Token::greater, "expected '>' in tuple type"))
412 return nullptr;
413
414 return TupleType::get(getContext(), types);
415}
416
417/// Parse a vector type.
418///
419/// vector-type ::= `vector` `<` vector-dim-list vector-element-type `>`
420/// vector-dim-list := (static-dim-list `x`)? (`[` static-dim-list `]` `x`)?
421/// static-dim-list ::= decimal-literal (`x` decimal-literal)*
422///
423VectorType Parser::parseVectorType() {
424 consumeToken(Token::kw_vector);
425
426 if (parseToken(Token::less, "expected '<' in vector type"))
427 return nullptr;
428
429 SmallVector<int64_t, 4> dimensions;
430 unsigned numScalableDims;
431 if (parseVectorDimensionList(dimensions, numScalableDims))
432 return nullptr;
433 if (any_of(dimensions, [](int64_t i) { return i <= 0; }))
434 return emitError(getToken().getLoc(),
435 "vector types must have positive constant sizes"),
436 nullptr;
437
438 // Parse the element type.
439 auto typeLoc = getToken().getLoc();
440 auto elementType = parseType();
441 if (!elementType || parseToken(Token::greater, "expected '>' in vector type"))
442 return nullptr;
443
444 if (!VectorType::isValidElementType(elementType))
445 return emitError(typeLoc, "vector elements must be int/index/float type"),
446 nullptr;
447
448 return VectorType::get(dimensions, elementType, numScalableDims);
449}
450
451/// Parse a dimension list in a vector type. This populates the dimension list,
452/// and returns the number of scalable dimensions in `numScalableDims`.
453///
454/// vector-dim-list := (static-dim-list `x`)? (`[` static-dim-list `]` `x`)?
455/// static-dim-list ::= decimal-literal (`x` decimal-literal)*
456///
457ParseResult
458Parser::parseVectorDimensionList(SmallVectorImpl<int64_t> &dimensions,
459 unsigned &numScalableDims) {
460 numScalableDims = 0;
461 // If there is a set of fixed-length dimensions, consume it
462 while (getToken().is(Token::integer)) {
463 int64_t value;
464 if (parseIntegerInDimensionList(value))
465 return failure();
466 dimensions.push_back(value);
467 // Make sure we have an 'x' or something like 'xbf32'.
468 if (parseXInDimensionList())
469 return failure();
470 }
471 // If there is a set of scalable dimensions, consume it
472 if (consumeIf(Token::l_square)) {
473 while (getToken().is(Token::integer)) {
474 int64_t value;
475 if (parseIntegerInDimensionList(value))
476 return failure();
477 dimensions.push_back(value);
478 numScalableDims++;
479 // Check if we have reached the end of the scalable dimension list
480 if (consumeIf(Token::r_square)) {
481 // Make sure we have something like 'xbf32'.
482 return parseXInDimensionList();
483 }
484 // Make sure we have an 'x'
485 if (parseXInDimensionList())
486 return failure();
487 }
488 // If we make it here, we've finished parsing the dimension list
489 // without finding ']' closing the set of scalable dimensions
490 return emitWrongTokenError(
491 "missing ']' closing set of scalable dimensions");
492 }
493
494 return success();
495}
496
497/// Parse a dimension list of a tensor or memref type. This populates the
498/// dimension list, using -1 for the `?` dimensions if `allowDynamic` is set and
499/// errors out on `?` otherwise. Parsing the trailing `x` is configurable.
500///
501/// dimension-list ::= eps | dimension (`x` dimension)*
502/// dimension-list-with-trailing-x ::= (dimension `x`)*
503/// dimension ::= `?` | decimal-literal
504///
505/// When `allowDynamic` is not set, this is used to parse:
506///
507/// static-dimension-list ::= eps | decimal-literal (`x` decimal-literal)*
508/// static-dimension-list-with-trailing-x ::= (dimension `x`)*
509ParseResult
510Parser::parseDimensionListRanked(SmallVectorImpl<int64_t> &dimensions,
511 bool allowDynamic, bool withTrailingX) {
512 auto parseDim = [&]() -> LogicalResult {
513 auto loc = getToken().getLoc();
514 if (consumeIf(Token::question)) {
1
Taking false branch
515 if (!allowDynamic)
516 return emitError(loc, "expected static shape");
517 dimensions.push_back(ShapedType::kDynamic);
518 } else {
519 int64_t value;
2
'value' declared without an initial value
520 if (failed(parseIntegerInDimensionList(value)))
3
Calling 'Parser::parseIntegerInDimensionList'
8
Returning from 'Parser::parseIntegerInDimensionList'
9
Taking false branch
521 return failure();
522 dimensions.push_back(value);
10
1st function call argument is an uninitialized value
523 }
524 return success();
525 };
526
527 if (withTrailingX) {
528 while (getToken().isAny(Token::integer, Token::question)) {
529 if (failed(parseDim()) || failed(parseXInDimensionList()))
530 return failure();
531 }
532 return success();
533 }
534
535 if (getToken().isAny(Token::integer, Token::question)) {
536 if (failed(parseDim()))
537 return failure();
538 while (getToken().is(Token::bare_identifier) &&
539 getTokenSpelling()[0] == 'x') {
540 if (failed(parseXInDimensionList()) || failed(parseDim()))
541 return failure();
542 }
543 }
544 return success();
545}
546
547ParseResult Parser::parseIntegerInDimensionList(int64_t &value) {
548 // Hexadecimal integer literals (starting with `0x`) are not allowed in
549 // aggregate type declarations. Therefore, `0xf32` should be processed as
550 // a sequence of separate elements `0`, `x`, `f32`.
551 if (getTokenSpelling().size() > 1 && getTokenSpelling()[1] == 'x') {
4
Assuming the condition is false
5
Taking false branch
552 // We can get here only if the token is an integer literal. Hexadecimal
553 // integer literals can only start with `0x` (`1x` wouldn't lex as a
554 // literal, just `1` would, at which point we don't get into this
555 // branch).
556 assert(getTokenSpelling()[0] == '0' && "invalid integer literal")(static_cast <bool> (getTokenSpelling()[0] == '0' &&
"invalid integer literal") ? void (0) : __assert_fail ("getTokenSpelling()[0] == '0' && \"invalid integer literal\""
, "mlir/lib/AsmParser/TypeParser.cpp", 556, __extension__ __PRETTY_FUNCTION__
))
;
557 value = 0;
558 state.lex.resetPointer(getTokenSpelling().data() + 1);
559 consumeToken();
560 } else {
561 // Make sure this integer value is in bound and valid.
562 Optional<uint64_t> dimension = getToken().getUInt64IntegerValue();
563 if (!dimension ||
6
Assuming the condition is true
564 *dimension > (uint64_t)std::numeric_limits<int64_t>::max())
565 return emitError("invalid dimension");
7
Returning without writing to 'value'
566 value = (int64_t)*dimension;
567 consumeToken(Token::integer);
568 }
569 return success();
570}
571
572/// Parse an 'x' token in a dimension list, handling the case where the x is
573/// juxtaposed with an element type, as in "xf32", leaving the "f32" as the next
574/// token.
575ParseResult Parser::parseXInDimensionList() {
576 if (getToken().isNot(Token::bare_identifier) || getTokenSpelling()[0] != 'x')
577 return emitWrongTokenError("expected 'x' in dimension list");
578
579 // If we had a prefix of 'x', lex the next token immediately after the 'x'.
580 if (getTokenSpelling().size() != 1)
581 state.lex.resetPointer(getTokenSpelling().data() + 1);
582
583 // Consume the 'x'.
584 consumeToken(Token::bare_identifier);
585
586 return success();
587}