packages feed

zeolite-lang 0.22.1.0 → 0.23.0.0

raw patch · 56 files changed

+1614/−625 lines, 56 files

Files

ChangeLog.md view
@@ -1,5 +1,47 @@ # Revision history for zeolite-lang +## 0.23.0.0  -- 2022-10-08++### Language++* **[breaking]** Adds the `exit` builtin to terminate the program with an exit+  code, with the usual semantics, i.e., 0 means success. This is breaking+  because `exit` is now a reserved name.++* **[breaking]** Adds the `Pointer<#x>` builtin `concrete` category, for use in+  C++ extensions. (This is breaking because `Pointer` is now reserved.)++### Compiler CLI++* **[new]** Adds general pointer storage to `BoxedValue` to allow C++ extensions+  to pass data to each other without marshaling.++  ```c+++  // Create a value that can be returned/passed in Zeolite function calls.+  BoxedValue boxed = Box_Pointer<Foo>(&value);++  // Access the pointer stored in the value.+  Foo* unboxed = boxed.AsPointer<Foo>();+  ```++  This is only type-safe if the Zeolite code uses a different `#x` in+  `Pointer<#x>` for each different type used with `Box_Pointer`/`AsPointer`.+  For example, the code above uses `Foo`, so `Pointer<Foo>` should be used+  everywhere such values are passed around in the Zeolite code. (`#x` is+  arbitrary, but it's more clear if the name used in Zeolite corresponds to the+  name of the C++ type.)++* **[new]** Adds % code coverage in output of `zeolite --missed-lines`.++### Libraries++* **[new]** Moves `Realtime` from `lib/thread` to `lib/util` since it doesn't+  inherently require threads. (This isn't breaking because `lib/util` was+  already a *public* dependency of `lib/thread`.)++* **[new]** Adds the `Counter.builder()` function to `lib/util` to build `Int`+  counters with more flexibile limits and increments.+ ## 0.22.1.0  -- 2022-04-18  ### Language
base/.zeolite-module view
@@ -25,6 +25,10 @@     source: "base/src/Extension_String.cpp"     categories: [String]   }+  category_source {+    source: "base/src/Extension_Pointer.cpp"+    categories: [Pointer]+  }   "base/boxed.hpp"   "base/category-header.hpp"   "base/category-source.hpp"
base/boxed.hpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -38,6 +38,7 @@     CHAR,     INT,     FLOAT,+    POINTER,     BOXED,   }; @@ -54,11 +55,12 @@    union {     unsigned char* as_bytes_;-    Pointer*  as_pointer_;-    PrimBool  as_bool_;-    PrimChar  as_char_;-    PrimInt   as_int_;-    PrimFloat as_float_;+    Pointer*    as_boxed_;+    PrimBool    as_bool_;+    PrimChar    as_char_;+    PrimInt     as_int_;+    PrimFloat   as_float_;+    PrimPointer as_pointer_;   } __attribute__((packed)) value_; } __attribute__((packed)); @@ -66,13 +68,13 @@ class BoxedValue {  public:   constexpr BoxedValue()-    : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_pointer_ = nullptr } } {}+    : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_boxed_ = nullptr } } {}    inline BoxedValue(const BoxedValue& other)     : union_(other.union_) {     switch (union_.type_) {       case UnionValue::Type::BOXED:-        ++union_.value_.as_pointer_->strong_;+        ++union_.value_.as_boxed_->strong_;         break;       default:         break;@@ -87,7 +89,7 @@       union_ = other.union_;       switch (union_.type_) {         case UnionValue::Type::BOXED:-          ++union_.value_.as_pointer_->strong_;+          ++union_.value_.as_boxed_->strong_;           break;         default:           break;@@ -99,7 +101,7 @@   inline BoxedValue(BoxedValue&& other)     : union_(other.union_) {     other.union_.type_  = UnionValue::Type::EMPTY;-    other.union_.value_.as_pointer_ = nullptr;+    other.union_.value_.as_boxed_ = nullptr;   }    inline BoxedValue& operator = (BoxedValue&& other) {@@ -109,7 +111,7 @@       }       union_ = other.union_;       other.union_.type_  = UnionValue::Type::EMPTY;-      other.union_.value_.as_pointer_ = nullptr;+      other.union_.value_.as_boxed_ = nullptr;     }     return *this;   }@@ -126,6 +128,9 @@   inline BoxedValue(PrimFloat value)     : union_{ .type_ = UnionValue::Type::FLOAT, .value_ = { .as_float_ = value } } {} +  inline BoxedValue(PrimPointer value)+    : union_{ .type_ = UnionValue::Type::POINTER, .value_ = { .as_pointer_ = value } } {}+   template<class T, class... As>   static inline BoxedValue New(const As&... args) {     using Pointer = UnionValue::Pointer;@@ -188,6 +193,18 @@     }   } +  template<class T>+  inline T* AsPointer() const {+    switch (union_.type_) {+      case UnionValue::Type::POINTER:+        return reinterpret_cast<T*>(union_.value_.as_boxed_);+      default:+        FAIL() << union_.CategoryName() << " is not a Pointer value";+        __builtin_unreachable();+        break;+    }+  }+   inline static bool Present(const BoxedValue& target) {     return target.union_.type_ != UnionValue::Type::EMPTY;   }@@ -229,8 +246,8 @@     value.union_.type_ = UnionValue::Type::BOXED;     value.union_.value_.as_bytes_ =       reinterpret_cast<unsigned char*>(const_cast<T*>(pointer))-sizeof(UnionValue::Pointer);-    if (value.union_.value_.as_pointer_->object_ != pointer ||-        ++value.union_.value_.as_pointer_->strong_ == 1) {+    if (value.union_.value_.as_boxed_->object_ != pointer ||+        ++value.union_.value_.as_boxed_->strong_ == 1) {       FAIL() << "Bad VAR_SELF pointer " << pointer << " in " << pointer->CategoryName();     }     return value;
base/builtin.0rp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -183,6 +183,19 @@   //   Specifically, resizing down and then back up again could either preserve   //   or overwrite any of the data.   @value resize (Int) -> (CharBuffer)+}++// A raw pointer type for use in C++ extensions.+//+// Literals: None.+//+// Notes:+// - #x is mostly irrelevant. It is only intended for differentiating between+//   values with incompatible types.+// - This is category is only meant for passing C++ data between extensions when+//   it would otherwise require marshaling, which is why it has no functions.+concrete Pointer<#x> {+  immutable }  // Convertible to Bool.
base/category-source.hpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -19,6 +19,7 @@ #ifndef CATEGORY_SOURCE_HPP_ #define CATEGORY_SOURCE_HPP_ +#include <cstdlib> #include <iostream>  // For occasional debugging output in generated code. #include <map> #include <sstream>@@ -32,25 +33,47 @@   #define BUILTIN_FAIL(e) { \-  FAIL() << TypeValue::Call((e), Function_Formatted_formatted, \-                            PassParamsArgs()).At(0).AsString(); \-  __builtin_unreachable(); \+    FAIL() << TypeValue::Call((e), Function_Formatted_formatted, \+                              PassParamsArgs()).At(0).AsString(); \+    __builtin_unreachable(); \   } +#define BUILTIN_EXIT(e) { \+    std::exit(e); \+    __builtin_unreachable(); \+  }+ #define RAW_FAIL(e) { \-  FAIL() << e; \-  __builtin_unreachable(); \+    FAIL() << e; \+    __builtin_unreachable(); \   }  #define VAR_SELF TypeValue::Var_self(this)  #define PARAM_SELF Param_self() -BoxedValue Box_Bool(PrimBool value);+inline BoxedValue Box_Bool(PrimBool value) {+  return BoxedValue(value);+}++inline BoxedValue Box_Char(PrimChar value) {+  return BoxedValue(value);+}++inline BoxedValue Box_Float(PrimFloat value) {+  return BoxedValue(value);+}++inline BoxedValue Box_Int(PrimInt value) {+  return BoxedValue(value);+}++template<class T1, class T2>+inline BoxedValue Box_Pointer(T2* value) {+  return BoxedValue(reinterpret_cast<PrimPointer>(static_cast<T1*>(value)));+}+ BoxedValue Box_String(const PrimString& value);-BoxedValue Box_Char(PrimChar value);-BoxedValue Box_Int(PrimInt value);-BoxedValue Box_Float(PrimFloat value);  S<const TypeInstance> Merge_Intersect(const L<S<const TypeInstance>>& params); S<const TypeInstance> Merge_Union(const L<S<const TypeInstance>>& params);
base/src/Extension_Bool.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -98,7 +98,3 @@ }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE; #endif  // ZEOLITE_PUBLIC_NAMESPACE--BoxedValue Box_Bool(PrimBool value) {-  return BoxedValue(value);-}
base/src/Extension_Char.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -113,7 +113,3 @@ }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE; #endif  // ZEOLITE_PUBLIC_NAMESPACE--BoxedValue Box_Char(PrimChar value) {-  return BoxedValue(value);-}
base/src/Extension_Float.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -105,7 +105,3 @@ }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE; #endif  // ZEOLITE_PUBLIC_NAMESPACE--BoxedValue Box_Float(PrimFloat value) {-  return BoxedValue(value);-}
base/src/Extension_Int.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -116,7 +116,3 @@ }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE; #endif  // ZEOLITE_PUBLIC_NAMESPACE--BoxedValue Box_Int(PrimInt value) {-  return BoxedValue(value);-}
+ base/src/Extension_Pointer.cpp view
@@ -0,0 +1,62 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Pointer.hpp"++#ifdef ZEOLITE_PUBLIC_NAMESPACE+namespace ZEOLITE_PUBLIC_NAMESPACE {+#endif  // ZEOLITE_PUBLIC_NAMESPACE++struct ExtCategory_Pointer : public Category_Pointer {+};++struct ExtType_Pointer : public Type_Pointer {+  inline ExtType_Pointer(Category_Pointer& p, Params<1>::Type params) : Type_Pointer(p, params) {}+};++ReturnTuple DispatchPointer(PrimPointer value, const ValueFunction& label,+                            const ParamsArgs& params_args) {+  switch (label.collection) {+    default:+      FAIL() << "Pointer does not implement " << label;+      __builtin_unreachable();+  }+}++Category_Pointer& CreateCategory_Pointer() {+  static auto& category = *new ExtCategory_Pointer();+  return category;+}++static auto& Pointer_instance_cache = *new InstanceCache<1, Type_Pointer>([](const Params<1>::Type& params) {+    return S_get(new ExtType_Pointer(CreateCategory_Pointer(), params));+  });++S<const Type_Pointer> CreateType_Pointer(const Params<1>::Type& params) {+  return Pointer_instance_cache.GetOrCreate(params);+}++void RemoveType_Pointer(const Params<1>::Type& params) {+  Pointer_instance_cache.Remove(params);+}++#ifdef ZEOLITE_PUBLIC_NAMESPACE+}  // namespace ZEOLITE_PUBLIC_NAMESPACE+using namespace ZEOLITE_PUBLIC_NAMESPACE;+#endif  // ZEOLITE_PUBLIC_NAMESPACE
base/src/boxed.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -39,6 +39,9 @@ ReturnTuple DispatchFloat(PrimFloat value, const ValueFunction& label,                           const ParamsArgs& params_args) __attribute__((weak)); +ReturnTuple DispatchPointer(PrimPointer value, const ValueFunction& label,+                            const ParamsArgs& params_args) __attribute__((weak));+ #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE;@@ -51,26 +54,26 @@  void Validate(const std::string& name, const UnionValue& the_union) {   if (the_union.type_ == UnionValue::Type::BOXED) {-    const auto strong = the_union.value_.as_pointer_->strong_.load();-    const auto weak   = the_union.value_.as_pointer_->weak_.load();-    const TypeValue* const object = the_union.value_.as_pointer_->object_;+    const auto strong = the_union.value_.as_boxed_->strong_.load();+    const auto weak   = the_union.value_.as_boxed_->weak_.load();+    const TypeValue* const object = the_union.value_.as_boxed_->object_;     const std::string type = object ? the_union.CategoryName() : "deleted value";      if (strong == 0 && object) {       FAIL() << "Leaked " << type << " " << name << " at "-             << ((void*) the_union.value_.as_pointer_) << " (S: "+             << ((void*) the_union.value_.as_boxed_) << " (S: "              << std::hex << strong << " W: " << weak << ")";     }      if (strong != 0 && !object) {       FAIL() << "Prematurely deleted value " << name << " at "-             << ((void*) the_union.value_.as_pointer_) << " (S: "+             << ((void*) the_union.value_.as_boxed_) << " (S: "              << std::hex << strong << " W: " << weak << ")";     }      if (strong < 0 || weak < 0 || (weak == 0 && strong > 0)) {       FAIL() << "Invalid counts for " << type << " " << name << " at "-             << ((void*) the_union.value_.as_pointer_) << " (S: "+             << ((void*) the_union.value_.as_boxed_) << " (S: "              << std::hex << strong << " W: " << weak << ")";     }   }@@ -80,16 +83,17 @@  std::string UnionValue::CategoryName() const {   switch (type_) {-    case UnionValue::Type::EMPTY: return "empty";-    case UnionValue::Type::BOOL:  return "Bool";-    case UnionValue::Type::CHAR:  return "Char";-    case UnionValue::Type::INT:   return "Int";-    case UnionValue::Type::FLOAT: return "Float";+    case UnionValue::Type::EMPTY:   return "empty";+    case UnionValue::Type::BOOL:    return "Bool";+    case UnionValue::Type::CHAR:    return "Char";+    case UnionValue::Type::INT:     return "Int";+    case UnionValue::Type::FLOAT:   return "Float";+    case UnionValue::Type::POINTER: return "Pointer";     case UnionValue::Type::BOXED:-      if (!value_.as_pointer_ || !value_.as_pointer_->object_) {+      if (!value_.as_boxed_ || !value_.as_boxed_->object_) {         FAIL() << "Function called on null pointer";       }-      return value_.as_pointer_->object_->CategoryName();+      return value_.as_boxed_->object_->CategoryName();   } } @@ -97,14 +101,14 @@   : union_(other.union_) {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      while (union_.value_.as_pointer_->lock_.test_and_set(std::memory_order_acquire));-      if (++union_.value_.as_pointer_->strong_ == 1) {-        --union_.value_.as_pointer_->strong_;-        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);+      while (union_.value_.as_boxed_->lock_.test_and_set(std::memory_order_acquire));+      if (++union_.value_.as_boxed_->strong_ == 1) {+        --union_.value_.as_boxed_->strong_;+        union_.value_.as_boxed_->lock_.clear(std::memory_order_release);         union_.type_ = UnionValue::Type::EMPTY;-        union_.value_.as_pointer_ = nullptr;+        union_.value_.as_boxed_ = nullptr;       } else {-        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);+        union_.value_.as_boxed_->lock_.clear(std::memory_order_release);       }       break;     default:@@ -119,10 +123,10 @@ const PrimString& BoxedValue::AsString() const {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {+      if (!union_.value_.as_boxed_ || !union_.value_.as_boxed_->object_) {         FAIL() << "Function called on null pointer";       }-      return union_.value_.as_pointer_->object_->AsString();+      return union_.value_.as_boxed_->object_->AsString();     default:       FAIL() << union_.CategoryName() << " is not a String value";       __builtin_unreachable();@@ -133,10 +137,10 @@ PrimCharBuffer& BoxedValue::AsCharBuffer() const {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {+      if (!union_.value_.as_boxed_ || !union_.value_.as_boxed_->object_) {         FAIL() << "Function called on null pointer";       }-      return union_.value_.as_pointer_->object_->AsCharBuffer();+      return union_.value_.as_boxed_->object_->AsCharBuffer();     default:       FAIL() << union_.CategoryName() << " is not a CharBuffer value";       __builtin_unreachable();@@ -159,24 +163,26 @@       return DispatchInt(union_.value_.as_int_, label, params_args);     case UnionValue::Type::FLOAT:       return DispatchFloat(union_.value_.as_float_, label, params_args);+    case UnionValue::Type::POINTER:+      return DispatchPointer(union_.value_.as_pointer_, label, params_args);     case UnionValue::Type::BOXED:-      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {+      if (!union_.value_.as_boxed_ || !union_.value_.as_boxed_->object_) {         FAIL() << "Function called on null pointer";       }-      return union_.value_.as_pointer_->object_->Dispatch(label, params_args);+      return union_.value_.as_boxed_->object_->Dispatch(label, params_args);   } }  void BoxedValue::Cleanup() {   while (union_.type_ == UnionValue::Type::BOXED) {-    while (union_.value_.as_pointer_->lock_.test_and_set(std::memory_order_acquire));-    if (--union_.value_.as_pointer_->strong_ == 0) {-      TypeValue* const object = union_.value_.as_pointer_->object_;-      union_.value_.as_pointer_->object_ = nullptr;-      union_.value_.as_pointer_->lock_.clear(std::memory_order_release);+    while (union_.value_.as_boxed_->lock_.test_and_set(std::memory_order_acquire));+    if (--union_.value_.as_boxed_->strong_ == 0) {+      TypeValue* const object = union_.value_.as_boxed_->object_;+      union_.value_.as_boxed_->object_ = nullptr;+      union_.value_.as_boxed_->lock_.clear(std::memory_order_release);       BoxedValue next = object->FlatCleanup();       object->~TypeValue();-      if (--union_.value_.as_pointer_->weak_ == 0) {+      if (--union_.value_.as_boxed_->weak_ == 0) {         // NOTE: as_bytes_ contains object => ~TypeValue() must happen first.         free(union_.value_.as_bytes_);       }@@ -184,23 +190,23 @@       // This is only to skip cleanup of next.       next.union_.type_ = UnionValue::Type::EMPTY;     } else {-      union_.value_.as_pointer_->lock_.clear(std::memory_order_release);+      union_.value_.as_boxed_->lock_.clear(std::memory_order_release);       break;     }   }   union_.type_ = UnionValue::Type::EMPTY;-  union_.value_.as_pointer_ = nullptr;+  union_.value_.as_boxed_ = nullptr; }   WeakValue::WeakValue()-  : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_pointer_ = nullptr } } {}+  : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_boxed_ = nullptr } } {}  WeakValue::WeakValue(const WeakValue& other)   : union_(other.union_) {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      ++union_.value_.as_pointer_->weak_;+      ++union_.value_.as_boxed_->weak_;       break;     default:       break;@@ -213,7 +219,7 @@     union_ = other.union_;     switch (union_.type_) {       case UnionValue::Type::BOXED:-        ++union_.value_.as_pointer_->weak_;+        ++union_.value_.as_boxed_->weak_;         break;       default:         break;@@ -225,7 +231,7 @@ WeakValue::WeakValue(WeakValue&& other)   : union_(other.union_) {   other.union_.type_  = UnionValue::Type::EMPTY;-  other.union_.value_.as_pointer_ = nullptr;+  other.union_.value_.as_boxed_ = nullptr; }  WeakValue& WeakValue::operator = (WeakValue&& other) {@@ -233,7 +239,7 @@     Cleanup();     union_ = other.union_;     other.union_.type_  = UnionValue::Type::EMPTY;-    other.union_.value_.as_pointer_ = nullptr;+    other.union_.value_.as_boxed_ = nullptr;   }   return *this; }@@ -242,7 +248,7 @@   : union_(other.union_) {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      ++union_.value_.as_pointer_->weak_;+      ++union_.value_.as_boxed_->weak_;       break;     default:       break;@@ -254,7 +260,7 @@   union_ = other.union_;   switch (union_.type_) {     case UnionValue::Type::BOXED:-      ++union_.value_.as_pointer_->weak_;+      ++union_.value_.as_boxed_->weak_;       break;     default:       break;@@ -273,7 +279,7 @@ void WeakValue::Cleanup() {   switch (union_.type_) {     case UnionValue::Type::BOXED:-      if (--union_.value_.as_pointer_->weak_ == 0) {+      if (--union_.value_.as_boxed_->weak_ == 0) {         free(union_.value_.as_bytes_);       }       break;@@ -281,7 +287,7 @@       break;   }   union_.type_  = UnionValue::Type::EMPTY;-  union_.value_.as_pointer_ = nullptr;+  union_.value_.as_boxed_ = nullptr; }  }  // namespace zeolite_internal
base/src/logging.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -191,6 +191,22 @@   return argv_; } +namespace {++std::unique_ptr<std::fstream> TryOpenOutputLog(const std::string& filename) {+  if (filename.empty()) return nullptr;+  std::unique_ptr<std::fstream> file(new std::fstream(+    filename, std::ios::in | std::ios::out | std::ios::ate | std::ios::app));+  if (*file) return file;+  // Maybe the file isn't seekable.+  file.reset(new std::fstream(filename, std::ios::out));+  if (*file) return file;+  FAIL() << "Failed to open call log " << filename << " for writing";+  __builtin_unreachable();+}++}  // namespace+ unsigned int UniqueId() {   const auto time = std::chrono::steady_clock::now().time_since_epoch();   return (1000000009 * std::chrono::duration_cast<std::chrono::microseconds>(time).count());@@ -206,19 +222,8 @@ LogCallsToFile::LogCallsToFile(std::string filename)   : unique_id_(UniqueId()),     filename_(std::move(filename)),-    log_file_(filename_.empty()?-                nullptr :-                new std::fstream(filename_, std::ios::in |-                                            std::ios::out |-                                            std::ios::ate |-                                            std::ios::app)),-    cross_and_capture_to_(this) {-  if (log_file_) {-    if (!*log_file_) {-      FAIL() << "Failed to open call log " << filename_ << " for writing";-    }-  }-}+    log_file_(TryOpenOutputLog(filename_)),+    cross_and_capture_to_(this) {}  void LogCallsToFile::LogCall(const char* name, const char* at) {   if (log_file_ && enable_coverage) {
base/types.hpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -43,6 +43,9 @@ using PrimChar = char; using PrimCharBuffer = std::string; using PrimFloat = double;++class OpaqueObject { ~OpaqueObject() = default; };+using PrimPointer = OpaqueObject*;  template<int S> inline PrimString PrimString_FromLiteral(const char(&literal)[S]) {
bin/zeolite.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2020-2021 Kevin P. Barry+Copyright 2020-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -24,6 +24,7 @@ import System.Exit import GHC.IO.Handle import System.IO+import Text.Printf (printf) import qualified Data.Map as Map import qualified Data.Set as Set @@ -122,8 +123,17 @@       h <- getCompilerHash backend       expected <- fmap Set.unions $ mapCompilerM (getTraces h) ps       actual <- loadTraces-      mapM_ (errorFromIO . hPutStrLn stdout) $ Set.toList $ expected `Set.difference` actual+      let difference = expected `Set.difference` actual+      mapM_ (errorFromIO . hPutStrLn stdout) $ Set.toList $ difference+      errorFromIO $ hPutStrLn stdout $ formatCoverage difference expected     exitSuccess where+      formatCoverage difference expected =+        "Coverage: " ++ show actualSize ++ " of " ++ show expectedSize +++        " lines (" ++ printf "%.2f" coverage ++ "%)" where+          diffSize = length $ Set.toList $ difference+          expectedSize = length $ Set.toList $ expected+          actualSize = expectedSize - diffSize+          coverage = 100.0 * (fromIntegral actualSize :: Double) / (fromIntegral expectedSize :: Double)       loadTraces = do         l' <- errorFromIO $ canonicalizePath (root </> l)         errorFromIO $ hPutStrLn stderr $ "Loading trace data from \"" ++ l' ++ "\"."
example/random/.zeolite-module view
@@ -3,7 +3,6 @@ private_deps: [   "lib/util"   "lib/math"-  "lib/thread" ] mode: binary {   category: RandomDemo
lib/thread/.zeolite-module view
@@ -16,10 +16,6 @@     categories: [ProcessThread]   }   category_source {-    source: "lib/thread/src/Extension_Realtime.cpp"-    categories: [Realtime]-  }-  category_source {     source: "lib/thread/src/Extension_ThreadCondition.cpp"     categories: [ThreadCondition]   }
lib/thread/src/Extension_ProcessThread.cpp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -31,6 +31,28 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE +namespace {++class InheritTrace : public TraceContext {+ public:+  inline explicit InheritTrace(const TraceList& trace)+    : trace_(trace), capture_to_(this) {}++ private:+  void SetLocal(const char*) final {}++  void AppendTrace(TraceList& trace) const final{+    trace.insert(trace.end(), trace_.begin(), trace_.end());+  }++  const TraceContext* GetNext() const final { return nullptr; }++  const TraceList& trace_;+  const ScopedCapture capture_to_;+};++}  // namespace+ BoxedValue CreateValue_ProcessThread(S<const Type_ProcessThread> parent, const ParamsArgs& params_args);  struct ExtCategory_ProcessThread : public Category_ProcessThread {@@ -53,7 +75,7 @@     TRACE_FUNCTION("ProcessThread.detach")     S<std::thread> temp = thread;     thread = nullptr;-    if (!isJoinable(temp.get())) {+    if (!IsJoinable(temp.get())) {       FAIL() << "thread has not been started";     } else {       temp->detach();@@ -63,14 +85,14 @@    ReturnTuple Call_isRunning(const ParamsArgs& params_args) final {     TRACE_FUNCTION("ProcessThread.isRunning")-    return ReturnTuple(Box_Bool(isJoinable(thread.get())));+    return ReturnTuple(Box_Bool(IsJoinable(thread.get())));   }    ReturnTuple Call_join(const ParamsArgs& params_args) final {     TRACE_FUNCTION("ProcessThread.join")     S<std::thread> temp = thread;     thread = nullptr;-    if (!isJoinable(temp.get())) {+    if (!IsJoinable(temp.get())) {       FAIL() << "thread has not been started";     } else {       temp->join();@@ -79,8 +101,9 @@   }    ReturnTuple Call_start(const ParamsArgs& params_args) final {+    const TraceList copied_trace = TraceContext::GetTrace();     TRACE_FUNCTION("ProcessThread.start")-    if (isJoinable(thread.get())) {+    if (IsJoinable(thread.get())) {       FAIL() << "thread is already running";     } else {       // NOTE: Capture VAR_SELF so that the thread retains a reference while@@ -88,7 +111,13 @@       // the thread.       const BoxedValue self = VAR_SELF;       thread.reset(new std::thread(-        capture_thread::ThreadCrosser::WrapCall([this,self] {+        capture_thread::ThreadCrosser::WrapCall([this,self,copied_trace] {+#ifndef DISABLE_TRACING+          InheritTrace inherit_trace(copied_trace);+#endif+          // NOTE: Don't include this in copied_trace because the latter defines+          // SetLocal as a no-op.+          TRACE_FUNCTION("ProcessThread.start")           TRACE_CREATION           TypeValue::Call(routine, Function_Routine_run, PassParamsArgs());         })));@@ -96,14 +125,14 @@     return ReturnTuple(VAR_SELF);   } -  inline static bool isJoinable(std::thread* thread) {+  inline static bool IsJoinable(std::thread* thread) {     return thread && thread->joinable();   }    ~ExtValue_ProcessThread() {     S<std::thread> temp = thread;     thread = nullptr;-    if (isJoinable(temp.get())) {+    if (IsJoinable(temp.get())) {       temp->detach();     }   }
− lib/thread/src/Extension_Realtime.cpp
@@ -1,106 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--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.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--#include <errno.h>-#include <math.h>-#include <string.h>-#include <time.h>--#include <chrono>-#include <iomanip>-#include <thread>--#include "category-source.hpp"-#include "Streamlined_Realtime.hpp"-#include "Category_Float.hpp"-#include "Category_Realtime.hpp"--#ifndef SLEEP_SPINLOCK_LIMIT-#define SLEEP_SPINLOCK_LIMIT 0.001-#endif--#ifdef ZEOLITE_PUBLIC_NAMESPACE-namespace ZEOLITE_PUBLIC_NAMESPACE {-#endif  // ZEOLITE_PUBLIC_NAMESPACE--struct ExtCategory_Realtime : public Category_Realtime {-};--struct ExtType_Realtime : public Type_Realtime {-  inline ExtType_Realtime(Category_Realtime& p, Params<0>::Type params) : Type_Realtime(p, params) {}--  ReturnTuple Call_sleepSeconds(const ParamsArgs& params_args) const final {-    TRACE_FUNCTION("Realtime.sleepSeconds")-    const PrimFloat Var_arg1 = (params_args.GetArg(0)).AsFloat();-    if (Var_arg1 > 0) {-      std::this_thread::sleep_for(std::chrono::duration<double>(Var_arg1));-    }-    return ReturnTuple();-  }--  ReturnTuple Call_sleepSecondsPrecise(const ParamsArgs& params_args) const final {-    TRACE_FUNCTION("Realtime.sleepSecondsPrecise")-    const PrimFloat Var_arg1 = (params_args.GetArg(0)).AsFloat();--    const auto spinlock_limit = std::chrono::duration<double>(SLEEP_SPINLOCK_LIMIT);--    const auto target_time =-      std::chrono::high_resolution_clock::now().time_since_epoch() +-      std::chrono::duration<double>(Var_arg1);--    while (true) {-      const auto now = std::chrono::high_resolution_clock::now().time_since_epoch();-      if (now >= target_time) break;-      const auto sleep_time = target_time - now;-      if (sleep_time <= spinlock_limit) {-        while (std::chrono::high_resolution_clock::now().time_since_epoch() < target_time);-        break;-      } else {-        std::this_thread::sleep_for(sleep_time - spinlock_limit);-      }-    }--    return ReturnTuple();-  }--  ReturnTuple Call_monoSeconds(const ParamsArgs& params_args) const final {-    TRACE_FUNCTION("Realtime.monoSeconds")-    const auto time = std::chrono::steady_clock::now().time_since_epoch();-    const PrimFloat seconds =-      (PrimFloat) std::chrono::duration_cast<std::chrono::microseconds>(time).count() /-      (PrimFloat) 1000000.0;-    return ReturnTuple(Box_Float(seconds));-  }-};--Category_Realtime& CreateCategory_Realtime() {-  static auto& category = *new ExtCategory_Realtime();-  return category;-}--S<const Type_Realtime> CreateType_Realtime(const Params<0>::Type& params) {-  static const auto cached = S_get(new ExtType_Realtime(CreateCategory_Realtime(), Params<0>::Type()));-  return cached;-}--void RemoveType_Realtime(const Params<0>::Type& params) {}--#ifdef ZEOLITE_PUBLIC_NAMESPACE-}  // namespace ZEOLITE_PUBLIC_NAMESPACE-using namespace ZEOLITE_PUBLIC_NAMESPACE;-#endif  // ZEOLITE_PUBLIC_NAMESPACE
lib/thread/src/testing.0rp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -23,6 +23,12 @@   refines Routine    @type create () -> (NoOpRoutine)+}++concrete CrashRoutine {+  refines Routine++  @type create () -> (CrashRoutine) }  concrete InfiniteRoutine {
lib/thread/src/testing.0rx view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -24,6 +24,16 @@   }    run () {}+}++define CrashRoutine {+  create () {+    return CrashRoutine{ }+  }++  run () {+    fail("CrashRoutine")+  } }  define InfiniteRoutine {
lib/thread/test/thread.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -241,5 +241,35 @@   weak Thread thread <- ProcessThread.from(NoOpRoutine.create())   if (present(strong(thread))) {     fail("thread is still present")+  }+}+++testcase "creation tracing in Thread" {+  crash+  // Thread creation.+  require "CrashThread\.new"+  // Inherited trace from start call.+  require "CrashThread\.execute"+}++unittest testName {+  \ CrashThread.new().execute()+}++concrete CrashThread {+  @type new () -> (CrashThread)+  @value execute () -> ()+}++define CrashThread {+  @value ProcessThread thread++  new () {+    return CrashThread{ ProcessThread.from(CrashRoutine.create()) }+  }++  execute () {+    \ thread.start().join()   } }
− lib/thread/test/time.0rt
@@ -1,79 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--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.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "zero sleep is allowed" {-  success-}--unittest zeroSleep {-  \ Realtime.sleepSeconds(0.0)-}--unittest negativeSleep {-  \ Realtime.sleepSeconds(-0.1)-}--unittest zeroPrecise {-  \ Realtime.sleepSecondsPrecise(0.0)-}--unittest negativePrecise {-  \ Realtime.sleepSecondsPrecise(-0.1)-}---testcase "sleepSeconds() does not interfere with test timeout" {-  crash-  require "signal 14"-  timeout 1-}--unittest test {-  \ Realtime.sleepSeconds(5.0)-}---testcase "sleepSecondsPrecise() does not interfere with test timeout" {-  crash-  require "signal 14"-  timeout 1-}--unittest test {-  \ Realtime.sleepSecondsPrecise(5.0)-}---testcase "monoSeconds() diff is somewhat accurate" {-  success-  timeout 2-}--unittest testSleep {-  Float start <- Realtime.monoSeconds()-  \ Realtime.sleepSeconds(0.5)-  Float stop <- Realtime.monoSeconds()-  \ Testing.checkBetween(stop-start,0.5,0.6)-}--unittest testPrecise {-  Float start <- Realtime.monoSeconds()-  \ Realtime.sleepSecondsPrecise(0.5)-  Float stop <- Realtime.monoSeconds()-  \ Testing.checkBetween(stop-start,0.5,0.51)-}
− lib/thread/time.0rp
@@ -1,35 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--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.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--// Functions for realtime computation.-concrete Realtime {-  // Sleep for the given number of seconds.-  @type sleepSeconds (Float) -> ()--  // Sleep for the given number of seconds, with more precision.-  @type sleepSecondsPrecise (Float) -> ()--  // Get a timestamp from a monotonic clock.-  //-  // Notes:-  // - The absolute timestamp is fairly meaningless; use timestamp diffs-  //   instead, e.g., to compute the wall time of an operation.-  // - In theory, this is microsecond-precise; however, the effective resolution-  //   could depend more on the kernel's latency.-  @type monoSeconds () -> (Float)-}
lib/util/.zeolite-module view
@@ -31,6 +31,10 @@     categories: [MutexLock]   }   category_source {+    source: "lib/util/src/Extension_Realtime.cpp"+    categories: [Realtime]+  }+  category_source {     source: "lib/util/src/Extension_SimpleMutex.cpp"     categories: [SimpleMutex]   }
lib/util/counters.0rp view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -24,6 +24,8 @@   @type revZeroIndexed (Int) -> (optional Order<Int>)   // Create an unbounded Int sequence.   @type unlimited () -> (optional Order<Int>)+  // Create a counter using a builder.+  @type builder () -> (CounterBuilder) }  // Creates an Order<#x> that repeats a single value, for use with traverse.@@ -50,4 +52,53 @@   @category minMax<#x>     #x defines LessThan<#x>   (#x,#x) -> (#x,#x)+}++// Builds a sequence of values.+//+// Many of the functions here overlap; therefore, you should never need all of+// them for a single counter.+//+// Example combinations:+//+// - start+count: Set the first value and the number of items, using the default+//   increment between items.+// - count+reverse: Use the default start and default increment, but reverse the+//   order that the items are returned in.+// - start+increment+limit: Count by the specified increment starting with the+//   specified starting point until the specified limit.+concrete CounterBuilder {+  @type new () -> (#self)++  // Finalize the counter.+  @value done () -> (optional Order<Int>)++  // Set the first value in the counter.+  @value start (Int) -> (#self)++  // Set the number of items returned.+  //+  // Notes:+  // - This overrides limit.+  @value count (Int) -> (#self)++  // Set a strict threshold that will not be met or exceeded.+  //+  // Notes:+  // - If increment < 0 then all items will be > limit.+  // - If increment > 0 then all items will be < limit.+  // - This overrides count.+  @value limit (Int) -> (#self)++  // Set the difference between consecutive items.+  @value increment (Int) -> (#self)++  // Reverse the order that the items are returned in.+  //+  // Notes:+  // - This has no effect if the count is unlimited, since the last item does+  //   not exist.+  // - This has the same effect regardless of when it gets called.+  // - Calling this twice reverts the counter to forward again.+  @value reverse () -> (#self) }
+ lib/util/src/Extension_Realtime.cpp view
@@ -0,0 +1,106 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include <errno.h>+#include <math.h>+#include <string.h>+#include <time.h>++#include <chrono>+#include <iomanip>+#include <thread>++#include "category-source.hpp"+#include "Streamlined_Realtime.hpp"+#include "Category_Float.hpp"+#include "Category_Realtime.hpp"++#ifndef SLEEP_SPINLOCK_LIMIT+#define SLEEP_SPINLOCK_LIMIT 0.001+#endif++#ifdef ZEOLITE_PUBLIC_NAMESPACE+namespace ZEOLITE_PUBLIC_NAMESPACE {+#endif  // ZEOLITE_PUBLIC_NAMESPACE++struct ExtCategory_Realtime : public Category_Realtime {+};++struct ExtType_Realtime : public Type_Realtime {+  inline ExtType_Realtime(Category_Realtime& p, Params<0>::Type params) : Type_Realtime(p, params) {}++  ReturnTuple Call_sleepSeconds(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Realtime.sleepSeconds")+    const PrimFloat Var_arg1 = (params_args.GetArg(0)).AsFloat();+    if (Var_arg1 > 0) {+      std::this_thread::sleep_for(std::chrono::duration<double>(Var_arg1));+    }+    return ReturnTuple();+  }++  ReturnTuple Call_sleepSecondsPrecise(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Realtime.sleepSecondsPrecise")+    const PrimFloat Var_arg1 = (params_args.GetArg(0)).AsFloat();++    const auto spinlock_limit = std::chrono::duration<double>(SLEEP_SPINLOCK_LIMIT);++    const auto target_time =+      std::chrono::high_resolution_clock::now().time_since_epoch() ++      std::chrono::duration<double>(Var_arg1);++    while (true) {+      const auto now = std::chrono::high_resolution_clock::now().time_since_epoch();+      if (now >= target_time) break;+      const auto sleep_time = target_time - now;+      if (sleep_time <= spinlock_limit) {+        while (std::chrono::high_resolution_clock::now().time_since_epoch() < target_time);+        break;+      } else {+        std::this_thread::sleep_for(sleep_time - spinlock_limit);+      }+    }++    return ReturnTuple();+  }++  ReturnTuple Call_monoSeconds(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Realtime.monoSeconds")+    const auto time = std::chrono::steady_clock::now().time_since_epoch();+    const PrimFloat seconds =+      (PrimFloat) std::chrono::duration_cast<std::chrono::microseconds>(time).count() /+      (PrimFloat) 1000000.0;+    return ReturnTuple(Box_Float(seconds));+  }+};++Category_Realtime& CreateCategory_Realtime() {+  static auto& category = *new ExtCategory_Realtime();+  return category;+}++S<const Type_Realtime> CreateType_Realtime(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Realtime(CreateCategory_Realtime(), Params<0>::Type()));+  return cached;+}++void RemoveType_Realtime(const Params<0>::Type& params) {}++#ifdef ZEOLITE_PUBLIC_NAMESPACE+}  // namespace ZEOLITE_PUBLIC_NAMESPACE+using namespace ZEOLITE_PUBLIC_NAMESPACE;+#endif  // ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/counters.0rx view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -18,99 +18,114 @@  define Counter {   zeroIndexed (max) {-    return ForwardCounter.new(max)+    return builder().count(max).done()   }    revZeroIndexed (max) {-    return ReverseCounter.new(max)+    return builder().count(max).reverse().done()   }    unlimited () {-    return UnlimitedCounter.new()+    return builder().done()   }-} -concrete ForwardCounter {-  @type new (Int) -> (optional Order<Int>)+  builder () {+    return CounterBuilder.new()+  } } -define ForwardCounter {-  $ReadOnly[limit]$--  refines Order<Int>--  @value Int current-  @value Int limit+define CounterBuilder {+  @value Int start+  @value Int increment+  @value optional Int limit+  @value optional Int count+  @value Bool reverse -  new (max) {-    if (max > 0) {-      return #self{ 0, max }-    } else {-      return empty-    }+  new () {+    return #self{ 0, 1, empty, empty, false }   } -  next () {-    if (current < limit-1) {-      current <- current+1-      return self+  done () {+    optional Int actualCount <- defer+    if (`present` limit) {+      if (increment != 0) {+        Bool missedStart <- (`require` limit - start) % increment != 0+        // NOTE: This still works if limit < start and increment < 0.+        actualCount <- (`require` limit - start) / increment + missedStart.asInt()+      } else {+        actualCount <- empty+      }     } else {-      return empty+      actualCount <- count     }+    Int actualStart <- start+    Int actualIncrement <- increment+    if (reverse && `present` actualCount) {+      actualStart <- start + increment*(require(actualCount)-1)+      actualIncrement <- -increment+    }+    return IntCounter.new(actualStart,actualIncrement,actualCount)   } -  get () {-    return current+  start (x) {+    start <- x+    return self   }-} -concrete ReverseCounter {-  @type new (Int) -> (optional Order<Int>)-}--define ReverseCounter {-  refines Order<Int>--  @value Int current+  count (n) {+    limit <- empty+    count <- n+    return self+  } -  new (max) {-    if (max > 0) {-      return #self{ max-1 }-    } else {-      return empty-    }+  limit (x) {+    count <- empty+    limit <- x+    return self   } -  next () {-    if (current > 0) {-      current <- current-1-      return self-    } else {-      return empty-    }+  increment (x) {+    increment <- x+    return self   } -  get () {-    return current+  reverse () {+    reverse <- !reverse+    return self   } } -concrete UnlimitedCounter {-  @type new () -> (optional Order<Int>)+concrete IntCounter {+  @type new (Int,Int,optional Int) -> (optional Order<Int>) } -define UnlimitedCounter {+define IntCounter {+  $ReadOnly[increment]$+   refines Order<Int>    @value Int current+  @value Int increment+  @value optional Int remaining -  new () {-    return #self{ 0 }+  new (start,increment,count) {+    if (`present` count && `require` count <= 0) {+      return empty+    } else {+      return #self{ start, increment, count }+    }   }    next () {-    current <- current+1-    return self+    if (`present` remaining && `require` remaining - 1 <= 0) {+      return empty+    } else {+      if (`present` remaining) {+        remaining <- `require` remaining - 1+      }+      current <- current+increment+      return self+    }   }    get () {@@ -119,32 +134,33 @@ }  define Repeat {-  $ReadOnlyExcept[current]$+  $ReadOnlyExcept[counter]$    refines Order<#x>    @value #x value-  @value Int current-  @value optional Int limit+  @value Order<any> counter    times (value,max) {-    if (max <= 0) {+    optional Order<any> counter <- Counter.builder().count(max).done()+    if (! `present` counter) {       return empty     } else {-      return Repeat<#y>{ value, 0, max }+      return Repeat<#y>{ value, `require` counter }     }   }    unlimited (value) {-    return Repeat<#y>{ value, 0, empty }+    return Repeat<#y>{ value, `require` Counter.builder().done() }   }    next () {-    if (!present(limit) || current+1 < require(limit)) {-      current <- current+1-      return self-    } else {+    optional Order<any> next <- counter.next()+    if (! `present` next) {       return empty+    } else {+      counter <- `require` next+      return self     }   } 
lib/util/test/counters.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry+Copyright 2021-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -38,14 +38,14 @@ }  unittest revZeroIndexed {-  Int index <- 10+  Int index <- 9    traverse (Counter.revZeroIndexed(10) -> Int i) {-    index <- index-1     \ Testing.checkEquals(i,index)+    index <- index-1   } -  \ Testing.checkEquals(index,0)+  \ Testing.checkEquals(index,-1) }  unittest revZeroIndexedEmpty {@@ -65,6 +65,40 @@   }    \ Testing.checkEquals(index,10)+}++unittest builderReversePositive {+  // Forward sequence: 3 8 13+  Int index <- 13++  traverse (Counter.builder()+                .start(3)+                .increment(5)+                .limit(17)+                .reverse()+                .done() -> Int i) {+    \ Testing.checkEquals(i,index)+    index <- index-5+  }++  \ Testing.checkEquals(index,-2)+}++unittest builderReverseNegative {+  // Forward sequence: 17 12 7+  Int index <- 7++  traverse (Counter.builder()+                .start(17)+                .increment(-5)+                .limit(3)+                .reverse()+                .done() -> Int i) {+    \ Testing.checkEquals(i,index)+    index <- index+5+  }++  \ Testing.checkEquals(index,22) }  unittest times {
+ lib/util/test/time.0rt view
@@ -0,0 +1,79 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "zero sleep is allowed" {+  success+}++unittest zeroSleep {+  \ Realtime.sleepSeconds(0.0)+}++unittest negativeSleep {+  \ Realtime.sleepSeconds(-0.1)+}++unittest zeroPrecise {+  \ Realtime.sleepSecondsPrecise(0.0)+}++unittest negativePrecise {+  \ Realtime.sleepSecondsPrecise(-0.1)+}+++testcase "sleepSeconds() does not interfere with test timeout" {+  crash+  require "signal 14"+  timeout 1+}++unittest test {+  \ Realtime.sleepSeconds(5.0)+}+++testcase "sleepSecondsPrecise() does not interfere with test timeout" {+  crash+  require "signal 14"+  timeout 1+}++unittest test {+  \ Realtime.sleepSecondsPrecise(5.0)+}+++testcase "monoSeconds() diff is somewhat accurate" {+  success+  timeout 2+}++unittest testSleep {+  Float start <- Realtime.monoSeconds()+  \ Realtime.sleepSeconds(0.5)+  Float stop <- Realtime.monoSeconds()+  \ Testing.checkBetween(stop-start,0.5,0.6)+}++unittest testPrecise {+  Float start <- Realtime.monoSeconds()+  \ Realtime.sleepSecondsPrecise(0.5)+  Float stop <- Realtime.monoSeconds()+  \ Testing.checkBetween(stop-start,0.5,0.51)+}
+ lib/util/time.0rp view
@@ -0,0 +1,35 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++// Functions for realtime computation.+concrete Realtime {+  // Sleep for the given number of seconds.+  @type sleepSeconds (Float) -> ()++  // Sleep for the given number of seconds, with more precision.+  @type sleepSecondsPrecise (Float) -> ()++  // Get a timestamp from a monotonic clock.+  //+  // Notes:+  // - The absolute timestamp is fairly meaningless; use timestamp diffs+  //   instead, e.g., to compute the wall time of an operation.+  // - In theory, this is microsecond-precise; however, the effective resolution+  //   could depend more on the kernel's latency.+  @type monoSeconds () -> (Float)+}
src/Cli/Compiler.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2020-2021 Kevin P. Barry+Copyright 2020-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -28,7 +28,7 @@ import Control.Monad (foldM,when) import Data.Either (partitionEithers) import Data.List (isSuffixOf,nub,sort)-import Data.Time.LocalTime (getZonedTime)+import Data.Time.Clock (getCurrentTime) import System.Directory import System.FilePath import System.IO@@ -86,6 +86,7 @@  compileModule :: (PathIOHandler r, CompilerBackend b) => r -> b -> ModuleSpec -> TrackedErrorsIO () compileModule resolver backend (ModuleSpec p d ee em is is2 ps xs ts es cs ep m f pn) = do+  time <- errorFromIO getCurrentTime   as  <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is   as2 <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is2   let ca0 = Map.empty@@ -103,7 +104,6 @@                else do                  bpDeps <- loadPublicDeps compilerHash f ca2 [base]                  return $ bpDeps ++ deps1-  time <- errorFromIO getZonedTime   root <- errorFromIO $ canonicalizePath p   path <- errorFromIO $ canonicalizePath (p </> d)   extra <- errorFromIO $ sequence $ map (canonicalizePath . (p</>)) ee@@ -134,7 +134,7 @@   let paths2 = base:s0:s1:(getIncludePathsForDeps (deps1' ++ deps2)) ++ ep' ++ paths'   let hxx   = filter (isSuffixOf ".hpp" . coFilename)       fs   let other = filter (not . isSuffixOf ".hpp" . coFilename) fs-  os1 <- mapCompilerM (writeOutputFile paths2)(hxx ++ other) >>= compileGenerated+  os1 <- mapCompilerM (writeOutputFile paths2 time) (hxx ++ other) >>= compileGenerated   let files = map (\f2 -> getCachedPath (p </> d) (show $ coNamespace f2) (coFilename f2)) fs ++               map (\f2 -> p </> getSourceFile f2) es   files' <- mapCompilerM checkOwnedFile files@@ -171,7 +171,7 @@       cmLinkFlags = getLinkFlags m,       cmObjectFiles = os1' ++ osOther ++ map OtherObjectFile os'     }-  bs <- createBinary compilerHash paths' (cm2:(deps1' ++ deps2)) m mf+  bs <- createBinary compilerHash paths' (cm2:(deps1' ++ deps2)) time m mf   let cm2' = CompileMetadata {       cmVersionHash = cmVersionHash cm2,       cmRoot = cmRoot cm2,@@ -195,7 +195,7 @@       cmLinkFlags = cmLinkFlags cm2,       cmObjectFiles = cmObjectFiles cm2     }-  writeMetadata (p </> d) cm2'+  writeMetadata (p </> d) cm2' time   let traces = Set.unions $ map coPossibleTraces $ hxx ++ other   writePossibleTraces (p </> d) traces where     ep' = fixPaths $ map (p </>) ep@@ -206,9 +206,9 @@              " already defined at " ++ formatFullContextBrace (csContext cc2)            Nothing -> return ()       return $ Map.insert n cc cm-    writeOutputFile paths ca@(CxxOutput _ f2 ns _ _ _ content) = do+    writeOutputFile paths time ca@(CxxOutput _ f2 ns _ _ _ content) = do       errorFromIO $ hPutStrLn stderr $ "Writing file " ++ f2-      writeCachedFile (p </> d) (show ns) f2 $ concat $ map (++ "\n") content+      _ <- writeCachedFile (p </> d) (show ns) f2 (Just time) $ concat $ map (++ "\n") content       if isSuffixOf ".cpp" f2 || isSuffixOf ".cc" f2          then do            let f2' = getCachedPath (p </> d) (show ns) f2@@ -269,14 +269,14 @@           return $ Left $ asyncCxxCommand backend command       | isSuffixOf ".a" f2 || isSuffixOf ".o" f2 = return $ Right [f2]       | otherwise = return $ Right []-    createBinary compilerHash paths deps (CompileBinary n _ lm o lf) [CxxOutput _ _ _ ns2 req _ content] = do+    createBinary compilerHash paths deps time (CompileBinary n _ lm o lf) [CxxOutput _ _ _ ns2 req _ content] = do       f0 <- if null o                 then errorFromIO $ canonicalizePath $ p </> d </> show n                 else errorFromIO $ canonicalizePath $ p </> d </> o       let main = takeFileName f0 ++ ".cpp"       errorFromIO $ hPutStrLn stderr $ "Writing file " ++ main       let mainAbs = getCachedPath (p </> d) "main" main-      writeCachedFile (p </> d) "main" main $ concat $ map (++ "\n") content+      _ <- writeCachedFile (p </> d) "main" main (Just time) $ concat $ map (++ "\n") content       base <- resolveBaseModule resolver       deps2  <- loadPrivateDeps compilerHash f (mapMetadata deps) deps       let paths' = fixPaths $ paths ++ base:(getIncludePathsForDeps deps)@@ -293,11 +293,11 @@         getCommand LinkDynamic mainAbs f0 deps2 paths2 = do           let objects = getLibrariesForDeps deps2           return $ CompileToBinary mainAbs objects [] f0 paths2 []-    createBinary _ _ _ (CompileBinary n _ _ _ _) [] =+    createBinary _ _ _ _ (CompileBinary n _ _ _ _) [] =       compilerErrorM $ "Main category " ++ show n ++ " not found."-    createBinary _ _ _ (CompileBinary n _ _ _ _) _ =+    createBinary _ _ _ _ (CompileBinary n _ _ _ _) _ =       compilerErrorM $ "Multiple matches for main category " ++ show n ++ "."-    createBinary _ _ _ _ _ = return []+    createBinary _ _ _ _ _ _  = return []     createLibrary _ _ [] [] = return []     createLibrary name lf deps os = do       let flags = lf ++ getLinkFlagsForDeps deps@@ -314,6 +314,7 @@   Map.Map CategoryName (CategorySpec SourceContext) ->[CompileMetadata] ->   [CompileMetadata] -> TrackedErrorsIO () createModuleTemplates resolver p d ds cm deps1 deps2 = do+  time <- errorFromIO getCurrentTime   (ps,xs,_) <- findSourceFiles p (d:ds)   (LanguageModule _ _ _ cs0 ps0 ts0 cs1 ps1 ts1 _ _) <-     fmap (createLanguageModule [] Map.empty . fst) $ loadModuleGlobals resolver p (PublicNamespace,PrivateNamespace) ps Nothing deps1 deps2@@ -326,12 +327,12 @@   let ca' = foldr Set.delete ca $ map dcName ds3   let testingCats = Set.fromList $ map getCategoryName ts1   ts <- fmap concat $ mapCompilerM (\n -> generate (n `Set.member` testingCats) tm n) $ Set.toList ca'-  mapCompilerM_ writeTemplate ts where+  mapCompilerM_ (writeTemplate time) ts where     generate testing tm n = do       (_,t) <- getConcreteCategory tm ([],n)       let spec = Map.findWithDefault (CategorySpec [] [] []) (getCategoryName t) cm       generateStreamlinedTemplate testing tm t spec-    writeTemplate (CxxOutput _ n _ _ _ _ content) = do+    writeTemplate time (CxxOutput _ n _ _ _ _ content) = do       let n' = p </> d </> n       exists <- errorFromIO $ doesFileExist n'       if exists@@ -339,6 +340,10 @@          else do            errorFromIO $ hPutStrLn stderr $ "Writing file " ++ n            errorFromIO $ writeFile n' $ concat $ map (++ "\n") content+           -- This is to avoid a race condition when the module is compiled+           -- immediately after generating templates, since the former+           -- explicitly sets the metadata timestamp.+           errorFromIO $ setModificationTime n' time  runModuleTests :: (PathIOHandler r, CompilerBackend b) =>   r -> b -> FilePath -> FilePath -> [FilePath] -> LoadedTests ->@@ -359,7 +364,7 @@ loadPrivateSource :: PathIOHandler r => r -> VersionHash -> FilePath -> FilePath -> TrackedErrorsIO (PrivateSource SourceContext) loadPrivateSource resolver h p f = do   [f'] <- zipWithContents resolver p [f]-  time <- errorFromIO getZonedTime+  time <- errorFromIO getCurrentTime   path <- errorFromIO $ canonicalizePath (p </> f)   let ns = StaticNamespace $ privateNamespace $ show time ++ show h ++ path   (pragmas,cs,ds) <- parseInternalSource f'
src/Compilation/CompilerState.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -201,7 +201,7 @@   JumpContinue |   JumpBreak |   JumpReturn |-  JumpFailCall |+  JumpImmediateExit |   JumpMax  -- Max value for use as initial state in folds.   deriving (Eq,Ord,Show) 
src/CompilerCxx/Code.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -125,19 +125,21 @@ showCreationTrace = "TRACE_CREATION"  hasPrimitiveValue :: CategoryName -> Bool-hasPrimitiveValue BuiltinBool  = True-hasPrimitiveValue BuiltinInt   = True-hasPrimitiveValue BuiltinFloat = True-hasPrimitiveValue BuiltinChar  = True-hasPrimitiveValue _            = False+hasPrimitiveValue BuiltinBool    = True+hasPrimitiveValue BuiltinInt     = True+hasPrimitiveValue BuiltinFloat   = True+hasPrimitiveValue BuiltinChar    = True+hasPrimitiveValue BuiltinPointer = True+hasPrimitiveValue _              = False  isStoredUnboxed :: ValueType -> Bool isStoredUnboxed t-  | t == boolRequiredValue  = True-  | t == intRequiredValue   = True-  | t == floatRequiredValue = True-  | t == charRequiredValue  = True-  | otherwise               = False+  | t == boolRequiredValue    = True+  | t == intRequiredValue     = True+  | t == floatRequiredValue   = True+  | t == charRequiredValue    = True+  | isPointerRequiredValue t = True+  | otherwise                 = False  expressionFromLiteral :: PrimitiveType -> String -> (ExpressionType,ExpressionValue) expressionFromLiteral PrimString e =@@ -150,6 +152,7 @@   (Positional [floatRequiredValue],UnboxedPrimitive PrimFloat $ "PrimFloat(" ++ e ++ ")") expressionFromLiteral PrimBool e =   (Positional [boolRequiredValue],UnboxedPrimitive PrimBool e)+expressionFromLiteral PrimPointer _ = undefined  getFromLazy :: ExpressionValue -> ExpressionValue getFromLazy (OpaqueMulti e)        = OpaqueMulti $ e ++ ".Get()"@@ -168,94 +171,105 @@ useAsWhatever (LazySingle e)         = useAsWhatever $ getFromLazy e  useAsReturns :: ExpressionValue -> String-useAsReturns (OpaqueMulti e)                 = e-useAsReturns (WrappedSingle e)               = "ReturnTuple(" ++ e ++ ")"-useAsReturns (UnwrappedSingle e)             = "ReturnTuple(" ++ e ++ ")"-useAsReturns (BoxedPrimitive PrimBool e)     = "ReturnTuple(Box_Bool(" ++ e ++ "))"-useAsReturns (BoxedPrimitive PrimString e)   = "ReturnTuple(Box_String(" ++ e ++ "))"-useAsReturns (BoxedPrimitive PrimChar e)     = "ReturnTuple(Box_Char(" ++ e ++ "))"-useAsReturns (BoxedPrimitive PrimInt e)      = "ReturnTuple(Box_Int(" ++ e ++ "))"-useAsReturns (BoxedPrimitive PrimFloat e)    = "ReturnTuple(Box_Float(" ++ e ++ "))"-useAsReturns (UnboxedPrimitive PrimBool e)   = "ReturnTuple(Box_Bool(" ++ e ++ "))"-useAsReturns (UnboxedPrimitive PrimString e) = "ReturnTuple(Box_String(" ++ e ++ "))"-useAsReturns (UnboxedPrimitive PrimChar e)   = "ReturnTuple(Box_Char(" ++ e ++ "))"-useAsReturns (UnboxedPrimitive PrimInt e)    = "ReturnTuple(Box_Int(" ++ e ++ "))"-useAsReturns (UnboxedPrimitive PrimFloat e)  = "ReturnTuple(Box_Float(" ++ e ++ "))"-useAsReturns (LazySingle e)                  = useAsReturns $ getFromLazy e+useAsReturns (OpaqueMulti e)                  = e+useAsReturns (WrappedSingle e)                = "ReturnTuple(" ++ e ++ ")"+useAsReturns (UnwrappedSingle e)              = "ReturnTuple(" ++ e ++ ")"+useAsReturns (BoxedPrimitive PrimBool e)      = "ReturnTuple(Box_Bool(" ++ e ++ "))"+useAsReturns (BoxedPrimitive PrimString e)    = "ReturnTuple(Box_String(" ++ e ++ "))"+useAsReturns (BoxedPrimitive PrimChar e)      = "ReturnTuple(Box_Char(" ++ e ++ "))"+useAsReturns (BoxedPrimitive PrimInt e)       = "ReturnTuple(Box_Int(" ++ e ++ "))"+useAsReturns (BoxedPrimitive PrimFloat e)     = "ReturnTuple(Box_Float(" ++ e ++ "))"+useAsReturns (BoxedPrimitive PrimPointer e)   = "ReturnTuple(Box_Pointer<void>(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimBool e)    = "ReturnTuple(Box_Bool(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimString e)  = "ReturnTuple(Box_String(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimChar e)    = "ReturnTuple(Box_Char(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimInt e)     = "ReturnTuple(Box_Int(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimFloat e)   = "ReturnTuple(Box_Float(" ++ e ++ "))"+useAsReturns (UnboxedPrimitive PrimPointer e) = "ReturnTuple(Box_Pointer<void>(" ++ e ++ "))"+useAsReturns (LazySingle e)                   = useAsReturns $ getFromLazy e  useAsArgs :: ExpressionValue -> String-useAsArgs (OpaqueMulti e)                 = e-useAsArgs (WrappedSingle e)               = e-useAsArgs (UnwrappedSingle e)             = e-useAsArgs (BoxedPrimitive PrimBool e)     = "Box_Bool(" ++ e ++ ")"-useAsArgs (BoxedPrimitive PrimString e)   = "Box_String(" ++ e ++ ")"-useAsArgs (BoxedPrimitive PrimChar e)     = "Box_Char(" ++ e ++ ")"-useAsArgs (BoxedPrimitive PrimInt e)      = "Box_Int(" ++ e ++ ")"-useAsArgs (BoxedPrimitive PrimFloat e)    = "Box_Float(" ++ e ++ ")"-useAsArgs (UnboxedPrimitive PrimBool e)   = "Box_Bool(" ++ e ++ ")"-useAsArgs (UnboxedPrimitive PrimString e) = "Box_String(" ++ e ++ ")"-useAsArgs (UnboxedPrimitive PrimChar e)   = "Box_Char(" ++ e ++ ")"-useAsArgs (UnboxedPrimitive PrimInt e)    = "Box_Int(" ++ e ++ ")"-useAsArgs (UnboxedPrimitive PrimFloat e)  = "Box_Float(" ++ e ++ ")"-useAsArgs (LazySingle e)                  = useAsArgs $ getFromLazy e+useAsArgs (OpaqueMulti e)                  = e+useAsArgs (WrappedSingle e)                = e+useAsArgs (UnwrappedSingle e)              = e+useAsArgs (BoxedPrimitive PrimBool e)      = "Box_Bool(" ++ e ++ ")"+useAsArgs (BoxedPrimitive PrimString e)    = "Box_String(" ++ e ++ ")"+useAsArgs (BoxedPrimitive PrimChar e)      = "Box_Char(" ++ e ++ ")"+useAsArgs (BoxedPrimitive PrimInt e)       = "Box_Int(" ++ e ++ ")"+useAsArgs (BoxedPrimitive PrimFloat e)     = "Box_Float(" ++ e ++ ")"+useAsArgs (BoxedPrimitive PrimPointer e)   = "Box_Pointer<void>(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimBool e)    = "Box_Bool(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimString e)  = "Box_String(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimChar e)    = "Box_Char(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimInt e)     = "Box_Int(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimFloat e)   = "Box_Float(" ++ e ++ ")"+useAsArgs (UnboxedPrimitive PrimPointer e) = "Box_Pointer<void>(" ++ e ++ ")"+useAsArgs (LazySingle e)                   = useAsArgs $ getFromLazy e  useAsUnwrapped :: ExpressionValue -> String-useAsUnwrapped (OpaqueMulti e)                 = "(" ++ e ++ ").At(0)"-useAsUnwrapped (WrappedSingle e)               = e-useAsUnwrapped (UnwrappedSingle e)             = e-useAsUnwrapped (BoxedPrimitive PrimBool e)     = "Box_Bool(" ++ e ++ ")"-useAsUnwrapped (BoxedPrimitive PrimString e)   = "Box_String(" ++ e ++ ")"-useAsUnwrapped (BoxedPrimitive PrimChar e)     = "Box_Char(" ++ e ++ ")"-useAsUnwrapped (BoxedPrimitive PrimInt e)      = "Box_Int(" ++ e ++ ")"-useAsUnwrapped (BoxedPrimitive PrimFloat e)    = "Box_Float(" ++ e ++ ")"-useAsUnwrapped (UnboxedPrimitive PrimBool e)   = "Box_Bool(" ++ e ++ ")"-useAsUnwrapped (UnboxedPrimitive PrimString e) = "Box_String(" ++ e ++ ")"-useAsUnwrapped (UnboxedPrimitive PrimChar e)   = "Box_Char(" ++ e ++ ")"-useAsUnwrapped (UnboxedPrimitive PrimInt e)    = "Box_Int(" ++ e ++ ")"-useAsUnwrapped (UnboxedPrimitive PrimFloat e)  = "Box_Float(" ++ e ++ ")"-useAsUnwrapped (LazySingle e)                  = useAsUnwrapped $ getFromLazy e+useAsUnwrapped (OpaqueMulti e)                  = "(" ++ e ++ ").At(0)"+useAsUnwrapped (WrappedSingle e)                = e+useAsUnwrapped (UnwrappedSingle e)              = e+useAsUnwrapped (BoxedPrimitive PrimBool e)      = "Box_Bool(" ++ e ++ ")"+useAsUnwrapped (BoxedPrimitive PrimString e)    = "Box_String(" ++ e ++ ")"+useAsUnwrapped (BoxedPrimitive PrimChar e)      = "Box_Char(" ++ e ++ ")"+useAsUnwrapped (BoxedPrimitive PrimInt e)       = "Box_Int(" ++ e ++ ")"+useAsUnwrapped (BoxedPrimitive PrimFloat e)     = "Box_Float(" ++ e ++ ")"+useAsUnwrapped (BoxedPrimitive PrimPointer e)   = "Box_Pointer<void>(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimBool e)    = "Box_Bool(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimString e)  = "Box_String(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimChar e)    = "Box_Char(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimInt e)     = "Box_Int(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimFloat e)   = "Box_Float(" ++ e ++ ")"+useAsUnwrapped (UnboxedPrimitive PrimPointer e) = "Box_Pointer<void>(" ++ e ++ ")"+useAsUnwrapped (LazySingle e)                   = useAsUnwrapped $ getFromLazy e  useAsUnboxed :: PrimitiveType -> ExpressionValue -> String-useAsUnboxed PrimBool   (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsBool()"-useAsUnboxed PrimString (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsString()"-useAsUnboxed PrimChar   (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsChar()"-useAsUnboxed PrimInt    (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsInt()"-useAsUnboxed PrimFloat  (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsFloat()"-useAsUnboxed PrimBool   (WrappedSingle e)   = "(" ++ e ++ ").AsBool()"-useAsUnboxed PrimString (WrappedSingle e)   = "(" ++ e ++ ").AsString()"-useAsUnboxed PrimChar   (WrappedSingle e)   = "(" ++ e ++ ").AsChar()"-useAsUnboxed PrimInt    (WrappedSingle e)   = "(" ++ e ++ ").AsInt()"-useAsUnboxed PrimFloat  (WrappedSingle e)   = "(" ++ e ++ ").AsFloat()"-useAsUnboxed PrimBool   (UnwrappedSingle e) = "(" ++ e ++ ").AsBool()"-useAsUnboxed PrimString (UnwrappedSingle e) = "(" ++ e ++ ").AsString()"-useAsUnboxed PrimChar   (UnwrappedSingle e) = "(" ++ e ++ ").AsChar()"-useAsUnboxed PrimInt    (UnwrappedSingle e) = "(" ++ e ++ ").AsInt()"-useAsUnboxed PrimFloat  (UnwrappedSingle e) = "(" ++ e ++ ").AsFloat()"+useAsUnboxed PrimBool    (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsBool()"+useAsUnboxed PrimString  (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsString()"+useAsUnboxed PrimChar    (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsChar()"+useAsUnboxed PrimInt     (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsInt()"+useAsUnboxed PrimFloat   (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsFloat()"+useAsUnboxed PrimPointer (OpaqueMulti e)     = "(" ++ e ++ ").At(0).AsPointer<OpaqueObject>()"+useAsUnboxed PrimBool    (WrappedSingle e)   = "(" ++ e ++ ").AsBool()"+useAsUnboxed PrimString  (WrappedSingle e)   = "(" ++ e ++ ").AsString()"+useAsUnboxed PrimChar    (WrappedSingle e)   = "(" ++ e ++ ").AsChar()"+useAsUnboxed PrimInt     (WrappedSingle e)   = "(" ++ e ++ ").AsInt()"+useAsUnboxed PrimFloat   (WrappedSingle e)   = "(" ++ e ++ ").AsFloat()"+useAsUnboxed PrimPointer (WrappedSingle e)   = "(" ++ e ++ ").AsPointer<OpaqueObject>()"+useAsUnboxed PrimBool    (UnwrappedSingle e) = "(" ++ e ++ ").AsBool()"+useAsUnboxed PrimString  (UnwrappedSingle e) = "(" ++ e ++ ").AsString()"+useAsUnboxed PrimChar    (UnwrappedSingle e) = "(" ++ e ++ ").AsChar()"+useAsUnboxed PrimInt     (UnwrappedSingle e) = "(" ++ e ++ ").AsInt()"+useAsUnboxed PrimFloat   (UnwrappedSingle e) = "(" ++ e ++ ").AsFloat()"+useAsUnboxed PrimPointer (UnwrappedSingle e) = "(" ++ e ++ ").AsPointer<OpaqueObject>()" useAsUnboxed _ (BoxedPrimitive _ e)         = e useAsUnboxed _ (UnboxedPrimitive _ e)       = e useAsUnboxed t (LazySingle e)               = useAsUnboxed t $ getFromLazy e  valueAsWrapped :: ExpressionValue -> ExpressionValue-valueAsWrapped (UnwrappedSingle e)             = WrappedSingle e-valueAsWrapped (BoxedPrimitive _ e)            = WrappedSingle e-valueAsWrapped (UnboxedPrimitive PrimBool e)   = WrappedSingle $ "Box_Bool(" ++ e ++ ")"-valueAsWrapped (UnboxedPrimitive PrimString e) = WrappedSingle $ "Box_String(" ++ e ++ ")"-valueAsWrapped (UnboxedPrimitive PrimChar e)   = WrappedSingle $ "Box_Char(" ++ e ++ ")"-valueAsWrapped (UnboxedPrimitive PrimInt e)    = WrappedSingle $ "Box_Int(" ++ e ++ ")"-valueAsWrapped (UnboxedPrimitive PrimFloat e)  = WrappedSingle $ "Box_Float(" ++ e ++ ")"-valueAsWrapped (LazySingle e)                  = valueAsWrapped $ getFromLazy e-valueAsWrapped v                               = v+valueAsWrapped (UnwrappedSingle e)              = WrappedSingle e+valueAsWrapped (BoxedPrimitive _ e)             = WrappedSingle e+valueAsWrapped (UnboxedPrimitive PrimBool e)    = WrappedSingle $ "Box_Bool(" ++ e ++ ")"+valueAsWrapped (UnboxedPrimitive PrimString e)  = WrappedSingle $ "Box_String(" ++ e ++ ")"+valueAsWrapped (UnboxedPrimitive PrimChar e)    = WrappedSingle $ "Box_Char(" ++ e ++ ")"+valueAsWrapped (UnboxedPrimitive PrimInt e)     = WrappedSingle $ "Box_Int(" ++ e ++ ")"+valueAsWrapped (UnboxedPrimitive PrimFloat e)   = WrappedSingle $ "Box_Float(" ++ e ++ ")"+valueAsWrapped (UnboxedPrimitive PrimPointer e) = WrappedSingle $ "Box_Pointer<void>(" ++ e ++ ")"+valueAsWrapped (LazySingle e)                   = valueAsWrapped $ getFromLazy e+valueAsWrapped v                                = v  valueAsUnwrapped :: ExpressionValue -> ExpressionValue-valueAsUnwrapped (OpaqueMulti e)                 = UnwrappedSingle $ "(" ++ e ++ ").At(0)"-valueAsUnwrapped (WrappedSingle e)               = UnwrappedSingle e-valueAsUnwrapped (UnboxedPrimitive PrimBool e)   = UnwrappedSingle $ "Box_Bool(" ++ e ++ ")"-valueAsUnwrapped (UnboxedPrimitive PrimString e) = UnwrappedSingle $ "Box_String(" ++ e ++ ")"-valueAsUnwrapped (UnboxedPrimitive PrimChar e)   = UnwrappedSingle $ "Box_Char(" ++ e ++ ")"-valueAsUnwrapped (UnboxedPrimitive PrimInt e)    = UnwrappedSingle $ "Box_Int(" ++ e ++ ")"-valueAsUnwrapped (UnboxedPrimitive PrimFloat e)  = UnwrappedSingle $ "Box_Float(" ++ e ++ ")"-valueAsUnwrapped (LazySingle e)                  = valueAsUnwrapped $ getFromLazy e-valueAsUnwrapped v                               = v+valueAsUnwrapped (OpaqueMulti e)                  = UnwrappedSingle $ "(" ++ e ++ ").At(0)"+valueAsUnwrapped (WrappedSingle e)                = UnwrappedSingle e+valueAsUnwrapped (UnboxedPrimitive PrimBool e)    = UnwrappedSingle $ "Box_Bool(" ++ e ++ ")"+valueAsUnwrapped (UnboxedPrimitive PrimString e)  = UnwrappedSingle $ "Box_String(" ++ e ++ ")"+valueAsUnwrapped (UnboxedPrimitive PrimChar e)    = UnwrappedSingle $ "Box_Char(" ++ e ++ ")"+valueAsUnwrapped (UnboxedPrimitive PrimInt e)     = UnwrappedSingle $ "Box_Int(" ++ e ++ ")"+valueAsUnwrapped (UnboxedPrimitive PrimFloat e)   = UnwrappedSingle $ "Box_Float(" ++ e ++ ")"+valueAsUnwrapped (UnboxedPrimitive PrimPointer e) = UnwrappedSingle $ "Box_Pointer<void>(" ++ e ++ ")"+valueAsUnwrapped (LazySingle e)                   = valueAsUnwrapped $ getFromLazy e+valueAsUnwrapped v                                = v  variableStoredType :: ValueType -> String variableStoredType t@@ -263,6 +277,7 @@   | t == intRequiredValue    = "PrimInt"   | t == floatRequiredValue  = "PrimFloat"   | t == charRequiredValue   = "PrimChar"+  | isPointerRequiredValue t = "PrimPointer"   | isWeakValue t            = "WeakValue"   | otherwise                = "BoxedValue" @@ -275,24 +290,27 @@   | t == intRequiredValue    = "PrimInt"   | t == floatRequiredValue  = "PrimFloat"   | t == charRequiredValue   = "PrimChar"+  | isPointerRequiredValue t = "PrimPointer"   | isWeakValue t            = "WeakValue&"   | otherwise                = "BoxedValue&"  readStoredVariable :: Bool -> ValueType -> String -> ExpressionValue readStoredVariable True t s = LazySingle $ readStoredVariable False t s readStoredVariable False t s-  | t == boolRequiredValue   = UnboxedPrimitive PrimBool  s-  | t == intRequiredValue    = UnboxedPrimitive PrimInt   s-  | t == floatRequiredValue  = UnboxedPrimitive PrimFloat s-  | t == charRequiredValue   = UnboxedPrimitive PrimChar  s+  | t == boolRequiredValue   = UnboxedPrimitive PrimBool    s+  | t == intRequiredValue    = UnboxedPrimitive PrimInt     s+  | t == floatRequiredValue  = UnboxedPrimitive PrimFloat   s+  | t == charRequiredValue   = UnboxedPrimitive PrimChar    s+  | isPointerRequiredValue t = UnboxedPrimitive PrimPointer s   | otherwise                = UnwrappedSingle s  writeStoredVariable :: ValueType -> ExpressionValue -> String writeStoredVariable t e-  | t == boolRequiredValue   = useAsUnboxed PrimBool  e-  | t == intRequiredValue    = useAsUnboxed PrimInt   e-  | t == floatRequiredValue  = useAsUnboxed PrimFloat e-  | t == charRequiredValue   = useAsUnboxed PrimChar  e+  | t == boolRequiredValue   = useAsUnboxed PrimBool    e+  | t == intRequiredValue    = useAsUnboxed PrimInt     e+  | t == floatRequiredValue  = useAsUnboxed PrimFloat   e+  | t == charRequiredValue   = useAsUnboxed PrimChar    e+  | isPointerRequiredValue t = useAsUnboxed PrimPointer e   | otherwise                = useAsUnwrapped e  functionLabelType :: ScopedFunction c -> String
src/CompilerCxx/Procedure.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -269,11 +269,24 @@   fa <- csAllFilters   lift $ (checkValueAssignment r fa t0 formattedRequiredValue) <??     "In fail call at " ++ formatFullContext c-  csSetJumpType c JumpFailCall+  csSetJumpType c JumpImmediateExit   maybeSetTrace c   csWrite ["BUILTIN_FAIL(" ++ useAsUnwrapped e0 ++ ")"]+compileStatement (ExitCall c e) = do+  csAddRequired (Set.fromList [BuiltinInt])+  e' <- compileExpression e+  when (length (pValues $ fst e') /= 1) $+    compilerErrorM $ "Expected single return in argument" ++ formatFullContextBrace c+  let (Positional [t0],e0) = e'+  r <- csResolver+  fa <- csAllFilters+  lift $ (checkValueAssignment r fa t0 intRequiredValue) <??+    "In exit call at " ++ formatFullContext c+  csSetJumpType c JumpImmediateExit+  maybeSetTrace c+  csWrite ["BUILTIN_EXIT(" ++ useAsUnboxed PrimInt e0 ++ ")"] compileStatement (RawFailCall s) = do-  csSetJumpType [] JumpFailCall+  csSetJumpType [] JumpImmediateExit   csWrite ["RAW_FAIL(" ++ show s ++ ")"] compileStatement (IgnoreValues c e) = do   (_,e') <- compileExpression e
src/Module/ProcessMetadata.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2020-2021 Kevin P. Barry+Copyright 2020-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -33,7 +33,6 @@   getObjectFileResolver,   getRealPathsForDeps,   getRecompilePath,-  getSourceFilesForDeps,   isPathConfigured,   isPathUpToDate,   isPrivateSource,@@ -60,6 +59,7 @@ import Control.Monad (when) import Data.List (isSuffixOf,nub) import Data.Maybe (isJust)+import Data.Time.Clock (UTCTime) import System.Directory import System.FilePath import System.IO@@ -140,12 +140,12 @@   m <- errorFromIO $ toTrackedErrors $ loadRecompile (p </> d)   return $ not $ isCompilerError m -writeMetadata :: FilePath -> CompileMetadata -> TrackedErrorsIO ()-writeMetadata p m = do+writeMetadata :: FilePath -> CompileMetadata -> UTCTime -> TrackedErrorsIO ()+writeMetadata p m t = do   p' <- errorFromIO $ canonicalizePath p   errorFromIO $ hPutStrLn stderr $ "Writing metadata for \"" ++ p' ++ "\"."   m' <- autoWriteConfig m <?? "In data for " ++ p-  writeCachedFile p' "" metadataFilename m'+  writeCachedFile p' "" metadataFilename (Just t) m'  writeRecompile :: FilePath -> ModuleConfig -> TrackedErrorsIO () writeRecompile p m = do@@ -158,7 +158,7 @@ writePossibleTraces :: FilePath -> Set.Set String -> TrackedErrorsIO () writePossibleTraces p ts = do   p' <- errorFromIO $ canonicalizePath p-  writeCachedFile p' "" tracesFilename $ concat $ map (++"\n") $ Set.toList ts+  writeCachedFile p' "" tracesFilename Nothing $ concat $ map (++"\n") $ Set.toList ts  readPossibleTraces :: FilePath -> TrackedErrorsIO (Set.Set String) readPossibleTraces p = do@@ -190,11 +190,15 @@   errorFromIO $ createDirectoryIfMissing False d2   return d2 -writeCachedFile :: FilePath -> String -> FilePath -> String -> TrackedErrorsIO ()-writeCachedFile p ns f c = do+writeCachedFile :: FilePath -> String -> FilePath -> Maybe UTCTime -> String -> TrackedErrorsIO ()+writeCachedFile p ns f t c = do   createCachePath p   errorFromIO $ createDirectoryIfMissing False $ p </> cachedDataPath </> ns-  errorFromIO $ writeFile (getCachedPath p ns f) c+  let filename = getCachedPath p ns f+  errorFromIO $ writeFile filename c+  case t of+       Just t2 -> errorFromIO $ setModificationTime filename t2+       Nothing -> return ()  getCachedPath :: FilePath -> String -> FilePath -> FilePath getCachedPath p ns f = fixPath $ p </> cachedDataPath </> ns </> f@@ -225,9 +229,9 @@ getRealPathsForDeps :: [CompileMetadata] -> [FilePath] getRealPathsForDeps = map cmPath -getSourceFilesForDeps :: [CompileMetadata] -> [FilePath]-getSourceFilesForDeps = concat . map extract where-  extract m = map (cmRoot m </>) (filter isPublicSource $ cmPublicFiles m ++ cmPrivateFiles m)+getSourceFilesForDeps :: [CompileMetadata] -> [(FilePath,[FilePath])]+getSourceFilesForDeps = map extract where+  extract m = (cmRoot m,filter isPublicSource $ cmPublicFiles m ++ cmPrivateFiles m)  getNamespacesForDeps :: [CompileMetadata] -> [Namespace] getNamespacesForDeps = filter (not . isNoNamespace) . map cmPublicNamespace@@ -460,7 +464,7 @@   let deps2' = filter (\cm -> not $ cmPath cm `Set.member` public) deps2   cs0 <- fmap concat $ mapCompilerM (processDeps False [FromDependency])            deps1   cs1 <- fmap concat $ mapCompilerM (processDeps False [FromDependency,ModuleOnly]) deps2'-  (cs2,xa) <- loadAllPublic (ns0,ns1) fs+  (cs2,xa) <- loadAllPublic (ns0,ns1) [(p,fs)]   cs3 <- case m of               Just m2 -> processDeps True [FromDependency] m2               _       -> return []@@ -475,7 +479,7 @@                    else cs       return $ map (updateCodeVisibility (Set.union (Set.fromList ss))) cs'     loadAllPublic (ns2,ns3) fs2 = do-      fs2' <- zipWithContents r p fs2+      fs2' <- fmap concat $ mapCompilerM (uncurry $ zipWithContents r) fs2       loaded <- mapCompilerM loadPublic fs2'       return (concat $ map fst loaded,Set.fromList $ concat $ map snd loaded)       where
src/Parser/Common.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -46,6 +46,7 @@   kwElif,   kwElse,   kwEmpty,+  kwExit,   kwFail,   kwFalse,   kwIf,@@ -217,6 +218,9 @@ kwEmpty :: TextParser () kwEmpty = keyword "empty" +kwExit :: TextParser ()+kwExit = keyword "exit"+ kwFail :: TextParser () kwFail = keyword "fail" @@ -329,6 +333,7 @@     kwElif,     kwElse,     kwEmpty,+    kwExit,     kwFail,     kwFalse,     kwIf,
src/Parser/Procedure.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -131,6 +131,7 @@                  parseBreak <|>                  parseContinue <|>                  parseFailCall <|>+                 parseExitCall <|>                  parseVoid <|>                  parseAssign <|>                  parsePragma <|>@@ -161,6 +162,11 @@       kwFail       e <- between (sepAfter $ string_ "(") (sepAfter $ string_ ")") sourceParser       return $ FailCall [c] e+    parseExitCall = do+      c <- getSourceContext+      kwExit+      e <- between (sepAfter $ string_ "(") (sepAfter $ string_ ")") sourceParser+      return $ ExitCall [c] e     parseIgnore = do       c <- getSourceContext       statementStart
src/Parser/TypeInstance.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2020 Kevin P. Barry+Copyright 2019-2020,2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -81,6 +81,7 @@         | n == "Int"        = BuiltinInt         | n == "Float"      = BuiltinFloat         | n == "String"     = BuiltinString+        | n == "Pointer"    = BuiltinPointer         | n == "Formatted"  = BuiltinFormatted         | otherwise = CategoryName n 
src/Test/Procedure.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -144,6 +144,10 @@     checkShortParseSuccess "if (((var.func())?T.call())) { }",     checkShortParseSuccess "fail(\"reason\")",     checkShortParseFail "\\ fail(\"reason\")",+    checkShortParseFail "\\ fail()",+    checkShortParseSuccess "exit(1)",+    checkShortParseFail "\\ exit(1)",+    checkShortParseFail "\\ exit()",     checkShortParseSuccess "failed <- 10",      checkShortParseSuccess "\\var?T<#x>.func().func2().func3()",
src/Types/Builtin.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -28,6 +28,7 @@   floatRequiredValue,   formattedRequiredValue,   intRequiredValue,+  isPointerRequiredValue,   isOpaqueMulti,   orderOptionalValue,   stringRequiredValue,@@ -36,6 +37,7 @@ import qualified Data.Map as Map  import Base.GeneralType+import Base.MergeTree (reduceMergeTree) import Base.Positional import Types.TypeCategory import Types.TypeInstance@@ -59,6 +61,13 @@ orderOptionalValue :: GeneralInstance -> ValueType orderOptionalValue t = ValueType OptionalValue $ singleType $ JustTypeInstance $ TypeInstance BuiltinOrder (Positional [t]) +isPointerRequiredValue :: ValueType -> Bool+isPointerRequiredValue (ValueType RequiredValue t) = check $ extractSingle t where+  check (Just (JustTypeInstance (TypeInstance BuiltinPointer (Positional [_])))) = True+  check _ = False+  extractSingle = reduceMergeTree (const Nothing) (const Nothing) Just+isPointerRequiredValue _ = False+ emptyType :: ValueType emptyType = ValueType OptionalValue minBound @@ -67,7 +76,8 @@   PrimChar |   PrimInt |   PrimFloat |-  PrimString+  PrimString |+  PrimPointer   deriving (Eq,Show)  data ExpressionValue =
src/Types/Procedure.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -176,6 +176,7 @@   LoopBreak [c] |   LoopContinue [c] |   FailCall [c] (Expression c) |+  ExitCall [c] (Expression c) |   RawFailCall String |   IgnoreValues [c] (Expression c) |   Assignment [c] (Positional (Assignable c)) (Expression c) |@@ -198,6 +199,7 @@ getStatementContext (LoopBreak c)           = c getStatementContext (LoopContinue c)        = c getStatementContext (FailCall c _)          = c+getStatementContext (ExitCall c _)          = c getStatementContext (RawFailCall _)         = [] getStatementContext (IgnoreValues c _)      = c getStatementContext (Assignment c _ _)      = c
src/Types/TypeInstance.hs view
@@ -1,5 +1,5 @@ {- ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry+Copyright 2019-2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -140,6 +140,7 @@   BuiltinInt |   BuiltinFloat |   BuiltinString |+  BuiltinPointer |   BuiltinFormatted |   BuiltinOrder |   CategoryNone@@ -152,6 +153,7 @@   show BuiltinInt          = "Int"   show BuiltinFloat        = "Float"   show BuiltinString       = "String"+  show BuiltinPointer      = "Pointer"   show BuiltinFormatted    = "Formatted"   show BuiltinOrder        = "Order"   show CategoryNone        = "(none)"
tests/cli-tests.sh view
@@ -1,7 +1,7 @@ #!/usr/bin/env bash  # -------------------------------------------------------------------------------# Copyright 2020-2021 Kevin P. Barry+# Copyright 2020-2022 Kevin P. Barry # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.@@ -29,7 +29,7 @@  ZEOLITE=("$@") -if [[ "$PARALLEL" ]]; then+if [[ "${PARALLEL-}" ]]; then   PARALLEL="-j $PARALLEL" else   PARALLEL='-j 1'@@ -378,6 +378,12 @@ }  +test_pointer() {+  do_zeolite -p "$ZEOLITE_PATH" $PARALLEL -r tests/pointer -f+  do_zeolite -p "$ZEOLITE_PATH" -t tests/pointer+}++ test_self_offset() {   do_zeolite -p "$ZEOLITE_PATH" $PARALLEL -r tests/self-offset -f   do_zeolite -p "$ZEOLITE_PATH" -t tests/self-offset@@ -573,6 +579,7 @@   test_module_only2   test_module_only3   test_module_only4+  test_pointer   test_self_offset   test_show_deps   test_simulate_refs
− tests/failure.0rt
@@ -1,69 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--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.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "fail builtin" {-  crash-  require stderr "Failed"-  require stderr "failedReturn"-}--unittest test {-  Int value <- Test:failedReturn()-}--concrete Test {-  @category failedReturn () -> (Int)-}--define Test {-  failedReturn () {-    fail("Failed")-  }-}----testcase "wrong type for fail" {-  error-  require "fail"-  require "Formatted"-}--unittest test {-  fail(Value.create())-}--concrete Value {-  @type create () -> (Value)-}--define Value {-  create () {-    return Value{}-  }-}---testcase "require empty" {-  crash-  require stderr "require.+empty"-}--unittest test {-  \ require(empty)-}
+ tests/pointer/.zeolite-module view
@@ -0,0 +1,21 @@+root: "../.."+path: "tests/pointer"+private_deps: [+  "lib/testing"+]+extra_files: [+  category_source {+    source: "tests/pointer/Extension_Request.cpp"+    categories: [Request]+  }+  category_source {+    source: "tests/pointer/Extension_Response.cpp"+    categories: [Response]+  }+  category_source {+    source: "tests/pointer/Extension_Service.cpp"+    categories: [Service]+  }+  "tests/pointer/call.hpp"+]+mode: incremental {}
+ tests/pointer/Extension_Request.cpp view
@@ -0,0 +1,92 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Request.hpp"+#include "Category_Message.hpp"+#include "Category_Pointer.hpp"+#include "Category_Request.hpp"+#include "Category_String.hpp"++#include "call.hpp"++#ifdef ZEOLITE_PRIVATE_NAMESPACE+namespace ZEOLITE_PRIVATE_NAMESPACE {+#endif  // ZEOLITE_PRIVATE_NAMESPACE++BoxedValue CreateValue_Request(S<const Type_Request> parent, const ParamsArgs& params_args);++struct ExtCategory_Request : public Category_Request {+};++struct ExtType_Request : public Type_Request {+  inline ExtType_Request(Category_Request& p, Params<0>::Type params) : Type_Request(p, params) {}++  ReturnTuple Call_create(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Request.create")+    return ReturnTuple(CreateValue_Request(PARAM_SELF, params_args));+  }++  ReturnTuple Call_getData(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Request.getData")+    return ReturnTuple(Box_String(params_args.GetArg(0).AsPointer<Request>()->request_data));+  }+};++struct ExtValue_Request : public Value_Request {+  inline ExtValue_Request(S<const Type_Request> p, const ParamsArgs& params_args)+    : Value_Request(std::move(p)),+      request_{params_args.GetArg(0).AsString()} {}++  ReturnTuple Call_asMessage(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Request.asMessage")+    return ReturnTuple(Box_Pointer<Message>(&request_));+  }++  ReturnTuple Call_asRequest(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Request.asRequest")+    return ReturnTuple(Box_Pointer<Request>(&request_));+  }++  ReturnTuple Call_formatted(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Request.formatted")+    return ReturnTuple(Box_String(request_.request_data));+  }++  Request request_;+};++Category_Request& CreateCategory_Request() {+  static auto& category = *new ExtCategory_Request();+  return category;+}++S<const Type_Request> CreateType_Request(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Request(CreateCategory_Request(), Params<0>::Type()));+  return cached;+}++void RemoveType_Request(const Params<0>::Type& params) {}+BoxedValue CreateValue_Request(S<const Type_Request> parent, const ParamsArgs& params_args) {+  return BoxedValue::New<ExtValue_Request>(std::move(parent), params_args);+}++#ifdef ZEOLITE_PRIVATE_NAMESPACE+}  // namespace ZEOLITE_PRIVATE_NAMESPACE+using namespace ZEOLITE_PRIVATE_NAMESPACE;+#endif  // ZEOLITE_PRIVATE_NAMESPACE
+ tests/pointer/Extension_Response.cpp view
@@ -0,0 +1,92 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Response.hpp"+#include "Category_Message.hpp"+#include "Category_Pointer.hpp"+#include "Category_Response.hpp"+#include "Category_String.hpp"++#include "call.hpp"++#ifdef ZEOLITE_PRIVATE_NAMESPACE+namespace ZEOLITE_PRIVATE_NAMESPACE {+#endif  // ZEOLITE_PRIVATE_NAMESPACE++BoxedValue CreateValue_Response(S<const Type_Response> parent, const ParamsArgs& params_args);++struct ExtCategory_Response : public Category_Response {+};++struct ExtType_Response : public Type_Response {+  inline ExtType_Response(Category_Response& p, Params<0>::Type params) : Type_Response(p, params) {}++  ReturnTuple Call_create(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Response.create")+    return ReturnTuple(CreateValue_Response(PARAM_SELF, params_args));+  }++  ReturnTuple Call_getData(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Response.getData")+    return ReturnTuple(Box_String(params_args.GetArg(0).AsPointer<Response>()->response_data));+  }+};++struct ExtValue_Response : public Value_Response {+  inline ExtValue_Response(S<const Type_Response> p, const ParamsArgs& params_args)+    : Value_Response(std::move(p)),+      response_{params_args.GetArg(0).AsString()} {}++  ReturnTuple Call_asMessage(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Response.asMessage")+    return ReturnTuple(Box_Pointer<Message>(&response_));+  }++  ReturnTuple Call_asResponse(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Response.asResponse")+    return ReturnTuple(Box_Pointer<Response>(&response_));+  }++  ReturnTuple Call_formatted(const ParamsArgs& params_args) final {+    TRACE_FUNCTION("Response.formatted")+    return ReturnTuple(Box_String(response_.response_data));+  }++  Response response_;+};++Category_Response& CreateCategory_Response() {+  static auto& category = *new ExtCategory_Response();+  return category;+}++S<const Type_Response> CreateType_Response(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Response(CreateCategory_Response(), Params<0>::Type()));+  return cached;+}++void RemoveType_Response(const Params<0>::Type& params) {}+BoxedValue CreateValue_Response(S<const Type_Response> parent, const ParamsArgs& params_args) {+  return BoxedValue::New<ExtValue_Response>(std::move(parent), params_args);+}++#ifdef ZEOLITE_PRIVATE_NAMESPACE+}  // namespace ZEOLITE_PRIVATE_NAMESPACE+using namespace ZEOLITE_PRIVATE_NAMESPACE;+#endif  // ZEOLITE_PRIVATE_NAMESPACE
+ tests/pointer/Extension_Service.cpp view
@@ -0,0 +1,82 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Service.hpp"+#include "Category_Request.hpp"+#include "Category_Response.hpp"+#include "Category_Service.hpp"++#include "call.hpp"++#ifdef ZEOLITE_PRIVATE_NAMESPACE+namespace ZEOLITE_PRIVATE_NAMESPACE {+#endif  // ZEOLITE_PRIVATE_NAMESPACE++BoxedValue CreateValue_Service(S<const Type_Service> parent, const ParamsArgs& params_args);++struct ExtCategory_Service : public Category_Service {+};++struct ExtType_Service : public Type_Service {+  inline ExtType_Service(Category_Service& p, Params<0>::Type params) : Type_Service(p, params) {}++  ReturnTuple Call_send(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Service.send")+    const Request* const request = params_args.GetArg(0).AsPointer<Request>();+    Response* const response = params_args.GetArg(1).AsPointer<Response>();+    if (!request) FAIL() << "Invalid Pointer<Request>";+    if (!response) FAIL() << "Invalid Pointer<Response>";+    response->response_data = request->request_data + " has been processed as Request";+    return ReturnTuple();+  }++  ReturnTuple Call_send2(const ParamsArgs& params_args) const final {+    TRACE_FUNCTION("Service.send2")+    const Message* const request = params_args.GetArg(0).AsPointer<Message>();+    Response* const response = params_args.GetArg(1).AsPointer<Response>();+    if (!request) FAIL() << "Invalid Pointer<Message>";+    if (!response) FAIL() << "Invalid Pointer<Response>";+    response->response_data = request->message_data + " has been processed as Message";+    return ReturnTuple();+  }+};++struct ExtValue_Service : public Value_Service {+  inline ExtValue_Service(S<const Type_Service> p, const ParamsArgs& params_args) : Value_Service(std::move(p)) {}+};++Category_Service& CreateCategory_Service() {+  static auto& category = *new ExtCategory_Service();+  return category;+}++S<const Type_Service> CreateType_Service(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Service(CreateCategory_Service(), Params<0>::Type()));+  return cached;+}++void RemoveType_Service(const Params<0>::Type& params) {}+BoxedValue CreateValue_Service(S<const Type_Service> parent, const ParamsArgs& params_args) {+  return BoxedValue::New<ExtValue_Service>(std::move(parent), params_args);+}++#ifdef ZEOLITE_PRIVATE_NAMESPACE+}  // namespace ZEOLITE_PRIVATE_NAMESPACE+using namespace ZEOLITE_PRIVATE_NAMESPACE;+#endif  // ZEOLITE_PRIVATE_NAMESPACE
+ tests/pointer/README.md view
@@ -0,0 +1,11 @@+# C++ Pointer Wrapping Test++This module tests `Pointer<#x>` in C++ extensions.++To compile and execute:++```shell+ZEOLITE_PATH=$(zeolite --get-path)+zeolite -p $ZEOLITE_PATH -r tests/pointer+zeolite -p $ZEOLITE_PATH -t tests/pointer+```
+ tests/pointer/call.0rp view
@@ -0,0 +1,50 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$+$TestsOnly$++@value interface Message {+  asMessage () -> (Pointer<Message>)+}++// NOTE: Inheriting Message doesn't matter for the purposes of Pointer; it's+// just an arbitrary substitution for type checking.++concrete Request {+  refines Formatted+  refines Message++  @type create (String) -> (#self)+  @type getData (Pointer<Request>) -> (String)+  @value asRequest () -> (Pointer<Request>)+}++concrete Response {+  refines Formatted+  refines Message++  @type create (String) -> (#self)+  @type getData (Pointer<Response>) -> (String)+  @value asResponse () -> (Pointer<Response>)+}++concrete Service {+  @type send (Pointer<Request>,Pointer<Response>) -> ()+  @type send2 (Pointer<Message>,Pointer<Response>) -> ()+}
+ tests/pointer/call.0rt view
@@ -0,0 +1,100 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "Pointer passing" {+  success+}++concrete CallHelper {+  @type call (String) -> (String)+  @type call2 (String) -> (String)+}++define CallHelper {+  call (data) {+    Request request <- Request.create(data)+    Response response <- Response.create("")+    // This is just meant to test code generated for passing Pointer as a+    // function arg or return.+    return Response.getData(callWithPointers(request.asRequest(),response.asResponse()))+  }++  call2 (data) {+    Request request <- Request.create(data)+    Response response <- Response.create("")+    \ Service.send2(request.asMessage(),response.asResponse())+    return response.formatted()+  }++  @type callWithPointers (Pointer<Request>,Pointer<Response>) -> (Pointer<Response>)+  callWithPointers (request,response) {+    \ Service.send(request,response)+    return response+  }+}++unittest test {+  \ CallHelper.call("DATA") `Testing.checkEquals` "DATA has been processed as Request"+  \ CallHelper.call2("DATA") `Testing.checkEquals` "REQUEST has been processed as Message"+}+++testcase "Pointer storage" {+  success+}++concrete Helper {+  @type new () -> (Helper)+  @value call () -> ()+}++define Helper {+  @category Request request <- Request.create("request")+  @category Pointer<Request> requestPointer <- request.asRequest()+  @value Response response+  @value Pointer<Response> responsePointer++  new () {+    Response response <- Response.create("response")+    return #self{ response, response.asResponse() }+  }++  call () {+    \ Request.getData(requestPointer) `Testing.checkEquals` "request"+    \ Response.getData(responsePointer) `Testing.checkEquals` "response"++    Pointer<Request> localRequest <- requestPointer+    Pointer<Response> localResponse <- responsePointer+    \ Request.getData(localRequest) `Testing.checkEquals` "request"+    \ Response.getData(localResponse) `Testing.checkEquals` "response"++    localRequest <- request.asRequest()+    localResponse <- response.asResponse()+    \ Request.getData(localRequest) `Testing.checkEquals` "request"+    \ Response.getData(localResponse) `Testing.checkEquals` "response"++    requestPointer <- localRequest+    responsePointer <- localResponse+    \ Request.getData(requestPointer) `Testing.checkEquals` "request"+    \ Response.getData(responsePointer) `Testing.checkEquals` "response"+  }+}++unittest test {+  \ Helper.new().call()+}
+ tests/pointer/call.hpp view
@@ -0,0 +1,53 @@+/* -----------------------------------------------------------------------------+Copyright 2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#ifndef CALL_H_+#define CALL_H_++#include <string>++struct Message {+  std::string message_data;+};++struct OtherBase {+  std::string other_base_data;+};++// NOTE: The inheritance of Message here doesn't need to match the inheritance+// in call.0rp because inheritance doesn't matter for Pointer.++// The virtual base class is meant to change the offset.+struct Request : public OtherBase, virtual public Message {+  explicit inline Request(std::string data)+    : Message{ "REQUEST" },+      request_data(std::move(data)) {}++  std::string request_data;+};++// The virtual base class is meant to change the offset.+struct Response : public OtherBase, virtual public Message {+  explicit inline Response(std::string data)+    : Message{ "RESPONSE" },+      response_data(std::move(data)) {}++  std::string response_data;+};++#endif  // CALL_H_
+ tests/program-exit.0rt view
@@ -0,0 +1,121 @@+/* -----------------------------------------------------------------------------+Copyright 2020,2022 Kevin P. Barry++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.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "fail builtin" {+  crash+  require stderr "Failed"+  require stderr "failedReturn"+}++unittest test {+  Int value <- Test:failedReturn()+}++concrete Test {+  @category failedReturn () -> (Int)+}++define Test {+  failedReturn () {+    // Calling within a function also validates that return checks are skipped.+    fail("Failed")+  }+}+++testcase "wrong type for fail" {+  error+  require "fail"+  require "Formatted"+}++unittest test {+  fail(Value.create())+}++concrete Value {+  @type create () -> (Value)+}++define Value {+  create () {+    return Value{}+  }+}+++testcase "exit builtin zero" {+  success+}++unittest test {+  Int value <- Test:exitReturn()+}++concrete Test {+  @category exitReturn () -> (Int)+}++define Test {+  exitReturn () {+    // Calling within a function also validates that return checks are skipped.+    exit(0)+  }+}+++testcase "exit builtin non-zero" {+  crash+  // Should not output a stack trace.+  exclude "exitReturn"+}++unittest test {+  Int value <- Test:exitReturn()+}++concrete Test {+  @category exitReturn () -> (Int)+}++define Test {+  exitReturn () {+    exit(1)+  }+}+++testcase "wrong type for exit" {+  error+  require "exit"+  require "Int"+}++unittest test {+  exit("message")+}+++testcase "require empty" {+  crash+  require stderr "require.+empty"+}++unittest test {+  \ require(empty)+}
tests/simple.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry+Copyright 2020,2022 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -135,3 +135,13 @@ unittest test {   while (true) {} }+++testcase "args escaped properly" {+  success+  // If the compiler uses quotes without escaping individual characters then+  // main will exit with an error without executing the test.+  args "\" }; exit(1); const char fake[] = { \""+}++unittest test {}
tests/simulate-refs/.zeolite-module view
@@ -9,7 +9,6 @@ private_deps: [   "lib/container"   "lib/math"-  "lib/thread"   "lib/util" ] mode: binary {
zeolite-lang.cabal view
@@ -1,7 +1,7 @@ cabal-version:       2.2  name:                zeolite-lang-version:             0.22.1.0+version:             0.23.0.0 synopsis:            Zeolite is a statically-typed, general-purpose programming language.  description:@@ -59,7 +59,7 @@ license-file:        LICENSE author:              Kevin P. Barry maintainer:          Kevin P. Barry <ta0kira@gmail.com>-copyright:           (c) Kevin P. Barry 2019-2021+copyright:           (c) Kevin P. Barry 2019-2022 category:            Compiler build-type:          Simple @@ -174,6 +174,12 @@                      tests/module-only4/*.0rp,                      tests/module-only4/*.0rt,                      tests/module-only4/*.0rx,+                     tests/pointer/README.md,+                     tests/pointer/.zeolite-module,+                     tests/pointer/*.cpp,+                     tests/pointer/*.hpp,+                     tests/pointer/*.0rp,+                     tests/pointer/*.0rt,                      tests/self-offset/README.md,                      tests/self-offset/.zeolite-module,                      tests/self-offset/*.cpp,