diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -902,24 +902,7 @@ template void processKeyWithDefault(const char *Key, Optional &Val, const Optional &DefaultValue, bool Required, - Context &Ctx) { - assert(DefaultValue.hasValue() == false && - "Optional shouldn't have a value!"); - void *SaveInfo; - bool UseDefault = true; - const bool sameAsDefault = outputting() && !Val.hasValue(); - if (!outputting() && !Val.hasValue()) - Val = T(); - if (Val.hasValue() && - this->preflightKey(Key, Required, sameAsDefault, UseDefault, - SaveInfo)) { - yamlize(*this, Val.getValue(), Required, Ctx); - this->postflightKey(SaveInfo); - } else { - if (UseDefault) - Val = DefaultValue; - } - } + Context &Ctx); template void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue, @@ -1625,6 +1608,40 @@ StringRef PaddingBeforeContainer; }; +template +void IO::processKeyWithDefault(const char *Key, Optional &Val, + const Optional &DefaultValue, bool Required, + Context &Ctx) { + assert(DefaultValue.hasValue() == false && + "Optional shouldn't have a value!"); + void *SaveInfo; + bool UseDefault = true; + const bool sameAsDefault = outputting() && !Val.hasValue(); + if (!outputting() && !Val.hasValue()) + Val = T(); + if (Val.hasValue() && + this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { + + // When reading an Optional key from a YAML description, we allow the + // special "" value, which can be used to specify that no value was + // requested, i.e. the DefaultValue will be assigned. The DefaultValue is + // usually None. + bool IsNone = false; + if (!outputting()) + if (auto *Node = dyn_cast(((Input *)this)->getCurrentNode())) + IsNone = Node->getRawValue() == ""; + + if (IsNone) + Val = DefaultValue; + else + yamlize(*this, Val.getValue(), Required, Ctx); + this->postflightKey(SaveInfo); + } else { + if (UseDefault) + Val = DefaultValue; + } +} + /// YAML I/O does conversion based on types. But often native data types /// are just a typedef of built in intergral types (e.g. int). But the C++ /// type matching system sees through the typedef and all the typedefed types diff --git a/llvm/test/tools/yaml2obj/ELF/none-value.yaml b/llvm/test/tools/yaml2obj/ELF/none-value.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/none-value.yaml @@ -0,0 +1,45 @@ +## We have a special "" value for all keys that are implemented +## as Optional<> in the code. Setting a key to "" means no-op and +## works in the same way as when a field was not specified at all. + +## Test a few keys for which the "" value is supported. +## We do not test all possible keys, because it would be too verbose. +## It reasonable to test all keys for a section, because normally many +## of them would conflict or intersect when specified together. +# RUN: yaml2obj %s --docnum=1 -o %t-none +# RUN: yaml2obj %s --docnum=2 -o %t-base +# RUN: cmp %t-none %t-base + +## We do not use the TEST macro. It exists to +## demonstrate the expected use case for the word. +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .bar + Type: SHT_PROGBITS + Offset: [[TEST=]] + Address: [[TEST=]] + Content: [[TEST=]] + Size: [[TEST=]] + ContentArray: [[TEST=]] + Info: [[TEST=]] + EntSize: [[TEST=]] + ShName: [[TEST=]] + ShOffset: [[TEST=]] + ShSize: [[TEST=]] + ShFlags: [[TEST=]] + +## The same document, but all fields that were set to are removed. +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .bar + Type: SHT_PROGBITS