This is an archive of the discontinued LLVM Phabricator instance.

YAMLIO: Improve template arg deduction for mapOptional
ClosedPublic

Authored by labath on Mar 8 2019, 10:17 AM.

Details

Summary

The way c++ template argument deduction works, both arguments are used
to deduce the template type in the three-argument overload of
mapOptional. This is a problem if the types are slightly different, even
if they are implicitly convertible. This is fairly easy to trigger with
integral types, as the default type of most integral constants is int,
which then requires casting the constant to the type of the other
argument.

This fixes that by telling the compiler to not use the default value for
argument deduction. The "telling" is done by spelling out the argument
type in a more complicated manner, as deduction only happens for simple
types. This allows the default value to be any type which is implicitly
convertible to the value type.

Diff Detail

Event Timeline

labath created this revision.Mar 8 2019, 10:17 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 8 2019, 10:17 AM
zturner added inline comments.Mar 12 2019, 2:04 PM
include/llvm/Support/YAMLTraits.h
865–870

What if you just say

template<typename T, typename U>
void mapOptional(const char *Key, T &Val, const U &Default) {
  EmptyContext Ctx;
  mapOptionalWithContext(Key, Val, static_cast<T>(Default), Ctx);
}

This way it's basically self-documenting and doesn't require enable_if magic.

labath updated this revision to Diff 190380.Mar 13 2019, 1:59 AM

The static_cast also allows explicit conversions, which I don't think we
should automatically apply here (that would be circumventing the intention of
the person who made that particular conversion explicit). I fixed that by piping
the default value through a temporary reference. That should allow the same set
of conversions as during a function call, but it is also starting to look a bit
magical.

Let me know what you think.

labath updated this revision to Diff 190383.Mar 13 2019, 2:17 AM

Apply the same fix to mapOptionalWithContext to maintain a consistent interface.

zturner added inline comments.Mar 13 2019, 8:59 AM
include/llvm/Support/YAMLTraits.h
896–899

Another possibility is static_assert(std::is_convertible<T, DefaultT>::value, "Default type must be implicitly convertible to value type!"); which should have the same effect, and also yield a better compiler error message. Then, since explicit conversions are disallowed by the static_assert, you could re-write the call site as this->processKeyWithDefault(Key, Val, static_cast<T>(Default), false, Ctx);

I don't have a strong preference, but this feels mildly better to me. WDYT?

labath marked an inline comment as done.Mar 14 2019, 5:18 AM
labath added inline comments.
include/llvm/Support/YAMLTraits.h
896–899

Yes, that does look better. I'll update the patch with that.

labath updated this revision to Diff 190605.Mar 14 2019, 5:32 AM

Use the static_assert approach to check for convertibility. The only change I
made was to static_cast the default value into a const T & instead of a plain
T, as the latter would force a copy even if the default value was already of
the same type.

(Interestingly, the static_cast to a reference type is already enough to
disqualify explicit conversions for gcc and msvc. Clang still permits those, but
I have no idea who is correct here.)

zturner accepted this revision.Mar 14 2019, 7:27 AM
This revision is now accepted and ready to land.Mar 14 2019, 7:27 AM
This revision was automatically updated to reflect the committed changes.