Disallow temporary default values in get_ref_default()
authorNick Terrell <terrelln@fb.com>
Mon, 27 Mar 2017 20:03:02 +0000 (13:03 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Mon, 27 Mar 2017 20:05:17 +0000 (13:05 -0700)
Summary: Passing a temporary default value to `get_ref_default()` returns a dangling reference when it is used.

Reviewed By: lbrandy, yfeldblum

Differential Revision: D4768769

fbshipit-source-id: 4c58a17dc7662ad553cf88a8544dae20016d2f6f

folly/MapUtil.h
folly/test/MapUtilTest.cpp

index 063bb0f43ba24e2a7a5d35f5ff7ab255970d89e7..77e092f6ab5b766aedf2d8e1374f2c2d4c44b3b1 100644 (file)
@@ -106,6 +106,24 @@ const typename Map::mapped_type& get_ref_default(
   return (pos != map.end() ? pos->second : dflt);
 }
 
+/**
+ * Passing a temporary default value returns a dangling reference when it is
+ * returned. Lifetime extension is broken by the indirection.
+ * The caller must ensure that the default value outlives the reference returned
+ * by get_ref_default().
+ */
+template <class Map>
+const typename Map::mapped_type& get_ref_default(
+    const Map& map,
+    const typename Map::key_type& key,
+    typename Map::mapped_type&& dflt) = delete;
+
+template <class Map>
+const typename Map::mapped_type& get_ref_default(
+    const Map& map,
+    const typename Map::key_type& key,
+    const typename Map::mapped_type&& dflt) = delete;
+
 /**
  * Given a map and a key, return a reference to the value corresponding to the
  * key in the map, or the given default reference if the key doesn't exist in
index 1fa91df0f054d004d9743de460243ab99d143375..33ffe0661adf0eb3844e497db2d0bf49db56921a 100644 (file)
@@ -19,6 +19,7 @@
 #include <map>
 #include <unordered_map>
 
+#include <folly/Traits.h>
 #include <folly/portability/GTest.h>
 
 using namespace folly;
@@ -138,3 +139,23 @@ TEST(MapUtil, get_ptr_path_mixed) {
   EXPECT_TRUE(get_ptr(cm, "a", 1, "b"));
   EXPECT_FALSE(get_ptr(cm, "b", 1, "b"));
 }
+
+namespace {
+template <typename T, typename = void>
+struct Compiles : std::false_type {};
+
+template <typename T>
+struct Compiles<
+    T,
+    void_t<decltype(get_ref_default(
+        std::declval<std::map<int, typename std::decay<T>::type>>(),
+        std::declval<int>(),
+        std::declval<T>()))>> : std::true_type {};
+}
+
+TEST(MapUtil, get_default_temporary) {
+  EXPECT_TRUE(Compiles<const int&>::value);
+  EXPECT_TRUE(Compiles<int&>::value);
+  EXPECT_FALSE(Compiles<const int&&>::value);
+  EXPECT_FALSE(Compiles<int&&>::value);
+}