Bug 1232686 - Use decltype to infer Variant::match return type; r=fitzgen
authorTerrence Cole <terrence@mozilla.com>
Tue, 15 Dec 2015 07:45:22 -0800
changeset 345087 2ce26d5c3376c756f2cfa43a4b75378879363a24
parent 345086 6df6c214b402c176653e63478be56c0c66619d6d
child 345088 5a9c26f8bb9d599e80c92f6a7f30ad91bd54a854
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1232686
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1232686 - Use decltype to infer Variant::match return type; r=fitzgen
devtools/shared/heapsnapshot/HeapSnapshot.cpp
js/src/gc/Tracer.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/SavedStacks.cpp
js/src/vm/UbiNode.cpp
mfbt/Variant.h
mfbt/tests/TestVariant.cpp
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -132,18 +132,16 @@ parseMessage(ZeroCopyInputStream& stream
 
   codedStream.PopLimit(limit);
   return true;
 }
 
 template<typename CharT, typename InternedStringSet>
 struct GetOrInternStringMatcher
 {
-  using ReturnType = const CharT*;
-
   InternedStringSet& internedStrings;
 
   explicit GetOrInternStringMatcher(InternedStringSet& strings) : internedStrings(strings) { }
 
   const CharT* match(const std::string* str) {
     MOZ_ASSERT(str);
     size_t length = str->length() / sizeof(CharT);
     auto tempString = reinterpret_cast<const CharT*>(str->data());
@@ -855,39 +853,33 @@ EstablishBoundaries(JSContext* cx,
 // A variant covering all the various two-byte strings that we can get from the
 // ubi::Node API.
 class TwoByteString : public Variant<JSAtom*, const char16_t*, JS::ubi::EdgeName>
 {
   using Base = Variant<JSAtom*, const char16_t*, JS::ubi::EdgeName>;
 
   struct AsTwoByteStringMatcher
   {
-    using ReturnType = TwoByteString;
-
     TwoByteString match(JSAtom* atom) {
       return TwoByteString(atom);
     }
 
     TwoByteString match(const char16_t* chars) {
       return TwoByteString(chars);
     }
   };
 
   struct IsNonNullMatcher
   {
-    using ReturnType = bool;
-
     template<typename T>
     bool match(const T& t) { return t != nullptr; }
   };
 
   struct LengthMatcher
   {
-    using ReturnType = size_t;
-
     size_t match(JSAtom* atom) {
       MOZ_ASSERT(atom);
       JS::ubi::AtomOrTwoByteChars s(atom);
       return s.length();
     }
 
     size_t match(const char16_t* chars) {
       MOZ_ASSERT(chars);
@@ -897,18 +889,16 @@ class TwoByteString : public Variant<JSA
     size_t match(const JS::ubi::EdgeName& ptr) {
       MOZ_ASSERT(ptr);
       return NS_strlen(ptr.get());
     }
   };
 
   struct CopyToBufferMatcher
   {
-    using ReturnType = size_t;
-
     RangedPtr<char16_t> destination;
     size_t              maxLength;
 
     CopyToBufferMatcher(RangedPtr<char16_t> destination, size_t maxLength)
       : destination(destination)
       , maxLength(maxLength)
     { }
 
@@ -980,18 +970,16 @@ public:
 // because each type is generally a different semantic thing in addition to
 // having a slightly different representation. For example, the set of edge
 // names and the set stack frames' source names naturally tend not to overlap
 // very much if at all.
 struct TwoByteString::HashPolicy {
   using Lookup = TwoByteString;
 
   struct HashingMatcher {
-    using ReturnType  = js::HashNumber;
-
     js::HashNumber match(const JSAtom* atom) {
       return js::DefaultHasher<const JSAtom*>::hash(atom);
     }
 
     js::HashNumber match(const char16_t* chars) {
       MOZ_ASSERT(chars);
       auto length = NS_strlen(chars);
       return HashString(chars, length);
@@ -1004,17 +992,16 @@ struct TwoByteString::HashPolicy {
   };
 
   static js::HashNumber hash(const Lookup& l) {
     HashingMatcher hasher;
     return l.match(hasher);
   }
 
   struct EqualityMatcher {
-    using ReturnType = bool;
     const TwoByteString& rhs;
     explicit EqualityMatcher(const TwoByteString& rhs) : rhs(rhs) { }
 
     bool match(const JSAtom* atom) {
       return rhs.is<JSAtom*>() && rhs.as<JSAtom*>() == atom;
     }
 
     bool match(const char16_t* chars) {
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -128,27 +128,26 @@ js::TraceChildren(JSTracer* trc, void* t
 
 namespace {
 struct TraceIncomingFunctor {
     JSTracer* trc_;
     const JS::CompartmentSet& compartments_;
     TraceIncomingFunctor(JSTracer* trc, const JS::CompartmentSet& compartments)
       : trc_(trc), compartments_(compartments)
     {}
-    using ReturnType = void;
     template <typename T>
-    ReturnType operator()(T tp) {
+    void operator()(T tp) {
         if (!compartments_.has((*tp)->compartment()))
             return;
         TraceManuallyBarrieredEdge(trc_, tp, "cross-compartment wrapper");
     }
     // StringWrappers are just used to avoid copying strings
     // across zones multiple times, and don't hold a strong
     // reference.
-    ReturnType operator()(JSString** tp) {}
+    void operator()(JSString** tp) {}
 };
 } // namespace (anonymous)
 
 JS_PUBLIC_API(void)
 JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments)
 {
     for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
         if (compartments.has(comp))
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -222,17 +222,16 @@ class WrapperMapRef : public BufferableR
     WrapperMapRef(WrapperMap* map, const CrossCompartmentKey& key)
       : map(map), key(key) {}
 
     struct TraceFunctor {
         JSTracer* trc_;
         const char* name_;
         TraceFunctor(JSTracer *trc, const char* name) : trc_(trc), name_(name) {}
 
-        using ReturnType = void;
         template <class T> void operator()(T* t) { TraceManuallyBarrieredEdge(trc_, t, name_); }
     };
     void trace(JSTracer* trc) override {
         CrossCompartmentKey prior = key;
         key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
         key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
         if (key == prior)
             return;
@@ -245,17 +244,16 @@ class WrapperMapRef : public BufferableR
         /* Rekey the entry. */
         map->rekeyAs(prior, key, key);
     }
 };
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 namespace {
 struct CheckGCThingAfterMovingGCFunctor {
-    using ReturnType = void;
     template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
 };
 } // namespace (anonymous)
 
 void
 JSCompartment::checkWrapperMapAfterMovingGC()
 {
     /*
@@ -270,17 +268,16 @@ JSCompartment::checkWrapperMapAfterMovin
         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
 #endif
 
 namespace {
 struct IsInsideNurseryFunctor {
-    using ReturnType = bool;
     template <class T> bool operator()(T tp) { return IsInsideNursery(*tp); }
 };
 } // namespace (anonymous)
 
 bool
 JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
                           const js::Value& wrapper)
 {
@@ -770,21 +767,19 @@ JSCompartment::sweepCrossCompartmentWrap
     crossCompartmentWrappers.sweep();
 }
 
 namespace {
 struct TraceRootFunctor {
     JSTracer* trc;
     const char* name;
     TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
-    using ReturnType = void;
-    template <class T> ReturnType operator()(T* t) { return TraceRoot(trc, t, name); }
+    template <class T> void operator()(T* t) { return TraceRoot(trc, t, name); }
 };
 struct NeedsSweepUnbarrieredFunctor {
-    using ReturnType = bool;
     template <class T> bool operator()(T* t) const { return IsAboutToBeFinalizedUnbarriered(t); }
 };
 } // namespace (anonymous)
 
 void
 CrossCompartmentKey::trace(JSTracer* trc)
 {
     applyToWrapped(TraceRootFunctor(trc, "CrossCompartmentKey::wrapped"));
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -101,68 +101,66 @@ struct CrossCompartmentKey
 
     bool operator==(const CrossCompartmentKey& other) const { return wrapped == other.wrapped; }
     bool operator!=(const CrossCompartmentKey& other) const { return wrapped != other.wrapped; }
 
     template <typename T> bool is() const { return wrapped.is<T>(); }
     template <typename T> const T& as() const { return wrapped.as<T>(); }
 
     template <typename F>
-    auto applyToWrapped(F f) -> typename F::ReturnType {
+    auto applyToWrapped(F f) -> decltype(f(static_cast<JSObject**>(nullptr))) {
+        using ReturnType = decltype(f(static_cast<JSObject**>(nullptr)));
         struct WrappedMatcher {
-            using ReturnType = typename F::ReturnType;
             F f_;
             explicit WrappedMatcher(F f) : f_(f) {}
             ReturnType match(JSObject*& obj) { return f_(&obj); }
             ReturnType match(JSString*& str) { return f_(&str); }
             ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<1>(tpl)); }
             ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<1>(tpl)); }
         } matcher(f);
         return wrapped.match(matcher);
     }
 
     template <typename F>
-    auto applyToDebugger(F f) -> typename F::ReturnType {
+    auto applyToDebugger(F f) -> decltype(f(static_cast<NativeObject**>(nullptr))) {
+        using ReturnType = decltype(f(static_cast<NativeObject**>(nullptr)));
         struct DebuggerMatcher {
-            using ReturnType = typename F::ReturnType;
             F f_;
             explicit DebuggerMatcher(F f) : f_(f) {}
             ReturnType match(JSObject*& obj) { return ReturnType(); }
             ReturnType match(JSString*& str) { return ReturnType(); }
             ReturnType match(DebuggerAndScript& tpl) { return f_(&mozilla::Get<0>(tpl)); }
             ReturnType match(DebuggerAndObject& tpl) { return f_(&mozilla::Get<0>(tpl)); }
         } matcher(f);
         return wrapped.match(matcher);
     }
 
     // Valid for JSObject* and Debugger keys. Crashes immediately if used on a
     // JSString* key.
     JSCompartment* compartment() {
         struct GetCompartmentFunctor {
-            using ReturnType = JSCompartment*;
-            ReturnType operator()(JSObject** tp) const { return (*tp)->compartment(); }
-            ReturnType operator()(JSScript** tp) const { return (*tp)->compartment(); }
-            ReturnType operator()(JSString** tp) const {
+            JSCompartment* operator()(JSObject** tp) const { return (*tp)->compartment(); }
+            JSCompartment* operator()(JSScript** tp) const { return (*tp)->compartment(); }
+            JSCompartment* operator()(JSString** tp) const {
                 MOZ_CRASH("invalid ccw key"); return nullptr;
             }
         };
         return applyToWrapped(GetCompartmentFunctor());
     }
 
     struct Hasher : public DefaultHasher<CrossCompartmentKey>
     {
         struct HashFunctor {
-            using ReturnType = HashNumber;
-            ReturnType match(JSObject* obj) { return DefaultHasher<JSObject*>::hash(obj); }
-            ReturnType match(JSString* str) { return DefaultHasher<JSString*>::hash(str); }
-            ReturnType match(const DebuggerAndScript& tpl) {
+            HashNumber match(JSObject* obj) { return DefaultHasher<JSObject*>::hash(obj); }
+            HashNumber match(JSString* str) { return DefaultHasher<JSString*>::hash(str); }
+            HashNumber match(const DebuggerAndScript& tpl) {
                 return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
                        DefaultHasher<JSScript*>::hash(mozilla::Get<1>(tpl));
             }
-            ReturnType match(const DebuggerAndObject& tpl) {
+            HashNumber match(const DebuggerAndObject& tpl) {
                 return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
                        DefaultHasher<JSObject*>::hash(mozilla::Get<1>(tpl)) ^
                        (mozilla::Get<2>(tpl) << 5);
             }
         };
         static HashNumber hash(const CrossCompartmentKey& key) {
             return key.wrapped.match(HashFunctor());
         }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -609,19 +609,18 @@ js::ZoneGlobalsAreAllGray(JS::Zone* zone
 namespace {
 struct VisitGrayCallbackFunctor {
     GCThingCallback callback_;
     void* closure_;
     VisitGrayCallbackFunctor(GCThingCallback callback, void* closure)
       : callback_(callback), closure_(closure)
     {}
 
-    using ReturnType = void;
     template <class T>
-    ReturnType operator()(T tp) const {
+    void operator()(T tp) const {
         if ((*tp)->isTenured() && (*tp)->asTenured().isMarked(gc::GRAY))
             callback_(closure_, JS::GCCellPtr(*tp));
     }
 };
 } // namespace (anonymous)
 
 JS_FRIEND_API(void)
 js::VisitGrayWrapperTargets(Zone* zone, GCThingCallback callback, void* closure)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3666,17 +3666,16 @@ class CompartmentCheckTracer : public JS
     JSCompartment* compartment;
 };
 
 namespace {
 struct IsDestComparatorFunctor {
     JS::GCCellPtr dst_;
     explicit IsDestComparatorFunctor(JS::GCCellPtr dst) : dst_(dst) {}
 
-    using ReturnType = bool;
     template <typename T> bool operator()(T* t) { return (*t) == dst_.asCell(); }
 };
 } // namespace (anonymous)
 
 static bool
 InCrossCompartmentMap(JSObject* src, JS::GCCellPtr dst)
 {
     JSCompartment* srccomp = src->compartment();
@@ -4374,19 +4373,18 @@ namespace {
 struct AddOutgoingEdgeFunctor {
     bool needsEdge_;
     ZoneComponentFinder& finder_;
 
     AddOutgoingEdgeFunctor(bool needsEdge, ZoneComponentFinder& finder)
       : needsEdge_(needsEdge), finder_(finder)
     {}
 
-    using ReturnType = void;
     template <typename T>
-    ReturnType operator()(T tp) {
+    void operator()(T tp) {
         TenuredCell& other = (*tp)->asTenured();
 
         /*
          * Add edge to wrapped object compartment if wrapped object is not
          * marked black to indicate that wrapper compartment not be swept
          * after wrapped compartment.
          */
         if (needsEdge_) {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1856,34 +1856,32 @@ UncompressedSourceCache::sizeOfExcluding
     return n;
 }
 
 const char16_t*
 ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder)
 {
     struct CharsMatcher
     {
-        using ReturnType = const char16_t*;
-
         JSContext* cx;
         ScriptSource& ss;
         UncompressedSourceCache::AutoHoldEntry& holder;
 
         explicit CharsMatcher(JSContext* cx, ScriptSource& ss,
                               UncompressedSourceCache::AutoHoldEntry& holder)
           : cx(cx)
           , ss(ss)
           , holder(holder)
         { }
 
-        ReturnType match(Uncompressed& u) {
+        const char16_t* match(Uncompressed& u) {
             return u.string.chars();
         }
 
-        ReturnType match(Compressed& c) {
+        const char16_t* match(Compressed& c) {
             if (const char16_t* decompressed = cx->caches.uncompressedSourceCache.lookup(&ss, holder))
                 return decompressed;
 
             const size_t lengthWithNull = ss.length() + 1;
             UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
             if (!decompressed) {
                 JS_ReportOutOfMemory(cx);
                 return nullptr;
@@ -1911,25 +1909,25 @@ ScriptSource::chars(JSContext* cx, Uncom
                 if (!str) {
                     JS_ReportOutOfMemory(cx);
                     return nullptr;
                 }
                 ss.data = SourceType(Uncompressed(mozilla::Move(*str)));
                 return ss.data.as<Uncompressed>().string.chars();
             }
 
-            ReturnType ret = decompressed.get();
+            const char16_t* ret = decompressed.get();
             if (!cx->caches.uncompressedSourceCache.put(&ss, Move(decompressed), holder)) {
                 JS_ReportOutOfMemory(cx);
                 return nullptr;
             }
             return ret;
         }
 
-        ReturnType match(Missing&) {
+        const char16_t* match(Missing&) {
             MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
             return nullptr;
         }
     };
 
     CharsMatcher cm(cx, *this, holder);
     return data.match(cm);
 }
@@ -2142,45 +2140,41 @@ ScriptSource::addSizeOfIncludingThis(moz
 }
 
 template<XDRMode mode>
 bool
 ScriptSource::performXDR(XDRState<mode>* xdr)
 {
     struct CompressedLengthMatcher
     {
-        using ReturnType = size_t;
-
-        ReturnType match(Uncompressed&) {
+        size_t match(Uncompressed&) {
             return 0;
         }
 
-        ReturnType match(Compressed& c) {
+        size_t match(Compressed& c) {
             return c.raw.length();
         }
 
-        ReturnType match(Missing&) {
+        size_t match(Missing&) {
             MOZ_CRASH("Missing source data in ScriptSource::performXDR");
             return 0;
         }
     };
 
     struct RawDataMatcher
     {
-        using ReturnType = void*;
-
-        ReturnType match(Uncompressed& u) {
+        void* match(Uncompressed& u) {
             return (void*) u.string.chars();
         }
 
-        ReturnType match(Compressed& c) {
+        void* match(Compressed& c) {
             return (void*) c.raw.chars();
         }
 
-        ReturnType match(Missing&) {
+        void* match(Missing&) {
             MOZ_CRASH("Missing source data in ScriptSource::performXDR");
             return nullptr;
         }
     };
 
     uint8_t hasSource = hasSourceData();
     if (!xdr->codeUint8(&hasSource))
         return false;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -734,27 +734,25 @@ class ScriptSource
     void setSourceRetrievable() { sourceRetrievable_ = true; }
     bool sourceRetrievable() const { return sourceRetrievable_; }
     bool hasSourceData() const { return !data.is<Missing>(); }
     bool hasCompressedSource() const { return data.is<Compressed>(); }
 
     size_t length() const {
         struct LengthMatcher
         {
-            using ReturnType = size_t;
-
-            ReturnType match(const Uncompressed& u) {
+            size_t match(const Uncompressed& u) {
                 return u.string.length();
             }
 
-            ReturnType match(const Compressed& c) {
+            size_t match(const Compressed& c) {
                 return c.uncompressedLength;
             }
 
-            ReturnType match(const Missing& m) {
+            size_t match(const Missing& m) {
                 MOZ_CRASH("ScriptSource::length on a missing source");
                 return 0;
             }
         };
 
         MOZ_ASSERT(hasSourceData());
         return data.match(LengthMatcher());
     }
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1560,18 +1560,16 @@ ConcreteStackFrame<SavedFrame>::construc
     }
     return true;
 }
 
 // A `mozilla::Variant` matcher that converts the inner value of a
 // `JS::ubi::AtomOrTwoByteChars` string to a `JSAtom*`.
 struct MOZ_STACK_CLASS AtomizingMatcher
 {
-    using ReturnType = JSAtom*;
-
     JSContext* cx;
     size_t     length;
 
     explicit AtomizingMatcher(JSContext* cx, size_t length)
       : cx(cx)
       , length(length)
     { }
 
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -50,18 +50,16 @@ using JS::ubi::EdgeRange;
 using JS::ubi::Node;
 using JS::ubi::EdgeVector;
 using JS::ubi::StackFrame;
 using JS::ubi::TracerConcrete;
 using JS::ubi::TracerConcreteWithCompartment;
 
 struct CopyToBufferMatcher
 {
-    using ReturnType = size_t;
-
     RangedPtr<char16_t> destination;
     size_t              maxLength;
 
     CopyToBufferMatcher(RangedPtr<char16_t> destination, size_t maxLength)
       : destination(destination)
       , maxLength(maxLength)
     { }
 
@@ -103,18 +101,16 @@ size_t
 JS::ubi::AtomOrTwoByteChars::copyToBuffer(RangedPtr<char16_t> destination, size_t length)
 {
     CopyToBufferMatcher m(destination, length);
     return match(m);
 }
 
 struct LengthMatcher
 {
-    using ReturnType = size_t;
-
     size_t
     match(JSAtom* atom)
     {
         return atom ? atom->length() : 0;
     }
 
     size_t
     match(const char16_t* chars)
--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -164,20 +164,21 @@ struct VariantImplementation<N, T> {
   }
 
   template<typename Variant>
   static bool
   equal(const Variant& aLhs, const Variant& aRhs) {
       return aLhs.template as<T>() == aRhs.template as<T>();
   }
 
-  template<typename Matcher, typename ConcreteVariant,
-           typename ReturnType = typename RemoveReference<Matcher>::Type::ReturnType>
-  static ReturnType
-  match(Matcher&& aMatcher, ConcreteVariant& aV) {
+  template<typename Matcher, typename ConcreteVariant>
+  static auto
+  match(Matcher&& aMatcher, ConcreteVariant& aV)
+    -> decltype(aMatcher.match(aV.template as<T>()))
+  {
     return aMatcher.match(aV.template as<T>());
   }
 };
 
 // VariantImplementation for some variant type T.
 template<size_t N, typename T, typename... Ts>
 struct VariantImplementation<N, T, Ts...>
 {
@@ -221,20 +222,20 @@ struct VariantImplementation<N, T, Ts...
     if (aLhs.template is<T>()) {
       MOZ_ASSERT(aRhs.template is<T>());
       return aLhs.template as<T>() == aRhs.template as<T>();
     } else {
       return Next::equal(aLhs, aRhs);
     }
   }
 
-  template<typename Matcher, typename ConcreteVariant,
-           typename ReturnType = typename RemoveReference<Matcher>::Type::ReturnType>
-  static ReturnType
+  template<typename Matcher, typename ConcreteVariant>
+  static auto
   match(Matcher&& aMatcher, ConcreteVariant& aV)
+    -> decltype(aMatcher.match(aV.template as<T>()))
   {
     if (aV.template is<T>()) {
       return aMatcher.match(aV.template as<T>());
     } else {
       // If you're seeing compilation errors here like "no matching
       // function for call to 'match'" then that means that the
       // Matcher doesn't exhaust all variant types. There must exist a
       // Matcher::match(T&) for every variant type T.
@@ -365,21 +366,21 @@ struct AsVariantTemporary
  *       } else {
  *         return doSomething(v.as<C>()); // Forgot about case D!
  *       }
  *     }
  *
  *     // Good!
  *     struct FooMatcher
  *     {
- *       using ReturnType = char*;
- *       ReturnType match(A& a) { ... }
- *       ReturnType match(B& b) { ... }
- *       ReturnType match(C& c) { ... }
- *       ReturnType match(D& d) { ... } // Compile-time error to forget D!
+ *       // The return type of all matchers must be identical.
+ *       char* match(A& a) { ... }
+ *       char* match(B& b) { ... }
+ *       char* match(C& c) { ... }
+ *       char* match(D& d) { ... } // Compile-time error to forget D!
  *     }
  *     char* foo(Variant<A, B, C, D>& v) {
  *       return v.match(FooMatcher());
  *     }
  *
  * ## Examples
  *
  * A tree is either an empty leaf, or a node with a value and two children:
@@ -553,25 +554,29 @@ public:
     MOZ_ASSERT(is<T>());
     return T(Move(as<T>()));
   }
 
   // Exhaustive matching of all variant types on the contained value.
 
   /** Match on an immutable const reference. */
   template<typename Matcher>
-  typename RemoveReference<Matcher>::Type::ReturnType
-  match(Matcher&& aMatcher) const {
+  auto
+  match(Matcher&& aMatcher) const
+    -> decltype(Impl::match(aMatcher, *this))
+  {
     return Impl::match(aMatcher, *this);
   }
 
   /** Match on a mutable non-const reference. */
   template<typename Matcher>
-  typename RemoveReference<Matcher>::Type::ReturnType
-  match(Matcher&& aMatcher) {
+  auto
+  match(Matcher&& aMatcher)
+    -> decltype(Impl::match(aMatcher, *this))
+  {
     return Impl::match(aMatcher, *this);
   }
 };
 
 /*
  * AsVariant() is used to construct a Variant<T,...> value containing the
  * provided T value using type inference. It can be used to construct Variant
  * values in expressions or return them from functions without specifying the
--- a/mfbt/tests/TestVariant.cpp
+++ b/mfbt/tests/TestVariant.cpp
@@ -124,18 +124,16 @@ testEquality()
 }
 
 struct Describer
 {
   static const char* little;
   static const char* medium;
   static const char* big;
 
-  using ReturnType = const char*;
-
   const char* match(const uint8_t&) { return little; }
   const char* match(const uint32_t&) { return medium; }
   const char* match(const uint64_t&) { return big; }
 };
 
 const char* Describer::little = "little";
 const char* Describer::medium = "medium";
 const char* Describer::big = "big";