ebafc720a90f — Leonard Ritter 2 months ago
* merged default branch
A => README.md +43 -0
@@ 0,0 1,43 @@ 
+![scopes_logo_210px.png](https://hg.sr.ht/~duangle/scopes/raw/default/extras/images/scopes_logo_210px.png)
+
+Welcome to Scopes
+-----------------
+
+Scopes is a retargetable programming language & infrastructure.
+
+See [Tags](https://hg.sr.ht/~duangle/scopes/tags) for releases.
+
+Features
+--------
+
+* A performant systems programming language that feels like a scripting language.
+* Aims to combine the **expressivity** of Scheme with the **convenience** of Python, Lua or Javascript and the **performance** of C.
+* Compile-time **resource management** using view propagation, an annotation-free variation of **borrow checking**.
+* A single codebase targets native 64-bit **Linux**, **Windows** and **macOS** as well as **SPIR-V** and **GLSL** shaders. Your math functions run on CPU and GPU without modification.
+* **Fully interoperable** with C libraries. Import and use C include files directly.
+* Support for **live code execution** as well as **offline compilation** of object files.
+* **Statically typed** but **fully inferred** type system via forward propagation. Supports **closures** as zero-cost abstraction.
+* **Open Source** [MIT licensed](http://opensource.org/licenses/MIT).
+* Built with [LLVM](http://llvm.org/) and [clang](http://llvm.org/) for **multi-stage programming** features and **overhead-free** interoperability with C libraries.
+
+See [Documentation](http://scopes.readthedocs.io/en/latest/) for more information.
+
+Editor Support
+--------------
+
+* [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=duangle.scopes#overview)
+* [Vim](https://github.com/radgeRayden/vim-scopes)
+* [Emacs](https://github.com/radgeRayden/emacs-scopes-mode)
+
+Help & Support
+--------------
+
+For help, questions and feedback:
+
+* Subscribe to the [mailing list](https://lists.sr.ht/~duangle/scopes)
+* Join the #scopes IRC channel on [Freenode](https://freenode.net/)
+* Write an [e-mail](mailto:support@duangle.com)
+
+For bug reports:
+
+* [Submit a ticket](https://todo.sr.ht/~duangle/scopes)

          
M lib/scopes/Option.sc +3 -3
@@ 60,15 60,15 @@ inline Option (T)
         inline __rimply (other-cls cls)
             option-rimply other-cls cls T
 
-        inline unwrap (self)
+        fn force-unwrap (self)
             assert self "unwrapping empty Option failed"
             extract-payload self T
 
-        inline try-unwrap (self)
+        fn unwrap (self)
             if (not self)
                 raise (UnwrapError)
             extract-payload self T
 
 do
-    let Option
+    let Option UnwrapError
     locals;

          
M lib/scopes/Rc.sc +79 -33
@@ 10,8 10,6 @@ 
     module provides a strong reference type `Rc`, as well as a weak reference
     type `Weak`.
 
-using import Option
-
 let
     PAYLOAD_INDEX = 0
     METADATA_INDEX = 1

          
@@ 43,7 41,8 @@ inline gen-type (T)
         let Type = T
         let RcType = RcType
 
-        inline... __typecall (cls, value : RcType)
+        inline... __typecall
+        case (cls, value : RcType)
             let md = (extractvalue value METADATA_INDEX)
             let refcount =
                 getelementptr

          
@@ 52,6 51,10 @@ inline gen-type (T)
             let rc = (add (load refcount) 1)
             store rc refcount
             bitcast (dupe (view value)) this-type
+        case (cls)
+            # null-weak that will never upgrade
+            let self = (nullof storage-type)
+            bitcast self this-type
 
     typedef+ RcType
         let Type = T

          
@@ 72,20 75,24 @@ inline gen-type (T)
             wrap (T args...)
     RcType
 
+typedef UpgradeError : (tuple)
+    inline __typecall (cls)
+        bitcast none this-type
+
 typedef+ ReferenceCounted
     fn strong-count (value)
         viewing value
-        load
-            getelementptr
-                extractvalue value METADATA_INDEX
-                \ 0 STRONGRC_INDEX
+        let md = (extractvalue value METADATA_INDEX)
+        if (not (ptrtoint md usize))
+            return 0
+        load (getelementptr md 0 STRONGRC_INDEX)
 
     fn weak-count (value)
         viewing value
-        load
-            getelementptr
-                extractvalue value METADATA_INDEX
-                \ 0 WEAKRC_INDEX
+        let md = (extractvalue value METADATA_INDEX)
+        if (not (ptrtoint md usize))
+            return 1
+        load (getelementptr md 0 WEAKRC_INDEX)
 
 typedef+ Weak
     inline... __typecall

          
@@ 94,9 101,11 @@ typedef+ Weak
 
     fn _drop (self)
         let md = (extractvalue self METADATA_INDEX)
+        if (not (ptrtoint md usize))
+            return;
         let refcount = (getelementptr md 0 WEAKRC_INDEX)
         let rc = (sub (load refcount) 1)
-        assert (rc >= 0)
+        assert (rc >= 0) "corrupt refcount encountered"
         store rc refcount
         if (rc == 0)
             let strongrefcount = (getelementptr md 0 STRONGRC_INDEX)

          
@@ 107,22 116,50 @@ typedef+ Weak
     inline __drop (self)
         _drop (deref self)
 
+    @@ memo
+    inline __== (cls other-cls)
+        static-if (cls == other-cls)
+            fn (self other)
+                == (extractvalue self METADATA_INDEX) (extractvalue other METADATA_INDEX)
+
+    fn... clone (self : Weak,)
+        viewing self
+        let md = (extractvalue self METADATA_INDEX)
+        if (ptrtoint md usize)
+            let refcount =
+                getelementptr md 0 WEAKRC_INDEX
+            let rc = (add (load refcount) 1)
+            store rc refcount
+        deref (dupe self)
+
     fn upgrade (self)
         viewing self
-        let refcount =
-            getelementptr
-                extractvalue self METADATA_INDEX
-                \ 0 STRONGRC_INDEX
+        let md = (extractvalue self METADATA_INDEX)
+        if (not (ptrtoint md usize))
+            raise (UpgradeError)
+        let refcount = (getelementptr md 0 STRONGRC_INDEX)
         let rc = (load refcount)
-        assert (rc >= 0)
+        assert (rc >= 0) "corrupt refcount encountered"
+        if (rc == 0)
+            raise (UpgradeError)
         let RcType = ((typeof self) . RcType)
-        let OptionType = (Option RcType)
-        if (rc == 0)
-            OptionType none
-        else
-            let rc = (add rc 1)
-            store rc refcount
-            OptionType (bitcast (dupe self) RcType)
+        let rc = (add rc 1)
+        store rc refcount
+        deref (bitcast (dupe self) RcType)
+
+    fn force-upgrade (self)
+        viewing self
+        let md = (extractvalue self METADATA_INDEX)
+        assert (ptrtoint md usize) "upgrading Weak failed"
+        let refcount = (getelementptr md 0 STRONGRC_INDEX)
+        let rc = (load refcount)
+        assert (rc >= 0) "corrupt refcount encountered"
+        assert (rc > 0) "upgrading Weak failed"
+        let RcType = ((typeof self) . RcType)
+        let rc = (add rc 1)
+        store rc refcount
+        deref (bitcast (dupe self) RcType)
+
 
 typedef+ Rc
     inline... __typecall

          
@@ 132,29 169,38 @@ typedef+ Rc
     inline new (T args...)
         (gen-type T) args...
 
-    fn... clone (value : Rc,)
+    fn... clone
+    case (value : Rc,)
         viewing value
         let refcount =
             getelementptr
                 extractvalue value METADATA_INDEX
                 \ 0 STRONGRC_INDEX
-        let rc = (add (load refcount) 1)
+        let rc = (load refcount)
+        assert (rc >= 0) "corrupt refcount encountered"
+        let rc = (add rc 1)
         store rc refcount
-        dupe value
+        deref (dupe value)
+    case (value : Weak,)
+        viewing value
+        'clone value
 
     inline wrap (value)
         ((gen-type (typeof value)) . wrap) value
 
     let _view = view
-    inline view (self)
+    inline... view (self : Rc,)
         ptrtoref (deref (extractvalue self PAYLOAD_INDEX))
 
     inline __countof (self)
         countof (view self)
 
-    spice __= (selfT otherT)
-        inline (lhs rhs)
-            (view lhs) = rhs
+    inline __= (selfT otherT)
+        static-if (selfT == otherT)
+            super-type.__= selfT otherT
+        else
+            inline (lhs rhs)
+                (view lhs) = rhs
 
     inline __@ (self keys...)
         @ (view self) keys...

          
@@ 197,7 243,7 @@ typedef+ Rc
         let md = (extractvalue self METADATA_INDEX)
         let refcount = (getelementptr md 0 STRONGRC_INDEX)
         let rc = (sub (load refcount) 1)
-        assert (rc >= 0)
+        assert (rc >= 0) "corrupt refcount encountered"
         if (rc == 0)
             let payload = (view self)
             __drop payload

          
@@ 216,5 262,5 @@ typedef+ Rc
     unlet _view _drop
 
 do
-    let Rc Weak
+    let Rc Weak UpgradeError
     locals;

          
M lib/scopes/core.sc +5 -5
@@ 4557,12 4557,12 @@ inline rrange (a b c)
         static-branch (none? b)
             inline () a
             inline () b
-    let to = (((to - from + (step - 1)) // step) * step - step + from)
+    let to = (((to - from + (step - 1)) // step) * step + from)
     Generator
-        inline () to
-        inline (x) (x >= from)
-        inline (x) x
-        inline (x) (x - step)
+        inline () (_ to (to - step))
+        inline (x0 x-1) (x0 > from)
+        inline (x0 x-1) x-1
+        inline (x0 x-1) (_ x-1 (x-1 - step))
 
 let parse-compile-flags =
     spice-macro

          
M lib/scopes/enum.sc +1 -1
@@ 34,7 34,7 @@ fn _extract-payload (enum-value extractT
             let ptrET = ('change-storage-class
                 ('mutable (pointer.type extractT)) 'Function)
             spice-quote
-                let ptr = (alloca payload-cls)
+                let ptr = (alloca (qualifiersof raw-payload))
                 store raw-payload ptr
                 let ptr = (bitcast ptr ptrET)
                 load ptr

          
M lib/scopes/testing.sc +10 -2
@@ 84,8 84,12 @@ define-sugar-macro test-error
             false
         except (err)
             io-write! "ASSERT OK: "
-            print
-                'format err
+            static-if ((typeof err) == Error)
+                print
+                    'format err
+            else
+                print
+                    typeof err
             true
 
     inline assertion-error! (msg)

          
@@ 213,6 217,10 @@ typedef One :: (tuple i32 (mutable point
     fn refcount ()
         deref _refcount
 
+    fn reset-refcount ()
+        _refcount = 0
+        ;
+
     fn test-refcount-balanced ()
         # this also fixes the refcount for subsequent tests
         let balanced? = (_refcount == 0)

          
M src/gen_llvm.cpp +7 -1
@@ 2391,9 2391,15 @@ struct LLVMIRGenerator {
         LLVMValueRef func = LLVMGetBasicBlockParent(old_bb);
         LLVMMetadataRef disp = LLVMGetSubprogram(func);
 
+        LLVMMetadataRef scope = disp;
+        if (active_function.anchor()->path != anchor->path) {
+            LLVMMetadataRef difile = source_file_to_scope(anchor->path);
+            scope = LLVMDIBuilderCreateLexicalBlock(di_builder, disp, difile, 1, 1);
+        }
+
         LLVMMetadataRef result = LLVMDIBuilderCreateDebugLocation(
             LLVMGetGlobalContext(),
-            anchor->lineno, anchor->column, disp, nullptr);
+            anchor->lineno, anchor->column, scope, nullptr);
 
         return result;
     }

          
M src/platform_abi.cpp +31 -0
@@ 36,6 36,37 @@ const char *abi_class_to_string(ABIClass
 #undef DEF_ABI_CLASS_NAMES
 
 #ifdef SCOPES_WIN32
+
+ABIClass merge_abi_classes(ABIClass class1, ABIClass class2) {
+    if (class1 == class2)
+        return class1;
+
+    if (class1 == ABI_CLASS_NO_CLASS)
+        return class2;
+    if (class2 == ABI_CLASS_NO_CLASS)
+        return class1;
+
+    if (class1 == ABI_CLASS_MEMORY || class2 == ABI_CLASS_MEMORY)
+        return ABI_CLASS_MEMORY;
+
+    if ((class1 == ABI_CLASS_INTEGERSI && class2 == ABI_CLASS_SSESF)
+        || (class2 == ABI_CLASS_INTEGERSI && class1 == ABI_CLASS_SSESF))
+        return ABI_CLASS_INTEGERSI;
+    if (class1 == ABI_CLASS_INTEGER || class1 == ABI_CLASS_INTEGERSI
+        || class2 == ABI_CLASS_INTEGER || class2 == ABI_CLASS_INTEGERSI)
+        return ABI_CLASS_INTEGER;
+
+    if (class1 == ABI_CLASS_X87
+        || class1 == ABI_CLASS_X87UP
+        || class1 == ABI_CLASS_COMPLEX_X87
+        || class2 == ABI_CLASS_X87
+        || class2 == ABI_CLASS_X87UP
+        || class2 == ABI_CLASS_COMPLEX_X87)
+        return ABI_CLASS_MEMORY;
+
+    return ABI_CLASS_SSE;
+}
+
 #else
 // x86-64 PS ABI based on https://www.uclibc.org/docs/psABI-x86_64.pdf
 

          
M src/prover.cpp +29 -5
@@ 3006,8 3006,14 @@ repeat:
         case FN_Alloca: {
             CHECKARGS(1, 1);
             READ_TYPE_CONST(T);
-            auto op = Alloca::from(T);
-            op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            auto ST = strip_qualifiers(T);
+            auto op = Alloca::from(ST);
+            auto vq = try_view(T);
+            if (vq) {
+                op->hack_change_value(view_type(op->get_type(), vq->ids));
+            } else {
+                op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            }
             return TypedValueRef(call.anchor(), op);
         } break;
         case FN_AllocaArray: {

          
@@ 3015,15 3021,27 @@ repeat:
             READ_TYPE_CONST(T);
             READ_STORAGETYPEOF(size);
             SCOPES_CHECK_RESULT(verify_integer(size));
+            auto ST = strip_qualifiers(T);
             auto op = Alloca::from(T, _size);
-            op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            auto vq = try_view(T);
+            if (vq) {
+                op->hack_change_value(view_type(op->get_type(), vq->ids));
+            } else {
+                op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            }
             return TypedValueRef(call.anchor(), op);
         } break;
         case FN_Malloc: {
             CHECKARGS(1, 1);
             READ_TYPE_CONST(T);
+            auto ST = strip_qualifiers(T);
             auto op = Malloc::from(T);
-            op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            auto vq = try_view(T);
+            if (vq) {
+                op->hack_change_value(view_type(op->get_type(), vq->ids));
+            } else {
+                op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            }
             return TypedValueRef(call.anchor(), op);
         } break;
         case FN_MallocArray: {

          
@@ 3031,8 3049,14 @@ repeat:
             READ_TYPE_CONST(T);
             READ_STORAGETYPEOF(size);
             SCOPES_CHECK_RESULT(verify_integer(size));
+            auto ST = strip_qualifiers(T);
             auto op = Malloc::from(T, _size);
-            op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            auto vq = try_view(T);
+            if (vq) {
+                op->hack_change_value(view_type(op->get_type(), vq->ids));
+            } else {
+                op->hack_change_value(UNIQUETYPE1(op->get_type()));
+            }
             return TypedValueRef(call.anchor(), op);
         } break;
         case FN_Free: {

          
M src/qualifier/unique_qualifiers.cpp +1 -0
@@ 156,6 156,7 @@ ViewQualifier::ViewQualifier(const IDSet
 void ViewQualifier::stream_prefix(StyledStream &ss) const {
     ss << "%";
     if (sorted_ids.empty()) {
+        ss << "?";
     } else {
         for (int i = 0; i < sorted_ids.size(); ++i) {
             if (i > 0) ss << "|";

          
M testing/makemain.sc +10 -7
@@ 6,11 6,14 @@ using import testing
 bin-dir := compiler-dir .. "/bin"
 include-dir := compiler-dir .. "/include"
 
-include
-    options "-I" include-dir
-""""#include "scopes/scopes.h"
+let lib =
+    include
+        options "-I" include-dir
+        """"#include "scopes/scopes.h"
 
-    int system(const char *command);
+            int system(const char *command);
+
+using lib.extern
 
 #-----------------
 

          
@@ 18,13 21,13 @@ fn get-executable-ptr ()
     bitcast (static-typify this-function) voidstar
 
 fn main (argc argv)
-    sc_main (get-executable-ptr) argc argv
-    return 0
+    sc_init (get-executable-ptr) argc argv
+    return (sc_main)
 
 #-----------------
 
 objfile := bin-dir .. "/newscopes.o"
-compile-object 
+compile-object
     default-target-triple
     compiler-file-kind-object
     objfile

          
M testing/test_loop.sc +8 -0
@@ 136,4 136,12 @@ do
                 break a b
         _ a b c
 
+do
+    # reverse loops
+    for i in (rrange 0 10)
+        test ((i >= 0) & (i < 10))
+    # unsigned values
+    for i in (rrange 0:u32 10:u32)
+        test ((i >= 0:u32) & (i < 10:u32))
+
 true

          
M testing/test_mutarray.sc +8 -0
@@ 219,6 219,14 @@ fn test-remove ()
     test (('value (a @ 2)) == 6)
     test (('value (a @ 3)) == 3)
     test (('value (a @ 4)) == 4)
+    'insert a (One 7) 0
+    test ((countof a) == 6)
+    test (('value (a @ 0)) == 7)
+    test (('value (a @ 1)) == 0)
+    test (('value (a @ 2)) == 1)
+    test (('value (a @ 3)) == 6)
+    test (('value (a @ 4)) == 3)
+    test (('value (a @ 5)) == 4)
     ;
 
 test-remove;

          
M testing/test_option.sc +12 -8
@@ 4,17 4,21 @@ using import Option
 
 # Option unwrap method
 do
-    let optT = (Option i32)
-    let opt = (optT 1234)
+    let optT = (Option One)
+    let opt = (optT (One 1234))
     let opt2 = (optT)
     let result =
-        try ('try-unwrap opt)
-        else 0
-    test (result == 1234)
+        try ('unwrap opt)
+        else
+            error "unwrap failed"
+    test (result == (One 1234))
+    let fallback = (One 12345)
     let result =
-        try ('try-unwrap opt2)
-        else 12345
-    test (result == 12345)
+        try (deref ('unwrap opt2))
+        else (view fallback)
+    test (result == (One 12345))
+    ;
 
+One.test-refcount-balanced;
 
 ;
  No newline at end of file

          
M testing/test_rc.sc +164 -11
@@ 42,6 42,17 @@ do
     test ((Rc.strong-count c) == 1)
     test ((Rc.weak-count c) == 0)
 
+    let nullweak = ((Weak vec3))
+    test ((Rc.strong-count nullweak) == 0)
+    test ((Rc.weak-count nullweak) == 1)
+    let nullweak2 = ((Weak vec3))
+    test ((Rc.weak-count nullweak) == 1)
+    test ((Rc.weak-count nullweak2) == 1)
+    test-error ('upgrade nullweak2)
+    let nullweak3 = ('clone nullweak)
+    test (nullweak == nullweak2)
+    test (nullweak == nullweak3)
+
     let w = (c as Weak)
 
     test ((Rc.strong-count c) == 1)

          
@@ 49,8 60,8 @@ do
     test ((Rc.strong-count w) == 1)
     test ((Rc.weak-count w) == 1)
 
-    let v = ('upgrade w)
-    let p = ('unwrap v)
+    let v = ('force-upgrade w)
+    let p = v
 
     test ((Rc.strong-count c) == 2)
     test ((Rc.weak-count c) == 1)

          
@@ 59,6 70,12 @@ do
     test ((Rc.strong-count p) == 2)
     test ((Rc.weak-count p) == 1)
 
+    let w2 = (Rc.clone w)
+    test ((Rc.strong-count p) == 2)
+    test ((Rc.weak-count p) == 2)
+    test (w == w2)
+    drop w2
+
     print c
     test (c.xz == (vec2 1 3))
     drop c

          
@@ 67,7 84,7 @@ do
     test ((Rc.strong-count w) == 0)
     test ((Rc.weak-count w) == 1)
 
-    test (not ('upgrade w))
+    test-error ('upgrade w)
 
     ;
 

          
@@ 79,19 96,16 @@ do
     struct Node
         parent : (Rc this-type)
 
-# using weak and strong references to build a tree
-    this tests if a complex tree of weak and strong references is cleaned up properly
+# using strong references to build a tree
+    this tests if a complex tree of strong references is cleaned up properly
 do
     using import struct
-    using import Option
     using import Array
 
     global deleted_names : (GrowingArray string)
 
     struct DemoNode
         let RcType = (Rc this-type)
-        let WeakType = RcType.WeakType
-        parent : (Option WeakType)
         children : (GrowingArray RcType)
         _name : string
 

          
@@ 102,7 116,6 @@ do
         case (parent : RcType, name : string, )
             let self =
                 RcType
-                    parent = parent
                     _name = name
             'append parent.children (Rc.clone self)
             self

          
@@ 136,6 149,7 @@ do
 
         let n321 = (DemoNode.new n32 "n321")
         let n322 = (DemoNode.new n32 "n322")
+
         ;
 
     drop root

          
@@ 154,19 168,158 @@ do
     test ((deleted_names @ 10) == "n322")
     test ((deleted_names @ 11) == "n33")
 
+# variation of same test to build a tree
+    this tests if a complex tree of strong and weak references is updated correctly
+do
+    using import struct
+    using import Array
+    using import enum
+
+    struct DemoNode
+        let RcType = (Rc this-type)
+        let WeakType = RcType.WeakType
+        parent : WeakType
+        children : (GrowingArray RcType)
+        _name : string
+
+        inline... new
+        case (name : string,)
+            RcType
+                _name = name
+        case (parent : RcType, name : string, )
+            let self =
+                RcType
+                    parent = parent
+                    _name = name
+            'append parent.children (Rc.clone self)
+            self
+
+        inline __== (self other)
+            if ((typeof self) == (typeof other))
+
+        inline __drop (self)
+            print "deleting" ('name self)
+            super-type.__drop self
+
+        inline name (self)
+            self._name
+
+        inline __repr (self)
+            deref self._name
+
+        fn child-index (self child)
+            for i elem in (enumerate self.children)
+                if (elem == child)
+                    return i
+            else
+                assert false "unexpected error in child-index()"
+                unreachable;
+
+        fn unparent (self)
+            let oldparent =
+                try ('upgrade self.parent)
+                else
+                    return (Rc.clone self)
+            let i = ('child-index oldparent self)
+            self.parent = (WeakType)
+            'remove oldparent.children i
+
+        fn... reparent (self newparent)
+            let self = (unparent self)
+            self.parent = newparent
+            'append newparent.children self
+
+    let root = (DemoNode.new "root")
+    do
+        let n1 = (DemoNode.new root "n1")
+        let n2 = (DemoNode.new root "n2")
+        let n3 = (DemoNode.new root "n3")
+
+        let n11 = (DemoNode.new n1 "n11")
+        let n21 = (DemoNode.new n2 "n21")
+        let n22 = (DemoNode.new n2 "n22")
+        let n31 = (DemoNode.new n3 "n31")
+        let n32 = (DemoNode.new n3 "n32")
+        let n33 = (DemoNode.new n3 "n33")
+
+        let n321 = (DemoNode.new n32 "n321")
+        let n322 = (DemoNode.new n32 "n322")
+        ;
+
+    fn... print-tree (node, indent = "")
+        returning void
+        print
+            indent .. ('name node)
+            Rc.strong-count node
+        test ((Rc.strong-count node) == 1)
+        indent := indent .. "  "
+        for child in node.children
+            this-function child indent
+
+    print-tree root
+    do
+        let node = (root . children @ 1 . children @ 0)
+        DemoNode.reparent node root
+        ;
+    print-tree root
+
+    drop root
+    print "ok"
+
 do
     # singleton test
-    T := (Rc i32)
+    T := (Rc One)
 
     fn singleton ()
         using import Option
         global data : (Option T)
         if (not data)
             data = (T 17)
-        deref ('unwrap data)
+        'force-unwrap data
 
     local example : T = (Rc.clone (singleton))
     test (example == (singleton))
     ;
+test ((One.refcount) == 1)
+One.reset-refcount;
+
+
+do
+    using import struct
+
+    # singleton test
+    typedef Inner :: i32
+        inline __typecall (cls v)
+            bitcast v this-type
+
+    struct T
+        a : Inner
+
+    RcT := (Rc T)
+
+    fn singleton ()
+        using import Option
+        global data : (Option RcT)
+        if (not data)
+            data = (RcT (a = (Inner 17)))
+        'force-unwrap data
+
+    let k = (Rc.clone (singleton))
+    local example : RcT = k
+    # error: value of type %1000:<Rc T> must be unique
+
+do
+    using import struct
+
+    struct T
+        a = 0
+
+    RcT := (Rc T)
+
+    let k = (Rc.clone (RcT 17))
+
+    local container : RcT
+    dump (typeof container) (typeof k)
+    container = k
 
 ;
  No newline at end of file