Commit ae4fa388 authored by Mike Curtiss's avatar Mike Curtiss Committed by Owen Yamauchi

HACK: Static detection of infinite sequences

Summary:
Certain operations should not be performed on infinite sequences
(e.g. sorting, left-folds, summation).  In some cases, we can
detect that a sequence is infinite at compile-time and provide
a static_assert to prevent such dangerous operations.

Test Plan:
Manually created cases where the operation should
be disallowed.  Compiler correctly raised an error.

Reviewed By: tjackson@fb.com

FB internal diff: D740011
parent 5c77fedb
...@@ -52,6 +52,11 @@ class FileReader : public GenImpl<ByteRange, FileReader> { ...@@ -52,6 +52,11 @@ class FileReader : public GenImpl<ByteRange, FileReader> {
} }
} }
} }
// Technically, there could be infinite files (e.g. /dev/random), but people
// who open those can do so at their own risk.
static constexpr bool infinite = false;
private: private:
File file_; File file_;
std::unique_ptr<IOBuf> buffer_; std::unique_ptr<IOBuf> buffer_;
......
...@@ -168,10 +168,16 @@ class GenImpl : public FBounded<Self> { ...@@ -168,10 +168,16 @@ class GenImpl : public FBounded<Self> {
template<class Body> template<class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
this->self().apply([&](Value value) -> bool { this->self().apply([&](Value value) -> bool {
static_assert(!infinite, "Cannot call foreach on infinite GenImpl");
body(std::forward<Value>(value)); body(std::forward<Value>(value));
return true; return true;
}); });
} }
// Child classes should override if the sequence generated is *definitely*
// infinite. 'infinite' may be false_type for some infinite sequences
// (due the the Halting Problem).
static constexpr bool infinite = false;
}; };
template<class LeftValue, template<class LeftValue,
...@@ -236,6 +242,8 @@ template<class Value, ...@@ -236,6 +242,8 @@ template<class Value,
typename std::enable_if< typename std::enable_if<
IsCompatibleSignature<Handler, void(Value)>::value>::type IsCompatibleSignature<Handler, void(Value)>::value>::type
operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) { operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
static_assert(!Gen::infinite,
"Cannot pull all values from an infinite sequence.");
gen.self().foreach(std::forward<Handler>(handler)); gen.self().foreach(std::forward<Handler>(handler));
} }
...@@ -439,6 +447,8 @@ public: ...@@ -439,6 +447,8 @@ public:
body(arg); body(arg);
} }
} }
static constexpr bool infinite = endless;
}; };
/** /**
...@@ -470,6 +480,8 @@ public: ...@@ -470,6 +480,8 @@ public:
first_.foreach(std::forward<Body>(body)); first_.foreach(std::forward<Body>(body));
second_.foreach(std::forward<Body>(body)); second_.foreach(std::forward<Body>(body));
} }
static constexpr bool infinite = First::infinite || Second::infinite;
}; };
/** /**
...@@ -567,6 +579,8 @@ class Map : public Operator<Map<Predicate>> { ...@@ -567,6 +579,8 @@ class Map : public Operator<Map<Predicate>> {
return handler(pred_(std::forward<Value>(value))); return handler(pred_(std::forward<Value>(value)));
}); });
} }
static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template<class Source,
...@@ -631,6 +645,8 @@ class Filter : public Operator<Filter<Predicate>> { ...@@ -631,6 +645,8 @@ class Filter : public Operator<Filter<Predicate>> {
return true; return true;
}); });
} }
static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template<class Source,
...@@ -699,6 +715,9 @@ class Until : public Operator<Until<Predicate>> { ...@@ -699,6 +715,9 @@ class Until : public Operator<Until<Predicate>> {
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_); return Gen(source.self(), pred_);
} }
// Theoretically an 'until' might stop an infinite
static constexpr bool infinite = false;
}; };
/** /**
...@@ -809,6 +828,8 @@ class Skip : public Operator<Skip> { ...@@ -809,6 +828,8 @@ class Skip : public Operator<Skip> {
return handler(std::forward<Value>(value)); return handler(std::forward<Value>(value));
}); });
} }
static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template<class Source,
...@@ -863,6 +884,7 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -863,6 +884,7 @@ class Order : public Operator<Order<Selector, Comparer>> {
class Generator : class Generator :
public GenImpl<StorageType&&, public GenImpl<StorageType&&,
Generator<Value, Source, StorageType, Result>> { Generator<Value, Source, StorageType, Result>> {
static_assert(!Source::infinite, "Cannot sort infinite source!");
Source source_; Source source_;
Selector selector_; Selector selector_;
Comparer comparer_; Comparer comparer_;
...@@ -1010,6 +1032,7 @@ class FoldLeft : public Operator<FoldLeft<Seed, Fold>> { ...@@ -1010,6 +1032,7 @@ class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
template<class Source, template<class Source,
class Value> class Value>
Seed compose(const GenImpl<Value, Source>& source) const { Seed compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot foldl infinite source");
Seed accum = seed_; Seed accum = seed_;
source | [&](Value v) { source | [&](Value v) {
accum = fold_(std::move(accum), std::forward<Value>(v)); accum = fold_(std::move(accum), std::forward<Value>(v));
...@@ -1107,6 +1130,7 @@ class All : public Operator<All<Predicate>> { ...@@ -1107,6 +1130,7 @@ class All : public Operator<All<Predicate>> {
template<class Source, template<class Source,
class Value> class Value>
bool compose(const GenImpl<Value, Source>& source) const { bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot call 'all' on infinite source");
bool all = true; bool all = true;
source | [&](Value v) -> bool { source | [&](Value v) -> bool {
if (!pred_(std::forward<Value>(v))) { if (!pred_(std::forward<Value>(v))) {
...@@ -1173,6 +1197,7 @@ class Count : public Operator<Count> { ...@@ -1173,6 +1197,7 @@ class Count : public Operator<Count> {
template<class Source, template<class Source,
class Value> class Value>
size_t compose(const GenImpl<Value, Source>& source) const { size_t compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot count infinite source");
return foldl(size_t(0), return foldl(size_t(0),
[](size_t accum, Value v) { [](size_t accum, Value v) {
return accum + 1; return accum + 1;
...@@ -1189,12 +1214,11 @@ class Count : public Operator<Count> { ...@@ -1189,12 +1214,11 @@ class Count : public Operator<Count> {
*/ */
class Sum : public Operator<Sum> { class Sum : public Operator<Sum> {
public: public:
Sum() { }
template<class Source, template<class Source,
class Value, class Value,
class StorageType = typename std::decay<Value>::type> class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const { StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot sum infinite source");
return foldl(StorageType(0), return foldl(StorageType(0),
[](StorageType&& accum, Value v) { [](StorageType&& accum, Value v) {
return std::move(accum) + std::forward<Value>(v); return std::move(accum) + std::forward<Value>(v);
...@@ -1222,6 +1246,9 @@ class Contains : public Operator<Contains<Needle>> { ...@@ -1222,6 +1246,9 @@ class Contains : public Operator<Contains<Needle>> {
class Value, class Value,
class StorageType = typename std::decay<Value>::type> class StorageType = typename std::decay<Value>::type>
bool compose(const GenImpl<Value, Source>& source) const { bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Calling contains on an infinite source might cause "
"an infinite loop.");
return !(source | [this](Value value) { return !(source | [this](Value value) {
return !(needle_ == std::forward<Value>(value)); return !(needle_ == std::forward<Value>(value));
}); });
...@@ -1414,6 +1441,8 @@ class Concat : public Operator<Concat> { ...@@ -1414,6 +1441,8 @@ class Concat : public Operator<Concat> {
inner.foreach(std::forward<Body>(body)); inner.foreach(std::forward<Body>(body));
}); });
} }
static constexpr bool infinite = Source::infinite;
}; };
template<class Value, template<class Value,
......
...@@ -109,6 +109,8 @@ class StringResplitter : public Operator<StringResplitter> { ...@@ -109,6 +109,8 @@ class StringResplitter : public Operator<StringResplitter> {
} }
return true; return true;
} }
static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template<class Source,
......
...@@ -159,7 +159,7 @@ TEST(Gen, Contains) { ...@@ -159,7 +159,7 @@ TEST(Gen, Contains) {
| eachTo<std::string>(); | eachTo<std::string>();
// std::string gen, const char* needle // std::string gen, const char* needle
EXPECT_TRUE(gen | contains("49")); EXPECT_TRUE(gen | take(9999) | contains("49"));
} }
} }
...@@ -427,7 +427,7 @@ TEST(Gen, Any) { ...@@ -427,7 +427,7 @@ TEST(Gen, Any) {
TEST(Gen, All) { TEST(Gen, All) {
EXPECT_TRUE(seq(0, 10) | all([](int i) { return i < 11; })); EXPECT_TRUE(seq(0, 10) | all([](int i) { return i < 11; }));
EXPECT_FALSE(seq(0, 10) | all([](int i) { return i < 5; })); EXPECT_FALSE(seq(0, 10) | all([](int i) { return i < 5; }));
EXPECT_FALSE(seq(0) | all([](int i) { return i < 10; })); EXPECT_FALSE(seq(0) | take(9999) | all([](int i) { return i < 10; }));
// empty lists satisfies all // empty lists satisfies all
EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i < 50; })); EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i < 50; }));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment