[flang] first cut at expressions
authorpeter klausler <pklausler@nvidia.com>
Tue, 19 Jun 2018 21:16:01 +0000 (14:16 -0700)
committerpeter klausler <pklausler@nvidia.com>
Fri, 22 Jun 2018 21:56:17 +0000 (14:56 -0700)
Original-commit: flang-compiler/f18@b06c5486e4d80e017a9c697c79ac588a88498547
Reviewed-on: https://github.com/flang-compiler/f18/pull/111
Tree-same-pre-rewrite: false

flang/lib/common/indirection.h
flang/lib/evaluate/CMakeLists.txt
flang/lib/evaluate/expression.h
flang/lib/evaluate/type.h
flang/test/evaluate/CMakeLists.txt
flang/test/evaluate/expression.cc [new file with mode: 0644]
flang/test/evaluate/logical.cc
flang/test/evaluate/real.cc

index 72b3967..1a78f30 100644 (file)
@@ -33,12 +33,12 @@ public:
     CHECK(p_ && "assigning null pointer to Indirection");
     p = nullptr;
   }
+  Indirection(const A &x) : p_{new A(x)} {}
   Indirection(A &&p) : p_{new A(std::move(p))} {}
   template<typename... ARGS>
   Indirection(ARGS &&... args) : p_{new A(std::forward<ARGS>(args)...)} {}
-  Indirection(Indirection &&that) {
-    CHECK(that.p_ && "constructing Indirection from null Indirection");
-    p_ = that.p_;
+  Indirection(Indirection &&that) : p_{that.p_} {
+    CHECK(p_ && "move construction of Indirection from null Indirection");
     that.p_ = nullptr;
   }
   ~Indirection() {
@@ -46,7 +46,7 @@ public:
     p_ = nullptr;
   }
   Indirection &operator=(Indirection &&that) {
-    CHECK(that.p_ && "assigning null Indirection to Indirection");
+    CHECK(that.p_ && "move assignment of null Indirection to Indirection");
     auto tmp = p_;
     p_ = that.p_;
     that.p_ = tmp;
index f7b388c..73d5c82 100644 (file)
@@ -18,3 +18,7 @@ add_library(FortranEvaluate
   logical.cc
   real.cc
 )
+
+target_link_libraries(FortranEvaluate
+  FortranCommon
+)
index a64a97a..589e17d 100644 (file)
 #ifndef FORTRAN_EVALUATE_EXPRESSION_H_
 #define FORTRAN_EVALUATE_EXPRESSION_H_
 
+#include "common.h"
 #include "type.h"
 #include "../common/indirection.h"
 #include <variant>
 
 namespace Fortran::evaluate {
 
-template<Classification C, int KIND> struct Expression;
+template<typename T> struct Expression;
 
-template<int KIND> struct Expression<Classification::Integer> {
-  static constexpr Classification classification{Classification::Integer};
+template<typename T> struct ExprOperand {
+  template<typename... ARGS> ExprOperand(const ARGS &...args) : v{args...} {}
+  template<typename... ARGS> ExprOperand(ARGS &&...args) : v{std::forward<ARGS>(args)...} {}
+  common::Indirection<Expression<T>> v;
+};
+
+struct IntegerOperand {
+  std::variant<
+    ExprOperand<Type<Category::Integer, 1>>,
+    ExprOperand<Type<Category::Integer, 2>>,
+    ExprOperand<Type<Category::Integer, 4>>,
+    ExprOperand<Type<Category::Integer, 8>>,
+    ExprOperand<Type<Category::Integer, 16>>> u;
+};
+struct RealOperand {
+  std::variant<
+    ExprOperand<Type<Category::Real, 2>>,
+    ExprOperand<Type<Category::Real, 4>>,
+    ExprOperand<Type<Category::Real, 8>>,
+    ExprOperand<Type<Category::Real, 10>>,
+    ExprOperand<Type<Category::Real, 16>>> u;
+};
+struct ComplexOperand {
+  std::variant<
+    ExprOperand<Type<Category::Complex, 2>>,
+    ExprOperand<Type<Category::Complex, 4>>,
+    ExprOperand<Type<Category::Complex, 8>>,
+    ExprOperand<Type<Category::Complex, 10>>,
+    ExprOperand<Type<Category::Complex, 16>>> u;
+};
+struct CharacterOperand {
+  std::variant<ExprOperand<Type<Category::Character, 1>>> u;
+};
+
+struct FloatingOperand {
+  std::variant<RealOperand, ComplexOperand> u;
+};
+struct NumericOperand {
+  std::variant<IntegerOperand, FloatingOperand> u;
+};
+
+template<Category C, int KIND> struct NumericBase {
+  static constexpr Category category{C};
   static constexpr int kind{KIND};
+  using Result = Type<category, kind>;
+  using Operand = ExprOperand<Result>;
+  using Constant = typename Result::Value;
+  struct Unary {
+    enum class Operator { Parentheses, Negate } op;
+    Operand x;
+  };
+  struct Binary {
+    enum class Operator { Add, Subtract, Multiply, Divide, Power } op;
+    Operand x, y;
+  };
+  struct Convert {
+    NumericOperand x;
+  };
 };
 
-using<int KIND> IntegerExpression = Expression<Classification::Integer, KIND>;
-using<int KIND> RealExpression = Expression<Classification::Real, KIND>;
+template<int KIND> struct Expression<Type<Category::Integer, KIND>> : public NumericBase<Category::Integer, KIND> {
+  using Base = NumericBase<Category::Integer, KIND>;
+  using Result = typename Base::Result;
+  using Constant = typename Base::Constant;
+  using Convert = typename Base::Convert;
+  using Unary = typename Base::Unary;
+  using Binary = typename Base::Binary;
+  Expression() = delete;
+  Expression(Expression &&) = default;
+  Expression(const Constant &x) : u{x} {}
+  Expression(Convert &&x) : u{std::move(x)} {}
+  Expression(typename Unary::Operator o, Expression &&a) : u{Unary{o, std::move(a)}} {}
+  Expression(typename Binary::Operator o, Expression &&a, Expression &&b) : u{Binary{o, std::move(a), std::move(b)}} {}
+  std::variant<Constant, Convert, Unary, Binary> u;
+};
+
+template<Category C, int KIND> struct FloatingBase : public NumericBase<C, KIND> {
+  using Result = typename NumericBase<C, KIND>::Result;
+  struct IntegerPower {
+    Result x;
+    IntegerOperand y;
+  };
+};
 
+template<int KIND> struct Expression<Type<Category::Real, KIND>> : public FloatingBase<Category::Real, KIND> {
+  using Base = FloatingBase<Category::Real, KIND>;
+  using Result = typename Base::Result;
+  using Constant = typename Base::Constant;
+  using Convert = typename Base::Convert;
+  using Unary = typename Base::Unary;
+  using Binary = typename Base::Binary;
+  using IntegerPower = typename Base::IntegerPower;
+  Expression() = delete;
+  Expression(Expression &&) = default;
+  Expression(const Constant &x) : u{x} {}
+  Expression(Convert &&x) : u{std::move(x)} {}
+  Expression(typename Unary::Operator o, Expression &&a) : u{Unary{o, std::move(a)}} {}
+  Expression(typename Binary::Operator o, Expression &&a, Expression &&b) : u{Binary{o, std::move(a), std::move(b)}} {}
+  std::variant<Constant, Convert, Unary, Binary, IntegerPower> u;
+};
+
+template<int KIND> struct Expression<Type<Category::Complex, KIND>> : public FloatingBase<Category::Complex, KIND> {
+  using Base = FloatingBase<Category::Complex, KIND>;
+  using Result = typename Base::Result;
+  using Constant = typename Base::Constant;
+  using Convert = typename Base::Convert;
+  using Unary = typename Base::Unary;
+  using Binary = typename Base::Binary;
+  using IntegerPower = typename Base::IntegerPower;
+  Expression() = delete;
+  Expression(Expression &&) = default;
+  Expression(const Constant &x) : u{x} {}
+  Expression(Convert &&x) : u{std::move(x)} {}
+  Expression(typename Unary::Operator o, Expression &&a) : u{Unary{o, std::move(a)}} {}
+  Expression(typename Binary::Operator o, Expression &&a, Expression &&b) : u{Binary{o, std::move(a), std::move(b)}} {}
+  std::variant<Constant, Convert, Unary, Binary, IntegerPower> u;
+};
+
+template<> struct Expression<Type<Category::Logical, 1>> {
+  // No need to distinguish the various kinds of LOGICAL in expressions.
+  static constexpr Category category{Category::Logical};
+  static constexpr int kind{1};
+  using Result = Type<category, kind>;
+  using Operand = ExprOperand<Result>;
+  using Constant = typename Result::Value;
+
+  struct Unary {
+    enum class Operator { Not } op;
+    Operand x;
+  };
+  struct Binary {
+    enum class Operator { And, Or, Eqv, Neqv } op;
+    Operand x, y;
+  };
+
+  enum class ComparisonOperator { LT, LE, EQ, NE, GE, GT };  // TODO: .UN.?
+  template<typename T>
+  struct Comparison {
+    ComparisonOperator op;
+    ExprOperand<T> x, y;
+  };
+
+  enum class EqualityOperator { EQ, NE };
+  template<int KIND>
+  struct ComplexComparison {
+    EqualityOperator op;
+    ExprOperand<Type<Category::Complex, KIND>> x, y;
+  };
+
+  Expression() = delete;
+  Expression(Expression &&) = default;
+  Expression(const Constant &x) : u{x} {}
+  Expression(typename Unary::Operator o, Expression &&a) : u{Unary{o, std::move(a)}} {}
+  Expression(typename Binary::Operator o, Expression &&a, Expression &&b) : u{Binary{o, std::move(a), std::move(b)}} {}
+  template<typename T>
+  Expression(ComparisonOperator o, Expression<T> &&a, Expression<T> &&b) : u{Comparison<T>{o, std::move(a), std::move(b)}} {}
+  template<int KIND>
+  Expression(EqualityOperator o, Expression<Type<Category::Complex, KIND>> &&a, Expression<Type<Category::Complex, KIND>> &&b) : u{ComplexComparison<KIND>{o, std::move(a), std::move(b)}} {}
+  std::variant<Constant, Unary, Binary,
+               Comparison<Type<Category::Integer, 1>>,
+               Comparison<Type<Category::Integer, 2>>,
+               Comparison<Type<Category::Integer, 4>>,
+               Comparison<Type<Category::Integer, 8>>,
+               Comparison<Type<Category::Integer, 16>>,
+               Comparison<Type<Category::Character, 1>>,
+               Comparison<Type<Category::Real, 2>>,
+               Comparison<Type<Category::Real, 4>>,
+               Comparison<Type<Category::Real, 8>>,
+               Comparison<Type<Category::Real, 10>>,
+               Comparison<Type<Category::Real, 16>>,
+               ComplexComparison<2>,
+               ComplexComparison<4>,
+               ComplexComparison<8>,
+               ComplexComparison<10>,
+               ComplexComparison<16>> u;
+};
+
+template<int KIND> struct Expression<Type<Category::Character, KIND>> {
+  static constexpr Category category{Category::Character};
+  static constexpr int kind{KIND};
+  using Result = Type<category, kind>;
+  using Constant = typename Result::Value;
+  struct Concat {
+    ExprOperand<Result> x, y;
+  };
+  Expression() = delete;
+  Expression(Expression &&) = default;
+  Expression(const Constant &x) : u{x} {}
+  Expression(Expression &&a, Expression &&b) : u{Concat{std::move(a), std::move(b)}} {}
+  std::variant<Constant, Concat> u;
+  // TODO: length
+};
 }  // namespace Fortran::evaluate
 #endif  // FORTRAN_EVALUATE_EXPRESSION_H_
index 926c5ec..67bfb23 100644 (file)
 #include "real.h"
 #include <string>
 
-namespace Fortran::evaluate::type {
+namespace Fortran::evaluate {
 
-enum class Classification { Integer, Real, Complex, Character, Logical };
+enum class Category { Integer, Real, Complex, Character, Logical, Derived };
 
-template<int KIND> struct Integer {
-  static constexpr Classification classification{Classification::Integer};
+template<Category C, int KIND> struct TypeBase {
+  static constexpr Category category{C};
   static constexpr int kind{KIND};
   static constexpr bool hasLen{false};
-  using ValueType = value::Integer<8 * kind>;
 };
 
-template<int KIND> struct Real;
-template<> struct Real<2> {
-  static constexpr Classification classification{Classification::Real};
-  static constexpr int kind{2};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Real<typename Integer<kind>::ValueType, 11>;
+template<Category C, int KIND> struct Type;
+
+template<int KIND> struct Type<Category::Integer, KIND>
+    : public TypeBase<Category::Integer, KIND> {
+  using Value = value::Integer<8 * KIND>;
 };
-template<> struct Real<4> {
-  static constexpr Classification classification{Classification::Real};
-  static constexpr int kind{4};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Real<typename Integer<kind>::ValueType, 24>;
+
+template<> struct Type<Category::Real, 2>
+    : public TypeBase<Category::Real, 2> {
+  using Value = value::Real<typename Type<Category::Integer, 2>::Value, 11>;
 };
-template<> struct Real<8> {
-  static constexpr Classification classification{Classification::Real};
-  static constexpr int kind{8};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Real<typename Integer<kind>::ValueType, 53>;
+
+template<> struct Type<Category::Real, 4>
+    : public TypeBase<Category::Real, 4> {
+  using Value = value::Real<typename Type<Category::Integer, 4>::Value, 24>;
 };
-template<> struct Real<10> {
-  static constexpr Classification classification{Classification::Real};
-  static constexpr int kind{10};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Real<value::Integer<80>, 64, false>;
+
+template<> struct Type<Category::Real, 8>
+    : public TypeBase<Category::Real, 8> {
+  using Value = value::Real<typename Type<Category::Integer, 8>::Value, 53>;
 };
-template<> struct Real<16> {
-  static constexpr Classification classification{Classification::Real};
-  static constexpr int kind{16};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Real<typename Integer<kind>::ValueType, 112>;
+
+template<> struct Type<Category::Real, 10>
+    : public TypeBase<Category::Real, 10> {
+  using Value = value::Real<value::Integer<80>, 64, false>;
+};
+
+template<> struct Type<Category::Real, 16>
+    : public TypeBase<Category::Real, 16> {
+  using Value = value::Real<typename Type<Category::Integer, 16>::Value, 112>;
 };
 
 // The KIND type parameter on COMPLEX is the kind of each of its components.
-template<int KIND> struct Complex {
-  static constexpr Classification classification{Classification::Complex};
-  static constexpr int kind{KIND};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Complex<typename Real<kind>::ValueType>;
+template<int KIND> struct Type<Category::Complex, KIND>
+    : public TypeBase<Category::Complex, KIND> {
+  using Value = value::Complex<typename Type<Category::Real, KIND>::Value>;
 };
 
-template<int KIND> struct Logical {
-  static constexpr Classification classification{Classification::Logical};
-  static constexpr int kind{KIND};
-  static constexpr bool hasLen{false};
-  using ValueType = value::Logical<8 * kind>;
+template<int KIND> struct Type<Category::Logical, KIND>
+    : public TypeBase<Category::Logical, KIND> {
+  using Value = value::Logical<8 * KIND>;
 };
 
-template<int KIND> struct Character {
-  static constexpr Classification classification{Classification::Character};
+template<int KIND> struct Type<Category::Character, KIND> {
+  static constexpr Category category{Category::Character};
   static constexpr int kind{KIND};
   static constexpr bool hasLen{true};
-  using ValueType = std::string;
+  using Value = std::string;
 };
 
 // Default REAL just simply has to be IEEE-754 single precision today.
@@ -97,12 +92,11 @@ template<int KIND> struct Character {
 // storage unit, so their kinds are also forced.  Default COMPLEX occupies
 // two numeric storage units.
 
-using DefaultReal = Real<4>;
-using DefaultInteger = Integer<DefaultReal::kind>;
+using DefaultReal = Type<Category::Real, 4>;
+using DefaultInteger = Type<Category::Integer, DefaultReal::kind>;
 using IntrinsicTypeParameterType = DefaultInteger;
-using DefaultComplex = Complex<DefaultReal::kind>;
-using DefaultLogical = Logical<DefaultReal::kind>;
-using DefaultCharacter = Character<1>;
-
+using DefaultComplex = Type<Category::Complex, DefaultReal::kind>;
+using DefaultLogical = Type<Category::Logical, DefaultReal::kind>;
+using DefaultCharacter = Type<Category::Character, 1>;
 }  // namespace Fortran::evaluate::type
 #endif  // FORTRAN_EVALUATE_TYPE_H_
index d676418..58afb2c 100644 (file)
@@ -63,6 +63,16 @@ target_link_libraries(real-test
   m
 )
 
+add_executable(expression-test
+  expression.cc
+)
+
+target_link_libraries(expression-test
+  FortranEvaluate
+  FortranEvaluateTesting
+)
+
+add_test(NAME Expression COMMAND expression-test)
 add_test(NAME Leadz COMMAND leading-zero-bit-count-test)
 add_test(NAME PopPar COMMAND bit-population-count-test)
 add_test(NAME Integer COMMAND integer-test)
diff --git a/flang/test/evaluate/expression.cc b/flang/test/evaluate/expression.cc
new file mode 100644 (file)
index 0000000..03501e6
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+//
+// 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.
+
+#include "testing.h"
+#include "../../lib/evaluate/expression.h"
+#include <cstdio>
+#include <cstdlib>
+
+using namespace Fortran::evaluate;
+
+int main() {
+  using Int4 = Type<Category::Integer, 4>;
+  using IntEx4 = Expression<Int4>;
+  auto ie = IntEx4{value::Integer<32>(666)};
+  auto one = IntEx4{value::Integer<32>(1)};
+  auto incr = IntEx4{IntEx4::Binary::Operator::Add, std::move(ie), std::move(one)};
+  using Log = Expression<Type<Category::Logical, 1>>;
+  auto two = IntEx4{value::Integer<32>(2)};
+  auto cmp = Log{Log::ComparisonOperator::EQ, std::move(incr), std::move(two)};
+  return testing::Complete();
+}
index 926e7ca..f8124dc 100644 (file)
 #include "../../lib/evaluate/type.h"
 #include <cstdio>
 
-using namespace Fortran::evaluate::type;
-
 template<int KIND> void testKind() {
-  using Type = Logical<KIND>;
-  TEST(Type::classification == Classification::Logical);
+  using Type = Fortran::evaluate::Type<Fortran::evaluate::Category::Logical, KIND>;
+  TEST(Type::category == Fortran::evaluate::Category::Logical);
   TEST(Type::kind == KIND);
   TEST(!Type::hasLen);
-  using Value = typename Type::ValueType;
+  using Value = typename Type::Value;
   MATCH(8 * KIND, Value::bits);
   TEST(!Value{}.IsTrue());
   TEST(!Value{false}.IsTrue());
index a6e24e1..fbb9a60 100644 (file)
 
 using namespace Fortran::evaluate;
 
-using Real2 = typename type::Real<2>::ValueType;
-using Real4 = typename type::Real<4>::ValueType;
-using Real8 = typename type::Real<8>::ValueType;
-using Real10 = typename type::Real<10>::ValueType;
-using Real16 = typename type::Real<16>::ValueType;
-using Integer4 = typename type::Integer<4>::ValueType;
-using Integer8 = typename type::Integer<8>::ValueType;
+using Real2 = typename Type<Category::Real, 2>::Value;
+using Real4 = typename Type<Category::Real, 4>::Value;
+using Real8 = typename Type<Category::Real, 8>::Value;
+using Real10 = typename Type<Category::Real, 10>::Value;
+using Real16 = typename Type<Category::Real, 16>::Value;
+using Integer4 = typename Type<Category::Integer, 4>::Value;
+using Integer8 = typename Type<Category::Integer, 8>::Value;
 
 template<typename R> void basicTests(int rm, Rounding rounding) {
   char desc[64];
@@ -361,4 +361,5 @@ int main() {
   roundTest(2, Rounding::Up, opds);
   roundTest(3, Rounding::Down, opds);
   // TODO: how to test Rounding::TiesAwayFromZero on x86?
+  return testing::Complete();
 }