xref: /llvm-project/mlir/include/mlir/IR/Properties.td (revision 378e1793379c9c63a4265ecf55c47308410ed25d)
1//===-- Properties.td - Properties definition file ----------------*- tablegen -*-===//
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 is the base properties defination file.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef PROPERTIES
14#define PROPERTIES
15
16include "mlir/IR/Constraints.td"
17include "mlir/IR/Utils.td"
18
19// Base class for defining properties.
20class Property<string storageTypeParam = "", string desc = ""> {
21  // User-readable one line summary used in error reporting messages. If empty,
22  // a generic message will be used.
23  string summary = desc;
24  // The full description of this property.
25  string description = "";
26  code storageType = storageTypeParam;
27  code interfaceType = storageTypeParam;
28
29  // The expression to convert from the storage type to the Interface
30  // type. For example, an enum can be stored as an int but returned as an
31  // enum class.
32  //
33  // Format:
34  // - `$_storage` will contain the property in the storage type.
35  code convertFromStorage = "$_storage";
36
37  // The call expression to build a property storage from the interface type.
38  //
39  // Format:
40  // - `$_storage` will contain the property in the storage type.
41  // - `$_value` will contain the property in the user interface type.
42  code assignToStorage = "$_storage = $_value";
43
44  // The call expression to convert from the storage type to an attribute.
45  // The resulting attribute must be non-null in non-error cases.
46  //
47  // Format:
48  // - `$_storage` is the storage type value.
49  // - `$_ctxt` is a `MLIRContext *`.
50  //
51  // The expression must return an `Attribute` and will be used as a function body.
52  code convertToAttribute = [{
53    return convertToAttribute($_ctxt, $_storage);
54  }];
55
56  // The call expression to convert from an Attribute to the storage type.
57  //
58  // Format:
59  // - `$_storage` is a reference to a value of the storage type.
60  // - `$_attr` is the attribute.
61  // - `$_diag` is a callback to get a Diagnostic to emit error.
62  //
63  // The expression must return a LogicalResult and will be used as a function body
64  // or in other similar contexts.
65  code convertFromAttribute = [{
66    return convertFromAttribute($_storage, $_attr, $_diag);
67  }];
68
69  // The verification predicate for this property. Defaults to the true predicate,
70  // since properties are always their expected type.
71  // Within the predicate, `$_self` is an instance of the **interface**
72  // type of the property. Setting this field to ? will also result in a
73  // true predicate but is not recommended, as it breaks composability.
74  Pred predicate = TruePred;
75
76  // The call expression to hash the property.
77  //
78  // Format:
79  // - `$_storage` is the variable to hash.
80  //
81  // The expression should define a llvm::hash_code.
82  // If unspecified, defaults to `llvm::hash_value($_storage)`.
83  // The default is not specified in tablegen because many combinators, like
84  // ArrayProperty, can fall back to more efficient implementations of
85  // `hashProperty` when their underlying elements have trivial hashing.
86  code hashProperty = "";
87
88  // The body of the parser for a value of this property.
89  // Format:
90  // - `$_parser` is the OpAsmParser.
91  // - `$_storage` is the location into which the value is to be placed if it is
92  //  present.
93  // - `$_ctxt` is a `MLIRContext *`
94  //
95  // This defines the body of a function (typically a lambda) that returns a
96  // ParseResult. There is an implicit `return success()` at the end of the parser
97  // code.
98  //
99  // When this code executes, `$_storage` will be initialized to the property's
100  // default value (if any, accounting for the storage type override).
101  code parser = [{
102    auto value = ::mlir::FieldParser<}] # storageType # [{>::parse($_parser);
103    if (::mlir::failed(value))
104      return ::mlir::failure();
105    $_storage = std::move(*value);
106  }];
107
108  // The body of the parser for a value of this property as the anchor of an optional
109  // group. This should parse the property if possible and do nothing if a value of
110  // the relevant type is not next in the parse stream.
111  // You are not required to define this parser if it cannot be meaningfully
112  // implemented.
113  // This has the same context and substitutions as `parser` except that it is
114  // required to return an OptionalParseResult.
115  //
116  // If the optional parser doesn't parse anything, it should not set
117  // $_storage, since the parser doesn't know if the default value has been
118  // overwritten.
119  code optionalParser = "";
120
121  // The printer for a value of this property.
122  // Format:
123  // - `$_storage` is the storage data.
124  // - `$_printer` is the OpAsmPrinter instance.
125  // - `$_ctxt` is a `MLIRContext *`
126  //
127  // This may be called in an expression context, so variable declarations must
128  // be placed within a new scope.
129  //
130  // The printer for a property should always print a non-empty value - default value
131  // printing elision happens outside the context of this printing expression.
132  code printer = "$_printer << $_storage";
133
134  // The call expression to emit the storage type to bytecode.
135  //
136  // Format:
137  // - `$_storage` is the storage type value.
138  // - `$_writer` is a `DialectBytecodeWriter`.
139  // - `$_ctxt` is a `MLIRContext *`.
140  //
141  // This will become the body af a function returning void.
142  code writeToMlirBytecode = [{
143    writeToMlirBytecode($_writer, $_storage);
144  }];
145
146  // The call expression to read the storage type from bytecode.
147  //
148  // Format:
149  // - `$_storage` is the storage type value.
150  // - `$_reader` is a `DialectBytecodeReader`.
151  // - `$_ctxt` is a `MLIRContext *`.
152  //
153  // This will become the body of a function returning LogicalResult.
154  // There is an implicit `return success()` at the end of this function.
155  //
156  // When this code executes, `$_storage` will be initialized to the property's
157  // default value (if any, accounting for the storage type override).
158  code readFromMlirBytecode = [{
159    if (::mlir::failed(readFromMlirBytecode($_reader, $_storage)))
160      return ::mlir::failure();
161  }];
162
163  // Base definition for the property. Used to look through `OptionalProperty`
164  // for some format generation, as with the `baseAttr` field on attributes.
165  Property baseProperty = ?;
166
167  // Default value for the property within its storage. This should be an expression
168  // of type `interfaceType` and should be comparable with other types of that
169  // interface typ with `==`. The empty string means there is no default value.
170  string defaultValue = "";
171
172  // If set, the default value the storage of the property should be initilized to.
173  // This is only needed when the storage and interface types of the property
174  // are distinct (ex. SmallVector for storage vs. ArrayRef for interfacing), as it
175  // will fall back to `defaultValue` when unspecified.
176  string storageTypeValueOverride = "";
177}
178
179/// Implementation of the Property class's `readFromMlirBytecode` field using
180/// the default `convertFromAttribute` implementation.
181/// Users not wanting to implement their own `readFromMlirBytecode` and
182/// `writeToMlirBytecode` implementations can opt into using this implementation
183/// by writing:
184///
185/// let writeToMlirBytecode = writeMlirBytecodeWithConvertToAttribute;
186/// let readFromMlirBytecode = readMlirBytecodeUsingConvertFromAttribute;
187///
188/// in their property definition.
189/// Serialization and deserialization is performed using the attributes
190/// returned by `convertFromAttribute` and `convertToAttribute`.
191///
192/// WARNING: This implementation creates a less than optimal encoding.
193/// Users caring about optimal encoding should not use this implementation and
194/// implement `readFromMlirBytecode` and `writeToMlirBytecode` themselves.
195defvar readMlirBytecodeUsingConvertFromAttribute = [{
196  ::mlir::Attribute attr;
197  if (::mlir::failed($_reader.readAttribute(attr)))
198    return ::mlir::failure();
199  if (::mlir::failed(convertFromAttribute($_storage, attr, nullptr)))
200    return ::mlir::failure();
201}];
202
203/// Implementation of the Property class's `writeToMlirBytecode` field using
204/// the default `convertToAttribute` implementation.
205/// See description of `readMlirBytecodeUsingConvertFromAttribute` above for
206/// details.
207defvar writeMlirBytecodeWithConvertToAttribute = [{
208  $_writer.writeAttribute(convertToAttribute($_ctxt, $_storage))
209}];
210
211//===----------------------------------------------------------------------===//
212// Primitive property kinds
213
214// Any kind of integer stored as properties.
215class IntProp<string storageTypeParam, string desc = ""> :
216    Property<storageTypeParam, desc> {
217  let summary = !if(!empty(desc), storageTypeParam, desc);
218  let optionalParser = [{
219    return $_parser.parseOptionalInteger($_storage);
220  }];
221  let writeToMlirBytecode = [{
222    $_writer.writeVarInt($_storage);
223  }];
224  let readFromMlirBytecode = [{
225    uint64_t val;
226    if (failed($_reader.readVarInt(val)))
227      return ::mlir::failure();
228    $_storage = val;
229  }];
230}
231
232class IntProperty<string storageTypeParam, string desc = "">
233  : IntProp<storageTypeParam, desc>, Deprecated<"moved to the shorter name IntProp">;
234
235def I32Prop : IntProp<"int32_t">;
236def I64Prop : IntProp<"int64_t">;
237
238def I32Property : IntProp<"int32_t">, Deprecated<"moved to shorter name I32Prop">;
239def I64Property : IntProp<"int64_t">, Deprecated<"moved to shorter name I64Prop">;
240
241class EnumProp<string storageTypeParam, string desc = "", string default = ""> :
242    Property<storageTypeParam, desc> {
243  // TODO:  implement predicate for enum validity.
244  let writeToMlirBytecode = [{
245    $_writer.writeVarInt(static_cast<uint64_t>($_storage));
246  }];
247  let readFromMlirBytecode = [{
248    uint64_t val;
249    if (failed($_reader.readVarInt(val)))
250      return ::mlir::failure();
251    $_storage = static_cast<}] # storageTypeParam # [{>(val);
252  }];
253  let defaultValue = default;
254}
255
256class EnumProperty<string storageTypeParam, string desc = "", string default = ""> :
257  EnumProp<storageTypeParam, desc, default>,
258  Deprecated<"moved to shorter name EnumProp">;
259
260// Note: only a class so we can deprecate the old name
261class _cls_StringProp : Property<"std::string", "string"> {
262  let interfaceType = "::llvm::StringRef";
263  let convertFromStorage = "::llvm::StringRef{$_storage}";
264  let assignToStorage = "$_storage = $_value.str()";
265  let optionalParser = [{
266    if (::mlir::failed($_parser.parseOptionalString(&$_storage)))
267      return std::nullopt;
268  }];
269  let printer = "$_printer.printString($_storage)";
270  let readFromMlirBytecode = [{
271    StringRef val;
272    if (::mlir::failed($_reader.readString(val)))
273      return ::mlir::failure();
274    $_storage = val.str();
275  }];
276  let writeToMlirBytecode = [{
277    $_writer.writeOwnedString($_storage);
278  }];
279}
280def StringProp : _cls_StringProp;
281def StringProperty : _cls_StringProp, Deprecated<"moved to shorter name StringProp">;
282
283// Note: only a class so we can deprecate the old name
284class _cls_BoolProp : IntProp<"bool", "boolean"> {
285  let printer = [{ $_printer << ($_storage ? "true" : "false") }];
286  let readFromMlirBytecode = [{
287    return $_reader.readBool($_storage);
288  }];
289  let writeToMlirBytecode = [{
290    $_writer.writeOwnedBool($_storage);
291  }];
292}
293def BoolProp : _cls_BoolProp;
294def BoolProperty : _cls_BoolProp, Deprecated<"moved to shorter name BoolProp">;
295
296// Note: only a class so we can deprecate the old name
297class _cls_UnitProp : Property<"bool", "unit property"> {
298  let summary = "unit property";
299  let description = [{
300    A property whose presence or abscence is used as a flag.
301
302    This is stored as a boolean that defaults to false, and is named UnitProperty
303    by analogy with UnitAttr, which has the more comprehensive rationale and
304    explains the less typical syntax.
305
306    Note that this attribute does have a syntax for the false case to allow for its
307    use in contexts where default values shouldn't be elided.
308  }];
309  let defaultValue = "false";
310
311  let convertToAttribute = [{
312    if ($_storage)
313      return ::mlir::UnitAttr::get($_ctxt);
314    else
315      return ::mlir::BoolAttr::get($_ctxt, false);
316  }];
317  let convertFromAttribute = [{
318    if (::llvm::isa<::mlir::UnitAttr>($_attr)) {
319      $_storage = true;
320      return ::mlir::success();
321    }
322    if (auto boolAttr = ::llvm::dyn_cast<::mlir::BoolAttr>($_attr)) {
323      $_storage = boolAttr.getValue();
324      return ::mlir::success();
325    }
326    return ::mlir::failure();
327  }];
328
329  let parser = [{
330    ::llvm::StringRef keyword;
331    if (::mlir::failed($_parser.parseOptionalKeyword(&keyword,
332        {"unit", "unit_absent"})))
333      return $_parser.emitError($_parser.getCurrentLocation(),
334        "expected 'unit' or 'unit_absent'");
335    $_storage = (keyword == "unit");
336  }];
337
338  let optionalParser = [{
339    ::llvm::StringRef keyword;
340    if (::mlir::failed($_parser.parseOptionalKeyword(&keyword,
341        {"unit", "unit_absent"})))
342      return std::nullopt;
343    $_storage = (keyword == "unit");
344  }];
345
346  let printer = [{
347    $_printer << ($_storage ? "unit" : "unit_absent")
348  }];
349
350  let writeToMlirBytecode = [{
351    $_writer.writeOwnedBool($_storage);
352  }];
353  let readFromMlirBytecode = [{
354    if (::mlir::failed($_reader.readBool($_storage)))
355      return ::mlir::failure();
356  }];
357}
358def UnitProp : _cls_UnitProp;
359def UnitProperty : _cls_UnitProp, Deprecated<"moved to shorter name UnitProp">;
360
361//===----------------------------------------------------------------------===//
362// Property field overwrites
363
364/// Class for giving a property a default value.
365/// This doesn't change anything about the property other than giving it a default
366/// which can be used by ODS to elide printing.
367class DefaultValuedProp<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> {
368  let defaultValue = default;
369  let storageTypeValueOverride = storageDefault;
370  let baseProperty = p;
371  // Keep up to date with `Property` above.
372  let summary = p.summary;
373  let description = p.description;
374  let storageType = p.storageType;
375  let interfaceType = p.interfaceType;
376  let convertFromStorage = p.convertFromStorage;
377  let assignToStorage = p.assignToStorage;
378  let convertToAttribute = p.convertToAttribute;
379  let convertFromAttribute = p.convertFromAttribute;
380  let predicate = p.predicate;
381  let hashProperty = p.hashProperty;
382  let parser = p.parser;
383  let optionalParser = p.optionalParser;
384  let printer = p.printer;
385  let readFromMlirBytecode = p.readFromMlirBytecode;
386  let writeToMlirBytecode = p.writeToMlirBytecode;
387}
388class DefaultValuedProperty<Property p, string default = "", string storageDefault = "">
389  : DefaultValuedProp<p, default, storageDefault>, Deprecated<"moved to shorter name DefaultValuedProp">;
390
391/// Apply the predicate `pred` to the property `p`, ANDing it with any
392/// predicates it may already have. If `newSummary` is provided, replace the
393/// summary of `p` with `newSummary`.
394class ConfinedProp<Property p, Pred pred, string newSummary = "">
395    : Property<p.storageType, !if(!empty(newSummary), p.summary, newSummary)> {
396  let predicate = !if(!ne(p.predicate, TruePred), And<[p.predicate, pred]>, pred);
397  let baseProperty = p;
398  // Keep up to date with `Property` above.
399  let description = p.description;
400  let storageType = p.storageType;
401  let interfaceType = p.interfaceType;
402  let convertFromStorage = p.convertFromStorage;
403  let assignToStorage = p.assignToStorage;
404  let convertToAttribute = p.convertToAttribute;
405  let convertFromAttribute = p.convertFromAttribute;
406  let hashProperty = p.hashProperty;
407  let parser = p.parser;
408  let optionalParser = p.optionalParser;
409  let printer = p.printer;
410  let readFromMlirBytecode = p.readFromMlirBytecode;
411  let writeToMlirBytecode = p.writeToMlirBytecode;
412  let defaultValue = p.defaultValue;
413  let storageTypeValueOverride = p.storageTypeValueOverride;
414}
415
416class ConfinedProperty<Property p, Pred pred, string newSummary = "">
417  : ConfinedProp<p, pred, newSummary>,
418    Deprecated<"moved to shorter name ConfinedProp">;
419
420//===----------------------------------------------------------------------===//
421// Primitive property combinators
422
423/// Create a variable named `name` of `prop`'s storage type that is initialized
424/// to the correct default value, if there is one.
425class _makePropStorage<Property prop, string name> {
426  code ret = prop.storageType # " " # name
427      # !cond(!not(!empty(prop.storageTypeValueOverride)) : " = " # prop.storageTypeValueOverride,
428        !not(!empty(prop.defaultValue)) : " = " # prop.defaultValue,
429        true : "") # ";";
430}
431
432/// Construct a `Pred`icate `ret` that wraps the predicate of the underlying
433/// property `childProp` with:
434///
435///   [](childProp.storageType& s) {
436///     return [](childProp.interfaceType i) {
437///       return leafSubst(childProp.predicate, "$_self" to "i");
438///     }(childProp.convertFromStorage(s))
439///   }
440///
441/// and then appends `prefix` and `suffix`.
442class _makeStorageWrapperPred<Property wrappedProp> {
443  Pred ret =
444    Concat<
445      "[](" # "const " # wrappedProp.storageType
446        # "& baseStore) -> bool { return []("
447        # wrappedProp.interfaceType # " baseIface) -> bool { return (",
448      SubstLeaves<"$_self", "baseIface", wrappedProp.predicate>,
449      "); }(" # !subst("$_storage", "baseStore", wrappedProp.convertFromStorage)
450        # "); }">;
451}
452
453/// The generic class for arrays of some other property, which is stored as a
454/// `SmallVector` of that property. This uses an `ArrayAttr` as its attribute form
455/// though subclasses can override this, as is the case with IntArrayAttr below.
456/// Those wishing to use a non-default number of SmallVector elements should
457/// subclass `ArrayProp`.
458class ArrayProp<Property elem = Property<>, string newSummary = ""> :
459  Property<"::llvm::SmallVector<" # elem.storageType # ">",
460    !if(!empty(newSummary), "array of " # elem.summary, newSummary)> {
461  let interfaceType = "::llvm::ArrayRef<" # elem.storageType # ">";
462  let convertFromStorage = "::llvm::ArrayRef<" # elem.storageType # ">{$_storage}";
463  let assignToStorage = "$_storage.assign($_value.begin(), $_value.end())";
464
465  let convertFromAttribute = [{
466    auto arrayAttr = ::llvm::dyn_cast_if_present<::mlir::ArrayAttr>($_attr);
467    if (!arrayAttr)
468      return $_diag() << "expected array attribute";
469    for (::mlir::Attribute elemAttr : arrayAttr) {
470      }] # _makePropStorage<elem, "elemVal">.ret # [{
471      auto elemRes = [&](Attribute propAttr, }] # elem.storageType # [{& propStorage) -> ::mlir::LogicalResult {
472        }] # !subst("$_attr", "propAttr",
473          !subst("$_storage", "propStorage", elem.convertFromAttribute)) # [{
474      }(elemAttr, elemVal);
475      if (::mlir::failed(elemRes))
476        return ::mlir::failure();
477      $_storage.push_back(std::move(elemVal));
478    }
479    return ::mlir::success();
480  }];
481
482  let convertToAttribute = [{
483    SmallVector<Attribute> elems;
484    for (const auto& elemVal : $_storage) {
485      auto elemAttr = [&](const }] # elem.storageType #[{& propStorage) -> ::mlir::Attribute {
486        }] # !subst("$_storage", "propStorage", elem.convertToAttribute) # [{
487      }(elemVal);
488      elems.push_back(elemAttr);
489    }
490    return ::mlir::ArrayAttr::get($_ctxt, elems);
491  }];
492
493  let predicate = !if(!eq(elem.predicate, TruePred),
494    TruePred,
495    Concat<"::llvm::all_of($_self, ", _makeStorageWrapperPred<elem>.ret, ")">);
496
497  defvar theParserBegin = [{
498    auto& storage = $_storage;
499    auto parseElemFn = [&]() -> ::mlir::ParseResult {
500      }] # _makePropStorage<elem, "elemVal">.ret # [{
501      auto elemParse = [&](}] # elem.storageType # [{& propStorage) -> ::mlir::ParseResult {
502        }] # !subst("$_storage", "propStorage", elem.parser) # [{
503        return ::mlir::success();
504       }(elemVal);
505      if (::mlir::failed(elemParse))
506        return ::mlir::failure();
507      storage.push_back(std::move(elemVal));
508      return ::mlir::success();
509    };
510    }];
511  let parser = theParserBegin # [{
512    return $_parser.parseCommaSeparatedList(
513      ::mlir::OpAsmParser::Delimiter::Square, parseElemFn);
514  }];
515  // Hack around the lack of a peek method
516  let optionalParser = theParserBegin # [{
517    auto oldLoc = $_parser.getCurrentLocation();
518    auto parseResult = $_parser.parseCommaSeparatedList(
519      ::mlir::OpAsmParser::Delimiter::OptionalSquare, parseElemFn);
520    if (::mlir::failed(parseResult))
521      return ::mlir::failure();
522    auto newLoc = $_parser.getCurrentLocation();
523    if (oldLoc == newLoc)
524      return std::nullopt;
525    return ::mlir::success();
526  }];
527
528  let printer = [{ [&](){
529    $_printer << "[";
530    auto elemPrinter = [&](const }] # elem.storageType # [{& elemVal) {
531      }] # !subst("$_storage", "elemVal", elem.printer) #[{;
532    };
533    ::llvm::interleaveComma($_storage, $_printer, elemPrinter);
534    $_printer << "]";
535  }()}];
536
537  let readFromMlirBytecode = [{
538    uint64_t length;
539    if (::mlir::failed($_reader.readVarInt(length)))
540      return ::mlir::failure();
541    $_storage.reserve(length);
542    for (uint64_t i = 0; i < length; ++i) {
543      }]# _makePropStorage<elem, "elemVal">.ret # [{
544      auto elemRead = [&](}] # elem.storageType # [{& propStorage) -> ::mlir::LogicalResult {
545        }] # !subst("$_storage", "propStorage", elem.readFromMlirBytecode) # [{;
546        return ::mlir::success();
547      }(elemVal);
548      if (::mlir::failed(elemRead))
549        return ::mlir::failure();
550      $_storage.push_back(std::move(elemVal));
551    }
552  }];
553
554  let writeToMlirBytecode = [{
555    $_writer.writeVarInt($_storage.size());
556    for (const auto& elemVal : $_storage) {
557      [&]() {
558        }] # !subst("$_storage", "elemVal", elem.writeToMlirBytecode) #[{;
559      }();
560    }
561  }];
562
563  // There's no hash_value for SmallVector<T>, so we construct the ArrayRef ourselves.
564  // In the non-trivial case, we define a mapped range to get internal hash
565  // codes.
566  let hashProperty = !if(!empty(elem.hashProperty),
567    [{::llvm::hash_value(::llvm::ArrayRef<}] # elem.storageType # [{>{$_storage})}],
568    [{[&]() -> ::llvm::hash_code {
569        auto getElemHash = [](const auto& propStorage) -> ::llvm::hash_code {
570          return }] # !subst("$_storage", "propStorage", elem.hashProperty) # [{;
571        };
572        auto mapped = ::llvm::map_range($_storage, getElemHash);
573        return ::llvm::hash_combine_range(mapped.begin(), mapped.end());
574      }()
575    }]);
576}
577class ArrayProperty<Property elem = Property<>, string newSummary = "">
578  : ArrayProp<elem, newSummary>, Deprecated<"moved to shorter name ArrayProp">;
579
580class IntArrayProp<Property elem, string newSummary=""> :
581    ArrayProp<elem, newSummary> {
582  // Bring back the trivial conversions we don't get in the general case.
583  let convertFromAttribute = [{
584    return convertFromAttribute($_storage, $_attr, $_diag);
585  }];
586  let convertToAttribute = [{
587    return convertToAttribute($_ctxt, $_storage);
588  }];
589}
590class IntArrayProperty<Property elem, string newSummary="">
591  : IntArrayProp<elem, newSummary>, Deprecated<"moved to shorter name IntArrayProp">;
592
593/// An optional property, stored as an std::optional<p.storageType>
594/// interfaced with as an std::optional<p.interfaceType>..
595/// The syntax is `none` (or empty string if elided) for an absent value or
596/// `some<[underlying property]>` when a value is set.
597///
598/// As a special exception, if the underlying property has an optional parser and
599/// no default value (ex. an integer property), the printer will skip the `some`
600/// bracketing and delegate to the optional parser. In that case, the syntax is the
601/// syntax of the underlying property, or the keyword `none` in the rare cases that
602/// it is needed. This behavior can be disabled by setting `canDelegateParsing` to 0.
603class OptionalProp<Property p, bit canDelegateParsing = 1>
604    : Property<"std::optional<" # p.storageType # ">", "optional " # p.summary> {
605
606  // In the cases where the underlying attribute is plain old data that's passed by
607  // value, the conversion code is trivial.
608  defvar hasTrivialStorage = !and(!eq(p.convertFromStorage, "$_storage"),
609    !eq(p.assignToStorage, "$_storage = $_value"),
610    !eq(p.storageType, p.interfaceType));
611
612  defvar delegatesParsing = !and(!empty(p.defaultValue),
613    !not(!empty(p.optionalParser)), canDelegateParsing);
614
615  let interfaceType = "std::optional<" # p.interfaceType # ">";
616  let defaultValue = "std::nullopt";
617
618  let convertFromStorage = !if(hasTrivialStorage,
619    p.convertFromStorage,
620    [{($_storage.has_value() ? std::optional<}] # p.interfaceType # ">{"
621      # !subst("$_storage", "(*($_storage))", p.convertFromStorage)
622      # [{} : std::nullopt)}]);
623  let assignToStorage = !if(hasTrivialStorage,
624    p.assignToStorage,
625    [{[&]() {
626      if (!$_value.has_value()) {
627        $_storage = std::nullopt;
628        return;
629      }
630      }] # _makePropStorage<p, "presentVal">.ret # [{
631      [&](}] # p.storageType # [{& propStorage) {
632        }] # !subst("$_storage", "propStorage",
633          !subst("$_value", "(*($_value))", p.assignToStorage)) # [{;
634      }(presentVal);
635      $_storage = std::move(presentVal);
636    }()}]);
637
638  let convertFromAttribute = [{
639    auto arrayAttr = ::llvm::dyn_cast<::mlir::ArrayAttr>($_attr);
640    if (!arrayAttr)
641      return $_diag() << "expected optional properties to materialize as arrays";
642    if (arrayAttr.size() > 1)
643      return $_diag() << "expected optional properties to become 0- or 1-element arrays";
644    if (arrayAttr.empty()) {
645      $_storage = std::nullopt;
646      return ::mlir::success();
647    }
648    ::mlir::Attribute presentAttr = arrayAttr[0];
649    }] # _makePropStorage<p, "presentVal">.ret # [{
650    auto presentRes = [&](Attribute propAttr, }] # p.storageType # [{& propStorage) -> ::mlir::LogicalResult {
651      }] # !subst("$_storage", "propStorage",
652          !subst("$_attr", "propAttr", p.convertFromAttribute)) # [{
653    }(presentAttr, presentVal);
654    if (::mlir::failed(presentRes))
655      return ::mlir::failure();
656    $_storage = std::move(presentVal);
657    return ::mlir::success();
658  }];
659
660  let convertToAttribute = [{
661    if (!$_storage.has_value()) {
662      return ::mlir::ArrayAttr::get($_ctxt, {});
663    }
664    auto attr = [&]() -> ::mlir::Attribute {
665      }] # !subst("$_storage", "(*($_storage))", p.convertToAttribute) # [{
666    }();
667    return ::mlir::ArrayAttr::get($_ctxt, {attr});
668  }];
669
670  let predicate = !if(!ne(p.predicate, TruePred),
671    Or<[CPred<"!$_self.has_value()">,
672        SubstLeaves<"$_self", "(*($_self))", p.predicate>]>,
673    TruePred);
674
675  defvar delegatedParserBegin = [{
676    if (::mlir::succeeded($_parser.parseOptionalKeyword("none"))) {
677      $_storage = std::nullopt;
678      return ::mlir::success();
679    }
680    }] #_makePropStorage<p, "presentVal">.ret # [{
681    auto delegParseResult = [&](}] # p.storageType # [{& propStorage) -> ::mlir::OptionalParseResult {
682    }] # !subst("$_storage", "propStorage", p.optionalParser) # [{
683        return ::mlir::success();
684    }(presentVal);
685    if (!delegParseResult.has_value()) {
686  }];
687
688  defvar delegatedParserEnd = [{
689    }
690    if (delegParseResult.has_value() && ::mlir::failed(*delegParseResult))
691      return ::mlir::failure();
692    $_storage = std::move(presentVal);
693    return ::mlir::success();
694  }];
695  // If we're being explicitly called for our parser, we're expecting to have been
696  // printede into a context where the default value isn't elided. Therefore,
697  // not-present from the underlying parser is a failure.
698  defvar delegatedParser = delegatedParserBegin # [{
699    return ::mlir::failure();
700  }] # delegatedParserEnd;
701  defvar delegatedOptionalParser = delegatedParserBegin # [{
702      return std::nullopt;
703  }] # delegatedParserEnd;
704
705  defvar generalParserBegin = [{
706    ::llvm::StringRef keyword;
707    if (::mlir::failed($_parser.parseOptionalKeyword(&keyword, {"none", "some"}))) {
708  }];
709  defvar generalParserEnd = [{
710    }
711    if (keyword == "none") {
712      $_storage = std::nullopt;
713      return ::mlir::success();
714    }
715    if (::mlir::failed($_parser.parseLess()))
716      return ::mlir::failure();
717    }] # _makePropStorage<p, "presentVal">.ret # [{
718    auto presentParse = [&](}] # p.storageType # [{& propStorage) -> ::mlir::ParseResult {
719      }] # !subst("$_storage", "propStorage", p.parser) # [{
720      return ::mlir::success();
721    }(presentVal);
722    if (presentParse || $_parser.parseGreater())
723      return ::mlir::failure();
724    $_storage = std::move(presentVal);
725  }];
726  defvar generalParser = generalParserBegin # [{
727    return $_parser.emitError($_parser.getCurrentLocation(), "expected 'none' or 'some<prop>'");
728  }] # generalParserEnd;
729  defvar generalOptionalParser = generalParserBegin # [{
730    return std::nullopt;
731  }] # generalParserEnd;
732
733  let parser = !if(delegatesParsing, delegatedParser, generalParser);
734  let optionalParser = !if(delegatesParsing,
735    delegatedOptionalParser, generalOptionalParser);
736
737  defvar delegatedPrinter = [{
738    [&]() {
739      if (!$_storage.has_value()) {
740        $_printer << "none";
741        return;
742      }
743      }] # !subst("$_storage", "(*($_storage))", p.printer) # [{;
744    }()}];
745  defvar generalPrinter = [{
746      [&]() {
747        if (!$_storage.has_value()) {
748          $_printer << "none";
749          return;
750        }
751        $_printer << "some<";
752        }] # !subst("$_storage", "(*($_storage))", p.printer) # [{;
753        $_printer << ">";
754      }()}];
755  let printer = !if(delegatesParsing, delegatedPrinter, generalPrinter);
756
757  let readFromMlirBytecode = [{
758    bool isPresent = false;
759    if (::mlir::failed($_reader.readBool(isPresent)))
760      return ::mlir::failure();
761    if (!isPresent) {
762      $_storage = std::nullopt;
763      return ::mlir::success();
764    }
765    }] # _makePropStorage<p, "presentVal">.ret # [{
766    auto presentResult = [&](}] # p.storageType # [{& propStorage) -> ::mlir::LogicalResult {
767      }] # !subst("$_storage", "propStorage", p.readFromMlirBytecode) # [{;
768      return ::mlir::success();
769    }(presentVal);
770    if (::mlir::failed(presentResult))
771      return ::mlir::failure();
772    $_storage = std::move(presentVal);
773  }];
774  let writeToMlirBytecode = [{
775    $_writer.writeOwnedBool($_storage.has_value());
776    if (!$_storage.has_value())
777      return;
778  }] # !subst("$_storage", "(*($_storage))", p.writeToMlirBytecode);
779
780  let hashProperty = !if(!empty(p.hashProperty), p.hashProperty,
781    [{ ::llvm::hash_value($_storage.has_value() ? std::optional<::llvm::hash_code>{}] #
782      !subst("$_storage", "(*($_storage))", p.hashProperty) #[{} : std::nullopt) }]);
783  assert !or(!not(delegatesParsing), !eq(defaultValue, "std::nullopt")),
784    "For delegated parsing to be used, the default value must be nullopt. " #
785    "To use a non-trivial default, set the canDelegateParsing argument to 0";
786}
787class OptionalProperty<Property p, bit canDelegateParsing = 1>
788  : OptionalProp<p, canDelegateParsing>,
789    Deprecated<"moved to shorter name OptionalProp">;
790#endif // PROPERTIES
791