Snap for 8253222 from dbe499e5997f5d02ebbc3fd3884c349796175f61 to sdk-release
Change-Id: Ie37e24f1fdc416ff6bc978446cf3e4a756f780d2
diff --git a/Android.bp b/Android.bp
index c98cd21..ef56692 100644
--- a/Android.bp
+++ b/Android.bp
@@ -248,6 +248,37 @@
test_suites: ["device_tests"],
}
+// Can be removed when we move to c++20
+cc_test {
+ name: "libbase_result_constraint_test",
+ defaults: ["libbase_cflags_defaults"],
+ host_supported: true,
+ srcs: [
+ "result_test_constraint.cpp",
+ ],
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+ },
+ cpp_std: "gnu++20",
+ local_include_dirs: ["."],
+ shared_libs: ["libbase"],
+ static_libs: ["libgmock"],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: ["device-tests"],
+}
+
cc_benchmark {
name: "libbase_benchmark",
defaults: ["libbase_cflags_defaults"],
diff --git a/include/android-base/errors.h b/include/android-base/errors.h
index f67e6eb..61fdbd8 100644
--- a/include/android-base/errors.h
+++ b/include/android-base/errors.h
@@ -69,6 +69,9 @@
//
// Actually this can be used for any type as long as the OkOrFail<T> contract is satisfied. See
// below.
+// If implicit conversion compilation errors occur involving a value type with a templated
+// forwarding ref ctor, compilation with cpp20 or explicitly converting to the desired
+// return type is required.
#define OR_RETURN(expr) \
({ \
decltype(expr)&& tmp = (expr); \
@@ -109,7 +112,7 @@
// The OkOrFail contract for a type T. This must be implemented for a type T if you want to use
// OR_RETURN(stmt) where stmt evalues to a value of type T.
-template <typename T>
+template <typename T, typename = void>
struct OkOrFail {
// Checks if T is ok or fail.
static bool IsOk(const T&);
diff --git a/include/android-base/result.h b/include/android-base/result.h
index a5016b9..9715a8c 100644
--- a/include/android-base/result.h
+++ b/include/android-base/result.h
@@ -96,6 +96,7 @@
#include <sstream>
#include <string>
+#include <type_traits>
#include "android-base/errors.h"
#include "android-base/expected.h"
@@ -297,9 +298,93 @@
// Specialization of android::base::OkOrFail<V> for V = Result<T, E>. See android-base/errors.h
// for the contract.
-template <typename T, typename E>
-struct OkOrFail<Result<T, E>> {
- typedef Result<T, E> V;
+
+namespace impl {
+template <typename U>
+using Code = std::decay_t<decltype(std::declval<U>().error().code())>;
+
+template <typename U>
+using ErrorType = std::decay_t<decltype(std::declval<U>().error())>;
+
+template <typename U>
+constexpr bool IsNumeric = std::is_integral_v<U> || std::is_floating_point_v<U> ||
+ (std::is_enum_v<U> && std::is_convertible_v<U, size_t>);
+
+// This base class exists to take advantage of shadowing
+// We include the conversion in this base class so that if the conversion in NumericConversions
+// overlaps, we (arbitrarily) choose the implementation in NumericConversions due to shadowing.
+template <typename T>
+struct ConversionBase {
+ ErrorType<T> error_;
+ // T is a expected<U, ErrorType<T>>.
+ operator const T() const && {
+ return unexpected(std::move(error_));
+ }
+
+ operator const Code<T>() const && {
+ return error_.code();
+ }
+
+};
+
+// User defined conversions can be followed by numeric conversions
+// Although we template specialize for the exact code type, we need
+// specializations for conversions to all numeric types to avoid an
+// ambiguous conversion sequence.
+template <typename T, typename = void>
+struct NumericConversions : public ConversionBase<T> {};
+template <typename T>
+struct NumericConversions<T,
+ std::enable_if_t<impl::IsNumeric<impl::Code<T>>>
+ > : public ConversionBase<T>
+{
+#pragma push_macro("SPECIALIZED_CONVERSION")
+#define SPECIALIZED_CONVERSION(type)\
+ operator const expected<type, ErrorType<T>>() const &&\
+ { return unexpected(std::move(this->error_));}
+
+ SPECIALIZED_CONVERSION(int)
+ SPECIALIZED_CONVERSION(short int)
+ SPECIALIZED_CONVERSION(unsigned short int)
+ SPECIALIZED_CONVERSION(unsigned int)
+ SPECIALIZED_CONVERSION(long int)
+ SPECIALIZED_CONVERSION(unsigned long int)
+ SPECIALIZED_CONVERSION(long long int)
+ SPECIALIZED_CONVERSION(unsigned long long int)
+ SPECIALIZED_CONVERSION(bool)
+ SPECIALIZED_CONVERSION(char)
+ SPECIALIZED_CONVERSION(unsigned char)
+ SPECIALIZED_CONVERSION(signed char)
+ SPECIALIZED_CONVERSION(wchar_t)
+ SPECIALIZED_CONVERSION(char16_t)
+ SPECIALIZED_CONVERSION(char32_t)
+ SPECIALIZED_CONVERSION(float)
+ SPECIALIZED_CONVERSION(double)
+ SPECIALIZED_CONVERSION(long double)
+
+#undef SPECIALIZED_CONVERSION
+#pragma pop_macro("SPECIALIZED_CONVERSION")
+ // For debugging purposes
+ using IsNumericT = std::true_type;
+};
+
+#ifdef __cpp_concepts
+template<class U>
+concept Trivial = std::is_same_v<U, U>;
+#endif
+} // namespace impl
+
+template <typename T, typename E, bool include_message>
+struct OkOrFail<Result<T, E, include_message>>
+ : public impl::NumericConversions<Result<T, E, include_message>> {
+ using V = Result<T, E, include_message>;
+ using Err = impl::ErrorType<V>;
+ using C = impl::Code<V>;
+private:
+ OkOrFail(Err&& v): impl::NumericConversions<V>{std::move(v)} {}
+ OkOrFail(const OkOrFail& other) = delete;
+ OkOrFail(const OkOrFail&& other) = delete;
+public:
// Checks if V is ok or fail
static bool IsOk(const V& val) { return val.ok(); }
@@ -307,20 +392,23 @@
static T Unwrap(V&& val) { return std::move(val.value()); }
// Consumes V when it's a fail value
- static OkOrFail<V> Fail(V&& v) {
+ static const OkOrFail<V> Fail(V&& v) {
assert(!IsOk(v));
- return OkOrFail<V>{std::move(v)};
+ return OkOrFail<V>{std::move(v.error())};
}
- V val_;
- // Turns V into S (convertible from E) or Result<U, E>
- template <typename S, typename = std::enable_if_t<std::is_convertible_v<E, S>>>
- operator S() && {
- return val_.error().code();
+ // We specialize as much as possible to avoid ambiguous conversion with
+ // templated expected ctor
+ operator const Result<C, E, include_message>() const && {
+ return unexpected(std::move(this->error_));
}
+#ifdef __cpp_concepts
+ template <impl::Trivial U>
+#else
template <typename U>
- operator Result<U, E>() && {
- return val_.error();
+#endif
+ operator const Result<U, E, include_message>() const && {
+ return unexpected(std::move(this->error_));
}
static std::string ErrorMessage(const V& val) { return val.error().message(); }
diff --git a/include/android-base/silent_death_test.h b/include/android-base/silent_death_test.h
index 16f99ef..261fa74 100644
--- a/include/android-base/silent_death_test.h
+++ b/include/android-base/silent_death_test.h
@@ -26,15 +26,41 @@
#define sigaction64 sigaction
#endif
-// Disables debuggerd stack traces to speed up death tests, make them less
-// noisy in logcat, and avoid expected deaths from showing up in stability metrics.
+// INTRODUCTION
+//
+// It can be useful to disable debuggerd stack traces/tombstones in death tests.
+// Reasons include:
+//
+// 1. speeding up death tests
+// 2. reducing the noise in logcat (for humans)
+// 3. avoiding bots from thinking that expected deaths should be reported in
+// stability metrics/have bugs auto-filed
//
// When writing new death tests, inherit the test suite from SilentDeathTest
-// defined below. Only use ScopedSilentDeath in a test case/suite if changing the
-// test base from testing::Test to SilentDeathTest adds additional complextity when
+// defined below.
+//
+// Only use ScopedSilentDeath in a test case/suite if changing the test base
+// class from testing::Test to SilentDeathTest adds additional complextity when
// test suite code is shared between death and non-death tests.
//
-// For example, use ScopedSilentDeath if you have:
+// EXAMPLES
+//
+// For example, use SilentDeathTest for this simple case where there's no shared
+// setup or teardown:
+//
+// using FooDeathTest = SilentDeathTest;
+//
+// TEST(FooTest, DoesThis) {
+// // normal test
+// }
+//
+// TEST_F(FooDeathTest, DoesThat) {
+// // death test
+// }
+//
+// Alternatively, use ScopedSilentDeath if you already have a Test subclass for
+// shared setup or teardown:
+//
// class FooTest : public testing::Test { ... /* shared setup/teardown */ };
//
// using FooDeathTest = FooTest;
@@ -43,10 +69,32 @@
// // normal test
// }
//
-// TEST_F (FooDeathTest, DoesThat) {
+// TEST_F(FooDeathTest, DoesThat) {
// ScopedSilentDeath _silentDeath;
// // death test
// }
+//
+// NOTES
+//
+// When writing death tests, consider using ASSERT_EXIT() and EXPECT_EXIT()
+// rather than the more obvious ASSERT_DEATH()/EXPECT_DEATH() macros... The
+// advantage is that you can specify a regular expression that you expect
+// the abort message to match, and can be explicit about what signal you expect
+// to die with, and you can also test for *successful* exits too. Examples:
+//
+// ASSERT_DEATH(foo(), "some text\\. some more\\.");
+//
+// ASSERT_EXIT(bar(), testing::ExitedWithCode(0), "Success");
+//
+// ASSERT_EXIT(baz(), testing::KilledBySignal(SIGABRT),
+// "expected detail message \\(blah\\)");
+//
+// As you can see the regular expression functionality is there for
+// ASSERT_DEATH() too, but it's important to realize that it's a regular
+// expression, so (as in the first and third examples), you'll need to quote
+// any metacharacters (and because it's a string literal, you'll either need
+// extra quoting or want to use a raw string).
+
class ScopedSilentDeath {
public:
ScopedSilentDeath() {
@@ -68,11 +116,6 @@
struct sigaction64 previous_;
};
-// When writing death tests, use `using myDeathTest = SilentDeathTest;` or inherit from
-// SilentDeathTest instead of inheriting from testing::Test yourself.
-//
-// Disables debuggerd stack traces to speed up death tests, make them less
-// noisy in logcat, and avoid expected deaths from showing up in stability metrics.
class SilentDeathTest : public testing::Test {
protected:
void SetUp() override {
diff --git a/result_test.cpp b/result_test.cpp
index 47feeee..3b2b2e3 100644
--- a/result_test.cpp
+++ b/result_test.cpp
@@ -15,11 +15,13 @@
*/
#include "android-base/result.h"
-
+#include <utils/ErrorsMacros.h>
#include "errno.h"
#include <istream>
+#include <memory>
#include <string>
+#include <type_traits>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -281,6 +283,143 @@
EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "fail: A");
}
+TEST(result, unwrap_ambiguous_int) {
+ const std::string firstSuccess{"a"};
+ constexpr int secondSuccess = 5;
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, StatusT> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT>("Fail", 10);
+ };
+ auto f = [&](bool success) -> Result<int, StatusT> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return secondSuccess;
+ };
+
+ auto r = f(true);
+ ASSERT_TRUE(r.ok());
+ EXPECT_EQ(r.value(), secondSuccess);
+ auto s = f(false);
+ ASSERT_TRUE(!s.ok());
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+TEST(result, unwrap_ambiguous_uint_conv) {
+ const std::string firstSuccess{"a"};
+ constexpr size_t secondSuccess = 5ull;
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, StatusT> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT>("Fail", 10);
+ };
+
+ auto f = [&](bool success) -> Result<size_t, StatusT> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return secondSuccess;
+ };
+
+ auto r = f(true);
+ ASSERT_TRUE(r.ok());
+ EXPECT_EQ(r.value(), secondSuccess);
+ auto s = f(false);
+ ASSERT_TRUE(!s.ok());
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+struct IntConst {
+ int val_;
+ template <typename T, typename = std::enable_if_t<std::is_convertible_v<T, int>>>
+ IntConst(T&& val) : val_(val) {}
+ operator status_t() {return val_;}
+};
+
+TEST(result, unwrap_ambiguous_constructible) {
+ constexpr int firstSuccess = 5;
+ constexpr int secondSuccess = 7;
+ struct A {
+ A (int val) : val_(val) {}
+ operator status_t() { return 0; }
+ int val_;
+ };
+ // If this returns Result<A, ...> instead of Result<IntConst, ...>,
+ // compilation fails unless we compile with c++20
+ auto enum_success_or_fail = [&](bool success) -> Result<IntConst, StatusT, false> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT, false>(10);
+ };
+ auto f = [&](bool success) -> Result<IntConst, StatusT, false> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val.val_);
+ return secondSuccess;
+ };
+ auto r = f(true);
+ EXPECT_EQ(r.value().val_, secondSuccess);
+ auto s = f(false);
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+struct Dangerous {};
+struct ImplicitFromDangerous {
+ ImplicitFromDangerous(Dangerous);
+};
+template <typename U>
+struct Templated {
+ U val_;
+ template <typename T, typename=std::enable_if_t<std::is_convertible_v<T, U>>>
+ Templated(T val) : val_(val) {}
+};
+
+
+TEST(result, dangerous_result_conversion) {
+ ResultError<Dangerous, false> error {Dangerous{}};
+ Result<Templated<Dangerous>, Dangerous, false> surprise {error};
+ EXPECT_TRUE(!surprise.ok());
+ Result<Templated<ImplicitFromDangerous>, Dangerous, false> surprise2 {error};
+ EXPECT_TRUE(!surprise2.ok());
+}
+
+TEST(result, generic_convertible) {
+ const std::string firstSuccess{"a"};
+ struct A {};
+ struct B {
+ operator A() {return A{};}
+ };
+
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, B> {
+ if (success) return firstSuccess;
+ return ResultError<B>("Fail", B{});
+ };
+ auto f = [&](bool success) -> Result<A, B> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return A{};
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ auto s = f(false);
+ EXPECT_TRUE(!s.ok());
+}
+
+TEST(result, generic_exact) {
+ const std::string firstSuccess{"a"};
+ struct A {};
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, A> {
+ if (success) return firstSuccess;
+ return ResultError<A>("Fail", A{});
+ };
+ auto f = [&](bool success) -> Result<A, A> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return A{};
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ auto s = f(false);
+ EXPECT_TRUE(!s.ok());
+}
+
struct MyData {
const int data;
static int copy_constructed;
@@ -309,6 +448,27 @@
}();
}
+TEST(result, supports_move_only_type) {
+ auto f = [](bool success) -> Result<std::unique_ptr<std::string>> {
+ if (success) return std::make_unique<std::string>("hello");
+ return Error() << "error";
+ };
+
+ auto g = [&](bool success) -> Result<std::unique_ptr<std::string>> {
+ auto r = OR_RETURN(f(success));
+ EXPECT_EQ("hello", *(r.get()));
+ return std::make_unique<std::string>("world");
+ };
+
+ auto s = g(true);
+ EXPECT_RESULT_OK(s);
+ EXPECT_EQ("world", *(s->get()));
+
+ auto t = g(false);
+ EXPECT_FALSE(t.ok());
+ EXPECT_EQ("error", t.error().message());
+}
+
struct ConstructorTracker {
static size_t constructor_called;
static size_t copy_constructor_called;
diff --git a/result_test_constraint.cpp b/result_test_constraint.cpp
new file mode 100644
index 0000000..253a276
--- /dev/null
+++ b/result_test_constraint.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Since result has c++20 conditional behavior, we compile with std=c++20 to
+// ensure functionality in both cases. Instead of duplicating the file, we will
+// include, since this test is not linked against anything.
+// This test can be removed when we move to c++20.
+#include "result_test.cpp"