1 //===- Multilib.cpp - Multilib 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 #include "clang/Driver/Multilib.h"
10 #include "clang/Basic/LLVM.h"
11 #include "clang/Driver/Driver.h"
12 #include "llvm/ADT/DenseSet.h"
13 #include "llvm/ADT/SmallSet.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Support/Compiler.h"
16 #include "llvm/Support/ErrorHandling.h"
17 #include "llvm/Support/Regex.h"
18 #include "llvm/Support/VersionTuple.h"
19 #include "llvm/Support/YAMLParser.h"
20 #include "llvm/Support/YAMLTraits.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include <algorithm>
23 #include <cassert>
24 #include <string>
25
26 using namespace clang;
27 using namespace driver;
28 using namespace llvm::sys;
29
Multilib(StringRef GCCSuffix,StringRef OSSuffix,StringRef IncludeSuffix,const flags_list & Flags,StringRef ExclusiveGroup,std::optional<StringRef> Error)30 Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
31 StringRef IncludeSuffix, const flags_list &Flags,
32 StringRef ExclusiveGroup, std::optional<StringRef> Error)
33 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
34 Flags(Flags), ExclusiveGroup(ExclusiveGroup), Error(Error) {
35 assert(GCCSuffix.empty() ||
36 (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
37 assert(OSSuffix.empty() ||
38 (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
39 assert(IncludeSuffix.empty() ||
40 (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
41 }
42
dump() const43 LLVM_DUMP_METHOD void Multilib::dump() const {
44 print(llvm::errs());
45 }
46
print(raw_ostream & OS) const47 void Multilib::print(raw_ostream &OS) const {
48 if (GCCSuffix.empty())
49 OS << ".";
50 else {
51 OS << StringRef(GCCSuffix).drop_front();
52 }
53 OS << ";";
54 for (StringRef Flag : Flags) {
55 if (Flag.front() == '-')
56 OS << "@" << Flag.substr(1);
57 }
58 }
59
operator ==(const Multilib & Other) const60 bool Multilib::operator==(const Multilib &Other) const {
61 // Check whether the flags sets match
62 // allowing for the match to be order invariant
63 llvm::StringSet<> MyFlags(llvm::from_range, Flags);
64
65 for (const auto &Flag : Other.Flags)
66 if (!MyFlags.contains(Flag))
67 return false;
68
69 if (osSuffix() != Other.osSuffix())
70 return false;
71
72 if (gccSuffix() != Other.gccSuffix())
73 return false;
74
75 if (includeSuffix() != Other.includeSuffix())
76 return false;
77
78 return true;
79 }
80
operator <<(raw_ostream & OS,const Multilib & M)81 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
82 M.print(OS);
83 return OS;
84 }
85
FilterOut(FilterCallback F)86 MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
87 llvm::erase_if(Multilibs, F);
88 return *this;
89 }
90
push_back(const Multilib & M)91 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
92
DiagnoseUnclaimedMultilibCustomFlags(const Driver & D,const SmallVector<StringRef> & UnclaimedCustomFlagValues,const SmallVector<custom_flag::Declaration> & CustomFlagDecls)93 static void DiagnoseUnclaimedMultilibCustomFlags(
94 const Driver &D, const SmallVector<StringRef> &UnclaimedCustomFlagValues,
95 const SmallVector<custom_flag::Declaration> &CustomFlagDecls) {
96 struct EditDistanceInfo {
97 StringRef FlagValue;
98 unsigned EditDistance;
99 };
100 const unsigned MaxEditDistance = 5;
101
102 for (StringRef Unclaimed : UnclaimedCustomFlagValues) {
103 std::optional<EditDistanceInfo> BestCandidate;
104 for (const auto &Decl : CustomFlagDecls) {
105 for (const auto &Value : Decl.ValueList) {
106 const std::string &FlagValueName = Value.Name;
107 unsigned EditDistance =
108 Unclaimed.edit_distance(FlagValueName, /*AllowReplacements=*/true,
109 /*MaxEditDistance=*/MaxEditDistance);
110 if (!BestCandidate || (EditDistance <= MaxEditDistance &&
111 EditDistance < BestCandidate->EditDistance)) {
112 BestCandidate = {FlagValueName, EditDistance};
113 }
114 }
115 }
116 if (!BestCandidate)
117 D.Diag(clang::diag::err_drv_unsupported_opt)
118 << (custom_flag::Prefix + Unclaimed).str();
119 else
120 D.Diag(clang::diag::err_drv_unsupported_opt_with_suggestion)
121 << (custom_flag::Prefix + Unclaimed).str()
122 << (custom_flag::Prefix + BestCandidate->FlagValue).str();
123 }
124 }
125
126 namespace clang::driver::custom_flag {
127 // Map implemented using linear searches as the expected size is too small for
128 // the overhead of a search tree or a hash table.
129 class ValueNameToDetailMap {
130 SmallVector<std::pair<StringRef, const ValueDetail *>> Mapping;
131
132 public:
133 template <typename It>
ValueNameToDetailMap(It FlagDeclsBegin,It FlagDeclsEnd)134 ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) {
135 for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) {
136 const Declaration &Decl = *DeclIt;
137 for (const auto &Value : Decl.ValueList)
138 Mapping.emplace_back(Value.Name, &Value);
139 }
140 }
141
get(StringRef Key) const142 const ValueDetail *get(StringRef Key) const {
143 auto Iter = llvm::find_if(
144 Mapping, [&](const auto &Pair) { return Pair.first == Key; });
145 return Iter != Mapping.end() ? Iter->second : nullptr;
146 }
147 };
148 } // namespace clang::driver::custom_flag
149
150 std::pair<Multilib::flags_list, SmallVector<StringRef>>
processCustomFlags(const Driver & D,const Multilib::flags_list & Flags) const151 MultilibSet::processCustomFlags(const Driver &D,
152 const Multilib::flags_list &Flags) const {
153 Multilib::flags_list Result;
154 SmallVector<StringRef> MacroDefines;
155
156 // Custom flag values detected in the flags list
157 SmallVector<const custom_flag::ValueDetail *> ClaimedCustomFlagValues;
158
159 // Arguments to -fmultilib-flag=<arg> that don't correspond to any valid
160 // custom flag value. An error will be printed out for each of these.
161 SmallVector<StringRef> UnclaimedCustomFlagValueStrs;
162
163 const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
164 CustomFlagDecls.begin(), CustomFlagDecls.end());
165
166 for (StringRef Flag : Flags) {
167 if (!Flag.starts_with(custom_flag::Prefix)) {
168 Result.push_back(Flag.str());
169 continue;
170 }
171
172 StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size());
173 const custom_flag::ValueDetail *Detail =
174 ValueNameToValueDetail.get(CustomFlagValueStr);
175 if (Detail)
176 ClaimedCustomFlagValues.push_back(Detail);
177 else
178 UnclaimedCustomFlagValueStrs.push_back(CustomFlagValueStr);
179 }
180
181 // Set of custom flag declarations for which a value was passed in the flags
182 // list. This is used to, firstly, detect multiple values for the same flag
183 // declaration (in this case, the last one wins), and secondly, to detect
184 // which declarations had no value passed in (in this case, the default value
185 // is selected).
186 llvm::SmallPtrSet<custom_flag::Declaration *, 32> TriggeredCustomFlagDecls;
187
188 // Detect multiple values for the same flag declaration. Last one wins.
189 for (auto *CustomFlagValue : llvm::reverse(ClaimedCustomFlagValues)) {
190 if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second)
191 continue;
192 Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name);
193 if (CustomFlagValue->MacroDefines)
194 MacroDefines.append(CustomFlagValue->MacroDefines->begin(),
195 CustomFlagValue->MacroDefines->end());
196 }
197
198 // Detect flag declarations with no value passed in. Select default value.
199 for (const auto &Decl : CustomFlagDecls) {
200 if (TriggeredCustomFlagDecls.contains(&Decl))
201 continue;
202 const custom_flag::ValueDetail &CustomFlagValue =
203 Decl.ValueList[*Decl.DefaultValueIdx];
204 Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue.Name);
205 if (CustomFlagValue.MacroDefines)
206 MacroDefines.append(CustomFlagValue.MacroDefines->begin(),
207 CustomFlagValue.MacroDefines->end());
208 }
209
210 DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs,
211 CustomFlagDecls);
212
213 return {Result, MacroDefines};
214 }
215
select(const Driver & D,const Multilib::flags_list & Flags,llvm::SmallVectorImpl<Multilib> & Selected,llvm::SmallVector<StringRef> * CustomFlagMacroDefines) const216 bool MultilibSet::select(
217 const Driver &D, const Multilib::flags_list &Flags,
218 llvm::SmallVectorImpl<Multilib> &Selected,
219 llvm::SmallVector<StringRef> *CustomFlagMacroDefines) const {
220 auto [FlagsWithCustom, CFMacroDefines] = processCustomFlags(D, Flags);
221 llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom));
222 Selected.clear();
223 bool AnyErrors = false;
224
225 // Determining the list of macro defines depends only on the custom flags
226 // passed in. The library variants actually selected are not relevant in
227 // this. Therefore this assignment can take place before the selection
228 // happens.
229 if (CustomFlagMacroDefines)
230 *CustomFlagMacroDefines = std::move(CFMacroDefines);
231
232 // Decide which multilibs we're going to select at all.
233 llvm::DenseSet<StringRef> ExclusiveGroupsSelected;
234 for (const Multilib &M : llvm::reverse(Multilibs)) {
235 // If this multilib doesn't match all our flags, don't select it.
236 if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) {
237 return FlagSet.contains(F);
238 }))
239 continue;
240
241 const std::string &group = M.exclusiveGroup();
242 if (!group.empty()) {
243 // If this multilib has the same ExclusiveGroup as one we've already
244 // selected, skip it. We're iterating in reverse order, so the group
245 // member we've selected already is preferred.
246 //
247 // Otherwise, add the group name to the set of groups we've already
248 // selected a member of.
249 auto [It, Inserted] = ExclusiveGroupsSelected.insert(group);
250 if (!Inserted)
251 continue;
252 }
253
254 // If this multilib is actually a placeholder containing an error message
255 // written by the multilib.yaml author, then set a flag that will cause a
256 // failure return. Our caller will display the error message.
257 if (M.isError())
258 AnyErrors = true;
259
260 // Select this multilib.
261 Selected.push_back(M);
262 }
263
264 // We iterated in reverse order, so now put Selected back the right way
265 // round.
266 std::reverse(Selected.begin(), Selected.end());
267
268 return !AnyErrors && !Selected.empty();
269 }
270
271 llvm::StringSet<>
expandFlags(const Multilib::flags_list & InFlags) const272 MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {
273 llvm::StringSet<> Result(llvm::from_range, InFlags);
274 for (const FlagMatcher &M : FlagMatchers) {
275 std::string RegexString(M.Match);
276
277 // Make the regular expression match the whole string.
278 if (!StringRef(M.Match).starts_with("^"))
279 RegexString.insert(RegexString.begin(), '^');
280 if (!StringRef(M.Match).ends_with("$"))
281 RegexString.push_back('$');
282
283 const llvm::Regex Regex(RegexString);
284 assert(Regex.isValid());
285 if (llvm::any_of(InFlags,
286 [&Regex](StringRef F) { return Regex.match(F); })) {
287 Result.insert_range(M.Flags);
288 }
289 }
290 return Result;
291 }
292
293 namespace {
294
295 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp
296 static const VersionTuple MultilibVersionCurrent(1, 0);
297
298 struct MultilibSerialization {
299 std::string Dir; // if this record successfully selects a library dir
300 std::string Error; // if this record reports a fatal error message
301 std::vector<std::string> Flags;
302 std::string Group;
303 };
304
305 enum class MultilibGroupType {
306 /*
307 * The only group type currently supported is 'Exclusive', which indicates a
308 * group of multilibs of which at most one may be selected.
309 */
310 Exclusive,
311
312 /*
313 * Future possibility: a second group type indicating a set of library
314 * directories that are mutually _dependent_ rather than mutually exclusive:
315 * if you include one you must include them all.
316 *
317 * It might also be useful to allow groups to be members of other groups, so
318 * that a mutually exclusive group could contain a mutually dependent set of
319 * library directories, or vice versa.
320 *
321 * These additional features would need changes in the implementation, but
322 * the YAML schema is set up so they can be added without requiring changes
323 * in existing users' multilib.yaml files.
324 */
325 };
326
327 struct MultilibGroupSerialization {
328 std::string Name;
329 MultilibGroupType Type;
330 };
331
332 struct MultilibSetSerialization {
333 llvm::VersionTuple MultilibVersion;
334 SmallVector<MultilibGroupSerialization> Groups;
335 SmallVector<MultilibSerialization> Multilibs;
336 SmallVector<MultilibSet::FlagMatcher> FlagMatchers;
337 SmallVector<custom_flag::Declaration> CustomFlagDeclarations;
338 };
339
340 } // end anonymous namespace
341
342 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
343 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
344 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
345 LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::ValueDetail)
346 LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::Declaration)
347
348 template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
mappingllvm::yaml::MappingTraits349 static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {
350 io.mapOptional("Dir", V.Dir);
351 io.mapOptional("Error", V.Error);
352 io.mapRequired("Flags", V.Flags);
353 io.mapOptional("Group", V.Group);
354 }
validatellvm::yaml::MappingTraits355 static std::string validate(IO &io, MultilibSerialization &V) {
356 if (V.Dir.empty() && V.Error.empty())
357 return "one of the 'Dir' and 'Error' keys must be specified";
358 if (!V.Dir.empty() && !V.Error.empty())
359 return "the 'Dir' and 'Error' keys may not both be specified";
360 if (StringRef(V.Dir).starts_with("/"))
361 return "paths must be relative but \"" + V.Dir + "\" starts with \"/\"";
362 return std::string{};
363 }
364 };
365
366 template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> {
enumerationllvm::yaml::ScalarEnumerationTraits367 static void enumeration(IO &io, MultilibGroupType &Val) {
368 io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive);
369 }
370 };
371
372 template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> {
mappingllvm::yaml::MappingTraits373 static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) {
374 io.mapRequired("Name", V.Name);
375 io.mapRequired("Type", V.Type);
376 }
377 };
378
379 template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {
mappingllvm::yaml::MappingTraits380 static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {
381 io.mapRequired("Match", M.Match);
382 io.mapRequired("Flags", M.Flags);
383 }
validatellvm::yaml::MappingTraits384 static std::string validate(IO &io, MultilibSet::FlagMatcher &M) {
385 llvm::Regex Regex(M.Match);
386 std::string RegexError;
387 if (!Regex.isValid(RegexError))
388 return RegexError;
389 if (M.Flags.empty())
390 return "value required for 'Flags'";
391 return std::string{};
392 }
393 };
394
395 template <>
396 struct llvm::yaml::MappingContextTraits<custom_flag::ValueDetail,
397 llvm::SmallSet<std::string, 32>> {
mappingllvm::yaml::MappingContextTraits398 static void mapping(llvm::yaml::IO &io, custom_flag::ValueDetail &V,
399 llvm::SmallSet<std::string, 32> &) {
400 io.mapRequired("Name", V.Name);
401 io.mapOptional("MacroDefines", V.MacroDefines);
402 }
validatellvm::yaml::MappingContextTraits403 static std::string validate(IO &io, custom_flag::ValueDetail &V,
404 llvm::SmallSet<std::string, 32> &NameSet) {
405 if (V.Name.empty())
406 return "custom flag value requires a name";
407 if (!NameSet.insert(V.Name).second)
408 return "duplicate custom flag value name: \"" + V.Name + "\"";
409 return {};
410 }
411 };
412
413 template <>
414 struct llvm::yaml::MappingContextTraits<custom_flag::Declaration,
415 llvm::SmallSet<std::string, 32>> {
mappingllvm::yaml::MappingContextTraits416 static void mapping(llvm::yaml::IO &io, custom_flag::Declaration &V,
417 llvm::SmallSet<std::string, 32> &NameSet) {
418 io.mapRequired("Name", V.Name);
419 io.mapRequired("Values", V.ValueList, NameSet);
420 std::string DefaultValueName;
421 io.mapRequired("Default", DefaultValueName);
422
423 for (auto [Idx, Value] : llvm::enumerate(V.ValueList)) {
424 Value.Decl = &V;
425 if (Value.Name == DefaultValueName) {
426 assert(!V.DefaultValueIdx);
427 V.DefaultValueIdx = Idx;
428 }
429 }
430 }
validatellvm::yaml::MappingContextTraits431 static std::string validate(IO &io, custom_flag::Declaration &V,
432 llvm::SmallSet<std::string, 32> &) {
433 if (V.Name.empty())
434 return "custom flag requires a name";
435 if (V.ValueList.empty())
436 return "custom flag must have at least one value";
437 if (!V.DefaultValueIdx)
438 return "custom flag must have a default value";
439 return {};
440 }
441 };
442
443 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
mappingllvm::yaml::MappingTraits444 static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
445 io.mapRequired("MultilibVersion", M.MultilibVersion);
446 io.mapRequired("Variants", M.Multilibs);
447 io.mapOptional("Groups", M.Groups);
448 llvm::SmallSet<std::string, 32> NameSet;
449 io.mapOptionalWithContext("Flags", M.CustomFlagDeclarations, NameSet);
450 io.mapOptional("Mappings", M.FlagMatchers);
451 }
validatellvm::yaml::MappingTraits452 static std::string validate(IO &io, MultilibSetSerialization &M) {
453 if (M.MultilibVersion.empty())
454 return "missing required key 'MultilibVersion'";
455 if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor())
456 return "multilib version " + M.MultilibVersion.getAsString() +
457 " is unsupported";
458 if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())
459 return "multilib version " + M.MultilibVersion.getAsString() +
460 " is unsupported";
461 for (const MultilibSerialization &Lib : M.Multilibs) {
462 if (!Lib.Group.empty()) {
463 bool Found = false;
464 for (const MultilibGroupSerialization &Group : M.Groups)
465 if (Group.Name == Lib.Group) {
466 Found = true;
467 break;
468 }
469 if (!Found)
470 return "multilib \"" + Lib.Dir +
471 "\" specifies undefined group name \"" + Lib.Group + "\"";
472 }
473 }
474 return std::string{};
475 }
476 };
477
478 llvm::ErrorOr<MultilibSet>
parseYaml(llvm::MemoryBufferRef Input,llvm::SourceMgr::DiagHandlerTy DiagHandler,void * DiagHandlerCtxt)479 MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
480 llvm::SourceMgr::DiagHandlerTy DiagHandler,
481 void *DiagHandlerCtxt) {
482 MultilibSetSerialization MS;
483 llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt);
484 YamlInput >> MS;
485 if (YamlInput.error())
486 return YamlInput.error();
487
488 multilib_list Multilibs;
489 Multilibs.reserve(MS.Multilibs.size());
490 for (const auto &M : MS.Multilibs) {
491 if (!M.Error.empty()) {
492 Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.Error);
493 } else {
494 std::string Dir;
495 if (M.Dir != ".")
496 Dir = "/" + M.Dir;
497 // We transfer M.Group straight into the ExclusiveGroup parameter for the
498 // Multilib constructor. If we later support more than one type of group,
499 // we'll have to look up the group name in MS.Groups, check its type, and
500 // decide what to do here.
501 Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group);
502 }
503 }
504
505 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers),
506 std::move(MS.CustomFlagDeclarations));
507 }
508
dump() const509 LLVM_DUMP_METHOD void MultilibSet::dump() const {
510 print(llvm::errs());
511 }
512
print(raw_ostream & OS) const513 void MultilibSet::print(raw_ostream &OS) const {
514 for (const auto &M : *this)
515 OS << M << "\n";
516 }
517
operator <<(raw_ostream & OS,const MultilibSet & MS)518 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
519 MS.print(OS);
520 return OS;
521 }
522
523 namespace clang::driver::custom_flag {
Declaration(const Declaration & Other)524 Declaration::Declaration(const Declaration &Other)
525 : Name(Other.Name), ValueList(Other.ValueList),
526 DefaultValueIdx(Other.DefaultValueIdx) {
527 for (ValueDetail &Detail : ValueList)
528 Detail.Decl = this;
529 }
530
Declaration(Declaration && Other)531 Declaration::Declaration(Declaration &&Other)
532 : Name(std::move(Other.Name)), ValueList(std::move(Other.ValueList)),
533 DefaultValueIdx(std::move(Other.DefaultValueIdx)) {
534 for (ValueDetail &Detail : ValueList)
535 Detail.Decl = this;
536 }
537
operator =(const Declaration & Other)538 Declaration &Declaration::operator=(const Declaration &Other) {
539 if (this == &Other)
540 return *this;
541 Name = Other.Name;
542 ValueList = Other.ValueList;
543 DefaultValueIdx = Other.DefaultValueIdx;
544 for (ValueDetail &Detail : ValueList)
545 Detail.Decl = this;
546 return *this;
547 }
548
operator =(Declaration && Other)549 Declaration &Declaration::operator=(Declaration &&Other) {
550 if (this == &Other)
551 return *this;
552 Name = std::move(Other.Name);
553 ValueList = std::move(Other.ValueList);
554 DefaultValueIdx = std::move(Other.DefaultValueIdx);
555 for (ValueDetail &Detail : ValueList)
556 Detail.Decl = this;
557 return *this;
558 }
559 } // namespace clang::driver::custom_flag
560