blueloveTH před 1 rokem
rodič
revize
eae0aa6c8b

+ 10 - 0
docs/1_5_0.md

@@ -194,3 +194,13 @@ Enabling the profiler has a performance overhead. Only enable it when you need i
 
 
 + `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead.
 + `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead.
 + `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead.
 + `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead.
+
+## Python Stringify functions
+
+The following functions now return `Str` object instead of `PyObject*`:
+
++ `vm->py_str`
++ `vm->py_repr`
++ `vm->py_json`
+
+Also, `vm->bind__str__` and `vm->bind__repr__` were changed to return `Str` object.

+ 3 - 3
docs/cheatsheet.md

@@ -237,21 +237,21 @@ Convert a python object to string
 
 
 ```cpp
 ```cpp
 PyObject* obj = py_var(vm, 123);
 PyObject* obj = py_var(vm, 123);
-obj = vm->py_str(obj);  // 123
+Str s = vm->py_str(obj);  // 123
 ```
 ```
 
 
 Get the string representation of a python object
 Get the string representation of a python object
 
 
 ```cpp
 ```cpp
 PyObject* obj = py_var(vm, "123");
 PyObject* obj = py_var(vm, "123");
-obj = vm->py_repr(obj); // "'123'"
+Str s = vm->py_repr(obj); // "'123'"
 ```
 ```
 
 
 Get the JSON representation of a python object
 Get the JSON representation of a python object
 
 
 ```cpp
 ```cpp
 PyObject* obj = py_var(vm, 123);
 PyObject* obj = py_var(vm, 123);
-obj = vm->py_json(obj); // "123"
+Str s = vm->py_json(obj); // "123"
 ```
 ```
 
 
 Get the hash value of a python object
 Get the hash value of a python object

+ 6 - 4
docs/quick-start/misc.md

@@ -14,17 +14,18 @@ auto _lock = vm->heap.gc_scope_lock();
 
 
 The scope lock is required if you create a PyObject and then try to run python-level bytecodes.
 The scope lock is required if you create a PyObject and then try to run python-level bytecodes.
 
 
-For example, you create a temporary object on the stack and then call `vm->py_str`.
+For example, you create a temporary object on the stack and then call `vm->py_next`.
 
 
 ```cpp
 ```cpp
 void some_func(VM* vm){
 void some_func(VM* vm){
     PyObject* obj = VAR(List(5));
     PyObject* obj = VAR(List(5));
     // unsafe
     // unsafe
-    PyObject obj_string = vm->py_str(obj);
+    PyObject iter = vm->py_iter(obj);
+    PyObject next = vm->py_next(iter);
 }
 }
 ```
 ```
 
 
-Because users can have an overload of `__str__`, this call is unsafe.
+Because users can have an overload of `__next__`, this call is unsafe.
 When the vm is running python-level bytecodes, gc may start and delete your temporary object.
 When the vm is running python-level bytecodes, gc may start and delete your temporary object.
 
 
 The scope lock prevents this from happening.
 The scope lock prevents this from happening.
@@ -34,6 +35,7 @@ void some_func(VM* vm){
     PyObject* obj = VAR(List(5));
     PyObject* obj = VAR(List(5));
     // safe
     // safe
     auto _lock = vm->heap.gc_scope_lock();
     auto _lock = vm->heap.gc_scope_lock();
-    PyObject obj_string = vm->py_str(obj);
+    PyObject iter = vm->py_iter(obj);
+    PyObject next = vm->py_next(iter);
 }
 }
 ```
 ```

+ 10 - 9
include/pocketpy/vm.h

@@ -50,10 +50,9 @@ struct PyTypeInfo{
 
 
     std::vector<StrName> annotated_fields = {};
     std::vector<StrName> annotated_fields = {};
 
 
-    // cached special methods
     // unary operators
     // unary operators
-    PyObject* (*m__repr__)(VM* vm, PyObject*) = nullptr;
-    PyObject* (*m__str__)(VM* vm, PyObject*) = nullptr;
+    Str (*m__repr__)(VM* vm, PyObject*) = nullptr;
+    Str (*m__str__)(VM* vm, PyObject*) = nullptr;
     i64 (*m__hash__)(VM* vm, PyObject*) = nullptr;
     i64 (*m__hash__)(VM* vm, PyObject*) = nullptr;
     i64 (*m__len__)(VM* vm, PyObject*) = nullptr;
     i64 (*m__len__)(VM* vm, PyObject*) = nullptr;
     PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr;
     PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr;
@@ -183,9 +182,10 @@ public:
     VM(bool enable_os=true);
     VM(bool enable_os=true);
 
 
 #if PK_REGION("Python Equivalents")
 #if PK_REGION("Python Equivalents")
-    PyObject* py_str(PyObject* obj);                        // x -> str(x)
-    PyObject* py_repr(PyObject* obj);                       // x -> repr(x)
-    PyObject* py_json(PyObject* obj);                       // x -> json.dumps(x)
+    Str py_str(PyObject* obj);                              // x -> str(x)
+    Str py_repr(PyObject* obj);                             // x -> repr(x)
+    Str py_json(PyObject* obj);                             // x -> json.dumps(x)
+
     PyObject* py_iter(PyObject* obj);                       // x -> iter(x)
     PyObject* py_iter(PyObject* obj);                       // x -> iter(x)
     PyObject* py_next(PyObject*);                           // x -> next(x)
     PyObject* py_next(PyObject*);                           // x -> next(x)
     PyObject* _py_next(const PyTypeInfo*, PyObject*);       // x -> next(x) with type info cache
     PyObject* _py_next(const PyTypeInfo*, PyObject*);       // x -> next(x) with type info cache
@@ -269,9 +269,10 @@ public:
 #endif
 #endif
 
 
 #if PK_REGION("Magic Bindings")
 #if PK_REGION("Magic Bindings")
-    void bind__repr__(Type type, PyObject* (*f)(VM*, PyObject*));
-    void bind__str__(Type type, PyObject* (*f)(VM*, PyObject*));
+    void bind__repr__(Type type, Str (*f)(VM*, PyObject*));
+    void bind__str__(Type type, Str (*f)(VM*, PyObject*));
     void bind__iter__(Type type, PyObject* (*f)(VM*, PyObject*));
     void bind__iter__(Type type, PyObject* (*f)(VM*, PyObject*));
+
     void bind__next__(Type type, unsigned (*f)(VM*, PyObject*));
     void bind__next__(Type type, unsigned (*f)(VM*, PyObject*));
     [[deprecated]] void bind__next__(Type type, PyObject* (*f)(VM*, PyObject*));
     [[deprecated]] void bind__next__(Type type, PyObject* (*f)(VM*, PyObject*));
     void bind__neg__(Type type, PyObject* (*f)(VM*, PyObject*));
     void bind__neg__(Type type, PyObject* (*f)(VM*, PyObject*));
@@ -356,7 +357,7 @@ public:
     bool issubclass(Type cls, Type base);
     bool issubclass(Type cls, Type base);
     void check_type(PyObject* obj, Type type){ if(!is_type(obj, type)) TypeError(type, _tp(obj)); }
     void check_type(PyObject* obj, Type type){ if(!is_type(obj, type)) TypeError(type, _tp(obj)); }
     void check_compatible_type(PyObject* obj, Type type){ if(!isinstance(obj, type)) TypeError(type, _tp(obj)); }
     void check_compatible_type(PyObject* obj, Type type){ if(!isinstance(obj, type)) TypeError(type, _tp(obj)); }
-    
+
     Type _tp(PyObject* obj){ return is_small_int(obj) ? tp_int : obj->type; }
     Type _tp(PyObject* obj){ return is_small_int(obj) ? tp_int : obj->type; }
     const PyTypeInfo* _tp_info(PyObject* obj) { return &_all_types[_tp(obj)]; }
     const PyTypeInfo* _tp_info(PyObject* obj) { return &_all_types[_tp(obj)]; }
     const PyTypeInfo* _tp_info(Type type) { return &_all_types[type]; }
     const PyTypeInfo* _tp_info(Type type) { return &_all_types[type]; }

+ 2 - 2
src/array2d.cpp

@@ -181,9 +181,9 @@ struct Array2d{
             return (i64)self.numel;
             return (i64)self.numel;
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0) -> Str{
             Array2d& self = PK_OBJ_GET(Array2d, _0);
             Array2d& self = PK_OBJ_GET(Array2d, _0);
-            return VAR(_S("array2d(", self.n_cols, ", ", self.n_rows, ')'));
+            return _S("array2d(", self.n_cols, ", ", self.n_rows, ')');
         });
         });
 
 
         vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){
         vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){

+ 6 - 5
src/ceval.cpp

@@ -134,7 +134,7 @@ __NEXT_STEP:;
         THIRD() = _0;
         THIRD() = _0;
     } DISPATCH()
     } DISPATCH()
     case OP_PRINT_EXPR:{
     case OP_PRINT_EXPR:{
-        if(TOP() != None) stdout_write(CAST(Str&, py_repr(TOP())) + "\n");
+        if(TOP() != None) stdout_write(py_repr(TOP()) + "\n");
         POP();
         POP();
     } DISPATCH()
     } DISPATCH()
     /*****************************************/
     /*****************************************/
@@ -383,7 +383,7 @@ __NEXT_STEP:;
     case OP_BUILD_STRING: {
     case OP_BUILD_STRING: {
         SStream ss;
         SStream ss;
         ArgsView view = STACK_VIEW(byte.arg);
         ArgsView view = STACK_VIEW(byte.arg);
-        for(PyObject* obj : view) ss << CAST(Str&, py_str(obj));
+        for(PyObject* obj : view) ss << py_str(obj);
         STACK_SHRINK(byte.arg);
         STACK_SHRINK(byte.arg);
         PUSH(VAR(ss.str()));
         PUSH(VAR(ss.str()));
     } DISPATCH()
     } DISPATCH()
@@ -657,7 +657,7 @@ __NEXT_STEP:;
         PUSH(_0);
         PUSH(_0);
     } DISPATCH()
     } DISPATCH()
     case OP_REPR:
     case OP_REPR:
-        TOP() = py_repr(TOP());
+        TOP() = VAR(py_repr(TOP()));
         DISPATCH()
         DISPATCH()
     case OP_CALL:{
     case OP_CALL:{
         if(heap._should_auto_collect()) heap._auto_collect();
         if(heap._should_auto_collect()) heap._auto_collect();
@@ -933,8 +933,9 @@ __NEXT_STEP:;
     } DISPATCH()
     } DISPATCH()
     case OP_RAISE_ASSERT:
     case OP_RAISE_ASSERT:
         if(byte.arg){
         if(byte.arg){
-            PyObject* _0 = py_str(POPX());
-            AssertionError(CAST(Str, _0));
+            Str msg = py_str(TOP());
+            POP();
+            AssertionError(msg);
         }else{
         }else{
             AssertionError();
             AssertionError();
         }
         }

+ 5 - 5
src/cffi.cpp

@@ -14,9 +14,9 @@ namespace pkpy{
             return reinterpret_cast<i64>(self.ptr);
             return reinterpret_cast<i64>(self.ptr);
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
             VoidP& self = PK_OBJ_GET(VoidP, obj);
             VoidP& self = PK_OBJ_GET(VoidP, obj);
-            return VAR(_S("<void* at ", self.hex(), ">"));
+            return _S("<void* at ", self.hex(), ">");
         });
         });
 
 
 #define BIND_CMP(name, op)  \
 #define BIND_CMP(name, op)  \
@@ -76,7 +76,7 @@ namespace pkpy{
             Struct& self = _CAST(Struct&, obj);
             Struct& self = _CAST(Struct&, obj);
             SStream ss;
             SStream ss;
             ss << "<struct object of " << self.size << " bytes>";
             ss << "<struct object of " << self.size << " bytes>";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){
         vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){
@@ -234,9 +234,9 @@ void add_module_c(VM* vm){
         T* target = (T*)voidp.ptr;                                      \
         T* target = (T*)voidp.ptr;                                      \
         return vm->heap.gcnew<VoidP>(lhs->type, target - offset);       \
         return vm->heap.gcnew<VoidP>(lhs->type, target - offset);       \
     });                                                                 \
     });                                                                 \
-    vm->bind__repr__(type_t, [](VM* vm, PyObject* obj){                 \
+    vm->bind__repr__(type_t, [](VM* vm, PyObject* obj) -> Str{          \
         VoidP& self = _CAST(VoidP&, obj);                               \
         VoidP& self = _CAST(VoidP&, obj);                               \
-        return VAR(_S("<", CNAME, "* at ", self.hex(), ">"));         \
+        return _S("<", CNAME, "* at ", self.hex(), ">");                \
     });                                                                 \
     });                                                                 \
 
 
     BIND_PRIMITIVE(char, "char")
     BIND_PRIMITIVE(char, "char")

+ 6 - 6
src/collections.cpp

@@ -105,21 +105,21 @@ namespace pkpy
             return vm->new_user_object<PyDequeIter>(_0, self.dequeItems.begin(), self.dequeItems.end());
             return vm->new_user_object<PyDequeIter>(_0, self.dequeItems.begin(), self.dequeItems.end());
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0) -> Str
         {
         {
-            if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
+            if(vm->_repr_recursion_set.count(_0)) return "[...]";
             const PyDeque &self = _CAST(PyDeque&, _0);
             const PyDeque &self = _CAST(PyDeque&, _0);
             SStream ss;
             SStream ss;
             ss << "deque([";
             ss << "deque([";
             vm->_repr_recursion_set.insert(_0);
             vm->_repr_recursion_set.insert(_0);
             for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
             for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
             {
             {
-                ss << CAST(Str&, vm->py_repr(*it));
+                ss << vm->py_repr(*it);
                 if (it != self.dequeItems.end() - 1) ss << ", ";
                 if (it != self.dequeItems.end() - 1) ss << ", ";
             }
             }
             vm->_repr_recursion_set.erase(_0);
             vm->_repr_recursion_set.erase(_0);
             self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
             self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         // enables comparison between two deques, == and != are supported
         // enables comparison between two deques, == and != are supported
@@ -253,7 +253,7 @@ namespace pkpy
                      int start = CAST_DEFAULT(int, args[2], 0);
                      int start = CAST_DEFAULT(int, args[2], 0);
                      int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size());
                      int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size());
                      int index = self.findIndex(vm, obj, start, stop);
                      int index = self.findIndex(vm, obj, start, stop);
-                     if (index < 0) vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
+                     if (index < 0) vm->ValueError(vm->py_repr(obj) + " is not in deque");
                      return VAR(index);
                      return VAR(index);
                  });
                  });
         // NEW: returns the index of the given object in the deque
         // NEW: returns the index of the given object in the deque
@@ -290,7 +290,7 @@ namespace pkpy
                      PyObject *obj = args[1];
                      PyObject *obj = args[1];
                      PyObject *removed = self.popObj(false, false, obj, vm);
                      PyObject *removed = self.popObj(false, false, obj, vm);
                      if (removed == nullptr)
                      if (removed == nullptr)
-                         vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
+                         vm->ValueError(vm->py_repr(obj) + " is not in list");
                      return vm->None;
                      return vm->None;
                  });
                  });
         // NEW: reverses the deque
         // NEW: reverses the deque

+ 3 - 4
src/dataclasses.cpp

@@ -45,8 +45,7 @@ static void patch__init__(VM* vm, Type cls){
 }
 }
 
 
 static void patch__repr__(VM* vm, Type cls){
 static void patch__repr__(VM* vm, Type cls){
-    vm->bind__repr__(cls, [](VM* vm, PyObject* _0){
-        auto _lock = vm->heap.gc_scope_lock();
+    vm->bind__repr__(cls, [](VM* vm, PyObject* _0) -> Str{
         const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
         const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
         const auto& fields = cls_info->annotated_fields;
         const auto& fields = cls_info->annotated_fields;
         const NameDict& obj_d = _0->attr();
         const NameDict& obj_d = _0->attr();
@@ -56,10 +55,10 @@ static void patch__repr__(VM* vm, Type cls){
         for(StrName field: fields){
         for(StrName field: fields){
             if(first) first = false;
             if(first) first = false;
             else ss << ", ";
             else ss << ", ";
-            ss << field << "=" << CAST(Str&, vm->py_repr(obj_d[field]));
+            ss << field << "=" << vm->py_repr(obj_d[field]);
         }
         }
         ss << ")";
         ss << ")";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 }
 }
 
 

+ 8 - 8
src/linalg.cpp

@@ -149,12 +149,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
             return VAR(val);
             return VAR(val);
         }, {}, BindType::STATICMETHOD);
         }, {}, BindType::STATICMETHOD);
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
             Vec2 self = _CAST(Vec2&, obj);
             Vec2 self = _CAST(Vec2&, obj);
             SStream ss;
             SStream ss;
             ss.setprecision(3);
             ss.setprecision(3);
             ss << "vec2(" << self.x << ", " << self.y << ")";
             ss << "vec2(" << self.x << ", " << self.y << ")";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args){
         vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args){
@@ -200,12 +200,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
             return vm->heap.gcnew<Vec3>(PK_OBJ_GET(Type, args[0]), x, y, z);
             return vm->heap.gcnew<Vec3>(PK_OBJ_GET(Type, args[0]), x, y, z);
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
             Vec3 self = _CAST(Vec3&, obj);
             Vec3 self = _CAST(Vec3&, obj);
             SStream ss;
             SStream ss;
             ss.setprecision(3);
             ss.setprecision(3);
             ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")";
             ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         PY_FIELD(Vec3, "x", x)
         PY_FIELD(Vec3, "x", x)
@@ -239,12 +239,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
             return vm->heap.gcnew<Vec4>(PK_OBJ_GET(Type, args[0]), x, y, z, w);
             return vm->heap.gcnew<Vec4>(PK_OBJ_GET(Type, args[0]), x, y, z, w);
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
             Vec4 self = _CAST(Vec4&, obj);
             Vec4 self = _CAST(Vec4&, obj);
             SStream ss;
             SStream ss;
             ss.setprecision(3);
             ss.setprecision(3);
             ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")";
             ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         PY_FIELD(Vec4, "x", x)
         PY_FIELD(Vec4, "x", x)
@@ -298,14 +298,14 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
             return vm->None;
             return vm->None;
         });
         });
 
 
-        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
             const Mat3x3& self = _CAST(Mat3x3&, obj);
             const Mat3x3& self = _CAST(Mat3x3&, obj);
             SStream ss;
             SStream ss;
             ss.setprecision(3);
             ss.setprecision(3);
             ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n";
             ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n";
             ss << "        " << self._21 << ", " << self._22 << ", " << self._23 << ",\n";
             ss << "        " << self._21 << ", " << self._22 << ", " << self._23 << ",\n";
             ss << "        " << self._31 << ", " << self._32 << ", " << self._33 << "])";
             ss << "        " << self._31 << ", " << self._32 << ", " << self._33 << "])";
-            return VAR(ss.str());
+            return ss.str();
         });
         });
 
 
         vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* index){
         vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* index){

+ 1 - 1
src/modules.cpp

@@ -103,7 +103,7 @@ void add_module_json(VM* vm){
     });
     });
 
 
     vm->bind_func(mod, "dumps", 1, [](VM* vm, ArgsView args) {
     vm->bind_func(mod, "dumps", 1, [](VM* vm, ArgsView args) {
-        return vm->py_json(args[0]);
+        return VAR(vm->py_json(args[0]));
     });
     });
 }
 }
 
 

+ 55 - 52
src/pocketpy.cpp

@@ -245,7 +245,7 @@ void __init_builtins(VM* _vm) {
     });
     });
 
 
     _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args){
     _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args){
-        return vm->py_repr(args[0]);
+        return VAR(vm->py_repr(args[0]));
     });
     });
 
 
     _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args){
     _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args){
@@ -343,13 +343,13 @@ void __init_builtins(VM* _vm) {
     });
     });
 
 
     // tp_object
     // tp_object
-    _vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) {
+    _vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) -> Str{
         if(is_tagged(obj)) PK_FATAL_ERROR();
         if(is_tagged(obj)) PK_FATAL_ERROR();
         SStream ss;
         SStream ss;
         ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at ";
         ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at ";
         ss.write_hex(obj);
         ss.write_hex(obj);
         ss << ">";
         ss << ">";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     _vm->bind__eq__(VM::tp_object, [](VM* vm, PyObject* _0, PyObject* _1) {
     _vm->bind__eq__(VM::tp_object, [](VM* vm, PyObject* _0, PyObject* _1) {
@@ -382,8 +382,8 @@ void __init_builtins(VM* _vm) {
     _vm->bind__iter__(VM::tp_range, [](VM* vm, PyObject* obj) { return vm->new_user_object<RangeIter>(PK_OBJ_GET(Range, obj)); });
     _vm->bind__iter__(VM::tp_range, [](VM* vm, PyObject* obj) { return vm->new_user_object<RangeIter>(PK_OBJ_GET(Range, obj)); });
     
     
     // tp_nonetype
     // tp_nonetype
-    _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) {
-        return VAR("None"); 
+    _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) -> Str {
+        return "None"; 
     });
     });
 
 
     // tp_float / tp_float
     // tp_float / tp_float
@@ -475,7 +475,9 @@ void __init_builtins(VM* _vm) {
         return VAR(bits);
         return VAR(bits);
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); });
+    _vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) -> Str{
+        return std::to_string(_CAST(i64, obj));
+    });
     _vm->bind__neg__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(-_CAST(i64, obj)); });
     _vm->bind__neg__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(-_CAST(i64, obj)); });
     _vm->bind__hash__(VM::tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); });
     _vm->bind__hash__(VM::tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); });
     _vm->bind__invert__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(~_CAST(i64, obj)); });
     _vm->bind__invert__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(~_CAST(i64, obj)); });
@@ -532,18 +534,18 @@ void __init_builtins(VM* _vm) {
 
 
     _vm->bind__neg__(VM::tp_float, [](VM* vm, PyObject* _0) { return VAR(-_CAST(f64, _0)); });
     _vm->bind__neg__(VM::tp_float, [](VM* vm, PyObject* _0) { return VAR(-_CAST(f64, _0)); });
 
 
-    _vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) -> Str {
         f64 val = _CAST(f64, _0);
         f64 val = _CAST(f64, _0);
         SStream ss;
         SStream ss;
         ss << val;
         ss << val;
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     // tp_str
     // tp_str
     _vm->bind_func(VM::tp_str, __new__, -1, [](VM* vm, ArgsView args) {
     _vm->bind_func(VM::tp_str, __new__, -1, [](VM* vm, ArgsView args) {
         if(args.size() == 1) return VAR(Str());
         if(args.size() == 1) return VAR(Str());
         if(args.size() > 2) vm->TypeError("str() takes at most 1 argument");
         if(args.size() > 2) vm->TypeError("str() takes at most 1 argument");
-        return vm->py_str(args[1]);
+        return VAR(vm->py_str(args[1]));
     });
     });
 
 
     _vm->bind__hash__(VM::tp_str, [](VM* vm, PyObject* _0) {
     _vm->bind__hash__(VM::tp_str, [](VM* vm, PyObject* _0) {
@@ -574,11 +576,12 @@ void __init_builtins(VM* _vm) {
         const Str& self = _CAST(Str&, _0);
         const Str& self = _CAST(Str&, _0);
         return VAR(self.index(CAST(Str&, _1)) != -1);
         return VAR(self.index(CAST(Str&, _1)) != -1);
     });
     });
-    _vm->bind__str__(VM::tp_str, [](VM* vm, PyObject* _0) { return _0; });
+
+    _vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) { return args[0]; });
     _vm->bind__iter__(VM::tp_str, [](VM* vm, PyObject* _0) { return vm->new_user_object<StringIter>(_0); });
     _vm->bind__iter__(VM::tp_str, [](VM* vm, PyObject* _0) { return vm->new_user_object<StringIter>(_0); });
-    _vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) -> Str {
         const Str& self = _CAST(Str&, _0);
         const Str& self = _CAST(Str&, _0);
-        return VAR(self.escape());
+        return self.escape();
     });
     });
 
 
 #define BIND_CMP_STR(name, op) \
 #define BIND_CMP_STR(name, op) \
@@ -799,36 +802,36 @@ void __init_builtins(VM* _vm) {
         return vm->None;
         return vm->None;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0){
-        if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
+    _vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0) -> Str{
+        if(vm->_repr_recursion_set.count(_0)) return "[...]";
         List& iterable = _CAST(List&, _0);
         List& iterable = _CAST(List&, _0);
         SStream ss;
         SStream ss;
         ss << '[';
         ss << '[';
         vm->_repr_recursion_set.insert(_0);
         vm->_repr_recursion_set.insert(_0);
         for(int i=0; i<iterable.size(); i++){
         for(int i=0; i<iterable.size(); i++){
-            ss << CAST(Str&, vm->py_repr(iterable[i]));
+            ss << vm->py_repr(iterable[i]);
             if(i != iterable.size()-1) ss << ", ";
             if(i != iterable.size()-1) ss << ", ";
         }
         }
         vm->_repr_recursion_set.erase(_0);
         vm->_repr_recursion_set.erase(_0);
         ss << ']';
         ss << ']';
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0){
+    _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0) -> Str{
         Tuple& iterable = _CAST(Tuple&, _0);
         Tuple& iterable = _CAST(Tuple&, _0);
         SStream ss;
         SStream ss;
         ss << '(';
         ss << '(';
         if(iterable.size() == 1){
         if(iterable.size() == 1){
-            ss << CAST(Str&, vm->py_repr(iterable[0]));
+            ss << vm->py_repr(iterable[0]);
             ss << ',';
             ss << ',';
         }else{
         }else{
             for(int i=0; i<iterable.size(); i++){
             for(int i=0; i<iterable.size(); i++){
-                ss << CAST(Str&, vm->py_repr(iterable[i]));
+                ss << vm->py_repr(iterable[i]);
                 if(i != iterable.size()-1) ss << ", ";
                 if(i != iterable.size()-1) ss << ", ";
             }
             }
         }
         }
         ss << ')';
         ss << ')';
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     _vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) {
     _vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) {
@@ -869,7 +872,7 @@ void __init_builtins(VM* _vm) {
         for(int i=start; i<self.size(); i++){
         for(int i=start; i<self.size(); i++){
             if(vm->py_eq(self[i], obj)) return VAR(i);
             if(vm->py_eq(self[i], obj)) return VAR(i);
         }
         }
-        vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list");
+        vm->ValueError(vm->py_repr(obj) + " is not in list");
         return vm->None;
         return vm->None;
     });
     });
 
 
@@ -882,7 +885,7 @@ void __init_builtins(VM* _vm) {
                 return vm->None;
                 return vm->None;
             }
             }
         }
         }
-        vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list");
+        vm->ValueError(vm->py_repr(obj) + " is not in list");
         return vm->None;
         return vm->None;
     });
     });
 
 
@@ -1075,9 +1078,9 @@ void __init_builtins(VM* _vm) {
     _vm->bind__hash__(VM::tp_bool, [](VM* vm, PyObject* _0) {
     _vm->bind__hash__(VM::tp_bool, [](VM* vm, PyObject* _0) {
         return (i64)_CAST(bool, _0);
         return (i64)_CAST(bool, _0);
     });
     });
-    _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) -> Str{
         bool val = _CAST(bool, _0);
         bool val = _CAST(bool, _0);
-        return VAR(val ? "True" : "False");
+        return val ? "True" : "False";
     });
     });
 
 
     _vm->bind__and__(VM::tp_bool, [](VM* vm, PyObject* _0, PyObject* _1) {
     _vm->bind__and__(VM::tp_bool, [](VM* vm, PyObject* _0, PyObject* _1) {
@@ -1096,11 +1099,11 @@ void __init_builtins(VM* _vm) {
     });
     });
 
 
     // tp_ellipsis / tp_NotImplementedType
     // tp_ellipsis / tp_NotImplementedType
-    _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) {
-        return VAR("...");
+    _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) -> Str{
+        return "...";
     });
     });
-    _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) {
-        return VAR("NotImplemented");
+    _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) -> Str{
+        return "NotImplemented";
     });
     });
 
 
     // tp_bytes
     // tp_bytes
@@ -1148,7 +1151,7 @@ void __init_builtins(VM* _vm) {
         return (i64)std::hash<std::string_view>()(view);
         return (i64)std::hash<std::string_view>()(view);
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) -> Str {
         const Bytes& self = _CAST(Bytes&, _0);
         const Bytes& self = _CAST(Bytes&, _0);
         SStream ss;
         SStream ss;
         ss << "b'";
         ss << "b'";
@@ -1157,7 +1160,7 @@ void __init_builtins(VM* _vm) {
             ss.write_hex((unsigned char)self[i]);
             ss.write_hex((unsigned char)self[i]);
         }
         }
         ss << "'";
         ss << "'";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
     _vm->bind__len__(VM::tp_bytes, [](VM* vm, PyObject* _0) {
     _vm->bind__len__(VM::tp_bytes, [](VM* vm, PyObject* _0) {
         return (i64)_CAST(Bytes&, _0).size();
         return (i64)_CAST(Bytes&, _0).size();
@@ -1189,14 +1192,14 @@ void __init_builtins(VM* _vm) {
         return vm->True;
         return vm->True;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) -> Str {
         const Slice& self = _CAST(Slice&, _0);
         const Slice& self = _CAST(Slice&, _0);
         SStream ss;
         SStream ss;
         ss << "slice(";
         ss << "slice(";
-        ss << CAST(Str, vm->py_repr(self.start)) << ", ";
-        ss << CAST(Str, vm->py_repr(self.stop)) << ", ";
-        ss << CAST(Str, vm->py_repr(self.step)) << ")";
-        return VAR(ss.str());
+        ss << vm->py_repr(self.start) << ", ";
+        ss << vm->py_repr(self.stop) << ", ";
+        ss << vm->py_repr(self.step) << ")";
+        return ss.str();
     });
     });
 
 
     // tp_mappingproxy
     // tp_mappingproxy
@@ -1251,8 +1254,8 @@ void __init_builtins(VM* _vm) {
         return ret;
         return ret;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) {
-        if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
+    _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) -> Str{
+        if(vm->_repr_recursion_set.count(_0)) return "{...}";
         MappingProxy& self = _CAST(MappingProxy&, _0);
         MappingProxy& self = _CAST(MappingProxy&, _0);
         SStream ss;
         SStream ss;
         ss << "mappingproxy({";
         ss << "mappingproxy({";
@@ -1262,11 +1265,11 @@ void __init_builtins(VM* _vm) {
             if(!first) ss << ", ";
             if(!first) ss << ", ";
             first = false;
             first = false;
             ss << k.escape() << ": ";
             ss << k.escape() << ": ";
-            ss << CAST(Str, vm->py_repr(v));
+            ss << vm->py_repr(v);
         }
         }
         vm->_repr_recursion_set.erase(_0);
         vm->_repr_recursion_set.erase(_0);
         ss << "})";
         ss << "})";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     _vm->bind__contains__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0, PyObject* _1) {
     _vm->bind__contains__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0, PyObject* _1) {
@@ -1412,8 +1415,8 @@ void __init_builtins(VM* _vm) {
         return vm->None;
         return vm->None;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) {
-        if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
+    _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) -> Str{
+        if(vm->_repr_recursion_set.count(_0)) return "{...}";
         Dict& self = _CAST(Dict&, _0);
         Dict& self = _CAST(Dict&, _0);
         SStream ss;
         SStream ss;
         ss << "{";
         ss << "{";
@@ -1422,11 +1425,11 @@ void __init_builtins(VM* _vm) {
         self.apply([&](PyObject* k, PyObject* v){
         self.apply([&](PyObject* k, PyObject* v){
             if(!first) ss << ", ";
             if(!first) ss << ", ";
             first = false;
             first = false;
-            ss << CAST(Str&, vm->py_repr(k)) << ": " << CAST(Str&, vm->py_repr(v));
+            ss << vm->py_repr(k) << ": " << vm->py_repr(v);
         });
         });
         vm->_repr_recursion_set.erase(_0);
         vm->_repr_recursion_set.erase(_0);
         ss << "}";
         ss << "}";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     _vm->bind__eq__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) {
     _vm->bind__eq__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) {
@@ -1444,9 +1447,9 @@ void __init_builtins(VM* _vm) {
         return vm->True;
         return vm->True;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) -> Str {
         const Str& path = CAST(Str&, _0->attr(__path__));
         const Str& path = CAST(Str&, _0->attr(__path__));
-        return VAR(_S("<module ", path.escape(), ">"));
+        return _S("<module ", path.escape(), ">");
     });
     });
 
 
     // tp_property
     // tp_property
@@ -1493,14 +1496,14 @@ void __init_builtins(VM* _vm) {
         return vm->None;
         return vm->None;
     });
     });
 
 
-    _vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) {
+    _vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str {
         Exception& self = _CAST(Exception&, _0);
         Exception& self = _CAST(Exception&, _0);
-        return VAR(_S(_type_name(vm, _0->type), '(', self.msg.escape(), ')'));
+        return _S(_type_name(vm, _0->type), '(', self.msg.escape(), ')');
     });
     });
 
 
-    _vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) {
+    _vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str{
         Exception& self = _CAST(Exception&, _0);
         Exception& self = _CAST(Exception&, _0);
-        return VAR(self.msg);
+        return self.msg;
     });
     });
 
 
     _vm->register_user_class<RangeIter>(_vm->builtins, "_range_iter");
     _vm->register_user_class<RangeIter>(_vm->builtins, "_range_iter");
@@ -1533,11 +1536,11 @@ void VM::__post_init_builtin_types(){
         return self;        // for generics
         return self;        // for generics
     });
     });
 
 
-    bind__repr__(tp_type, [](VM* vm, PyObject* self){
+    bind__repr__(tp_type, [](VM* vm, PyObject* self) -> Str{
         SStream ss;
         SStream ss;
         const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)];
         const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)];
         ss << "<class '" << info.name << "'>";
         ss << "<class '" << info.name << "'>";
-        return VAR(ss.str());
+        return ss.str();
     });
     });
 
 
     bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0])));
     bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0])));
@@ -1589,7 +1592,7 @@ void VM::__post_init_builtin_types(){
         const Str& _2 = CAST(Str&, args[2]);
         const Str& _2 = CAST(Str&, args[2]);
         SStream ss;
         SStream ss;
         for(int i=0; i<_0.size(); i++){
         for(int i=0; i<_0.size(); i++){
-            ss << CAST(Str&, vm->py_str(_0[i]));
+            ss << vm->py_str(_0[i]);
             if(i != _0.size()-1) ss << _1;
             if(i != _0.size()-1) ss << _1;
         }
         }
         ss << _2;
         ss << _2;

+ 2 - 2
src/pocketpy_c.cpp

@@ -473,7 +473,7 @@ bool pkpy_py_repr(pkpy_vm* vm_handle) {
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyObject* item = vm->s_data.top();
     PyObject* item = vm->s_data.top();
     PK_PROTECTED(
     PK_PROTECTED(
-        item = vm->py_repr(item);
+        item = VAR(vm->py_repr(item));
     )
     )
     vm->s_data.top() = item;
     vm->s_data.top() = item;
     return true;
     return true;
@@ -485,7 +485,7 @@ bool pkpy_py_str(pkpy_vm* vm_handle) {
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PK_ASSERT_N_EXTRA_ELEMENTS(1)
     PyObject* item = vm->s_data.top();
     PyObject* item = vm->s_data.top();
     PK_PROTECTED(
     PK_PROTECTED(
-        item = vm->py_str(item);
+        item = VAR(vm->py_str(item));
     )
     )
     vm->s_data.top() = item;
     vm->s_data.top() = item;
     return true;
     return true;

+ 37 - 16
src/vm.cpp

@@ -83,24 +83,34 @@ namespace pkpy{
         __init_builtin_types();
         __init_builtin_types();
     }
     }
 
 
-    PyObject* VM::py_str(PyObject* obj){
+    Str VM::py_str(PyObject* obj){
         const PyTypeInfo* ti = _tp_info(obj);
         const PyTypeInfo* ti = _tp_info(obj);
         if(ti->m__str__) return ti->m__str__(this, obj);
         if(ti->m__str__) return ti->m__str__(this, obj);
         PyObject* self;
         PyObject* self;
         PyObject* f = get_unbound_method(obj, __str__, &self, false);
         PyObject* f = get_unbound_method(obj, __str__, &self, false);
-        if(self != PY_NULL) return call_method(self, f);
+        if(self != PY_NULL){
+            PyObject* retval = call_method(self, f);
+            if(!is_type(retval, tp_str)){
+                throw std::runtime_error("object.__str__ must return str");
+            }
+            return PK_OBJ_GET(Str, retval);
+        }
         return py_repr(obj);
         return py_repr(obj);
     }
     }
 
 
-    PyObject* VM::py_repr(PyObject* obj){
+    Str VM::py_repr(PyObject* obj){
         const PyTypeInfo* ti = _tp_info(obj);
         const PyTypeInfo* ti = _tp_info(obj);
         if(ti->m__repr__) return ti->m__repr__(this, obj);
         if(ti->m__repr__) return ti->m__repr__(this, obj);
-        return call_method(obj, __repr__);
+        PyObject* retval = call_method(obj, __repr__);
+        if(!is_type(retval, tp_str)){
+            throw std::runtime_error("object.__repr__ must return str");
+        }
+        return PK_OBJ_GET(Str, retval);
     }
     }
 
 
-    PyObject* VM::py_json(PyObject* obj){
+    Str VM::py_json(PyObject* obj){
         auto j = JsonSerializer(this, obj);
         auto j = JsonSerializer(this, obj);
-        return VAR(j.serialize());
+        return j.serialize();
     }
     }
 
 
     PyObject* VM::py_iter(PyObject* obj){
     PyObject* VM::py_iter(PyObject* obj){
@@ -495,7 +505,7 @@ i64 VM::py_hash(PyObject* obj){
 }
 }
 
 
 PyObject* VM::__format_object(PyObject* obj, Str spec){
 PyObject* VM::__format_object(PyObject* obj, Str spec){
-    if(spec.empty()) return py_str(obj);
+    if(spec.empty()) return VAR(py_str(obj));
     char type;
     char type;
     switch(spec.end()[-1]){
     switch(spec.end()[-1]){
         case 'f': case 'd': case 's':
         case 'f': case 'd': case 's':
@@ -560,7 +570,7 @@ PyObject* VM::__format_object(PyObject* obj, Str spec){
     }else if(type == 's'){
     }else if(type == 's'){
         ret = CAST(Str&, obj);
         ret = CAST(Str&, obj);
     }else{
     }else{
-        ret = CAST(Str&, py_str(obj));
+        ret = py_str(obj);
     }
     }
     if(width != -1 && width > ret.length()){
     if(width != -1 && width > ret.length()){
         int pad = width - ret.length();
         int pad = width - ret.length();
@@ -602,7 +612,7 @@ static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){
     switch(byte.op){
     switch(byte.op){
         case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH:
         case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH:
             if(vm != nullptr){
             if(vm != nullptr){
-                argStr += _S(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")").sv();
+                argStr += _S(" (", vm->py_repr(co->consts[byte.arg]), ")").sv();
             }
             }
             break;
             break;
         case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL:
         case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL:
@@ -1398,14 +1408,27 @@ void VM::bind__next__(Type type, PyObject* (*f)(VM*, PyObject*)){
             return lambda_get_userdata<PyObject*(*)(VM*, PyObject*)>(args.begin())(vm, args[0]);    \
             return lambda_get_userdata<PyObject*(*)(VM*, PyObject*)>(args.begin())(vm, args[0]);    \
         }, f);                                                                          \
         }, f);                                                                          \
     }
     }
-
-    BIND_UNARY_SPECIAL(__repr__)
-    BIND_UNARY_SPECIAL(__str__)
     BIND_UNARY_SPECIAL(__iter__)
     BIND_UNARY_SPECIAL(__iter__)
     BIND_UNARY_SPECIAL(__neg__)
     BIND_UNARY_SPECIAL(__neg__)
     BIND_UNARY_SPECIAL(__invert__)
     BIND_UNARY_SPECIAL(__invert__)
 #undef BIND_UNARY_SPECIAL
 #undef BIND_UNARY_SPECIAL
 
 
+void VM::bind__str__(Type type, Str (*f)(VM*, PyObject*)){
+    _all_types[type].m__str__ = f;
+    bind_func(type, __str__, 1, [](VM* vm, ArgsView args){
+        Str s = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
+        return VAR(s);
+    }, f);
+}
+
+void VM::bind__repr__(Type type, Str (*f)(VM*, PyObject*)){
+    _all_types[type].m__repr__ = f;
+    bind_func(type, __repr__, 1, [](VM* vm, ArgsView args){
+        Str s = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
+        return VAR(s);
+    }, f);
+}
+
 void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){
 void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){
     _all_types[type].m__hash__ = f;
     _all_types[type].m__hash__ = f;
     bind_func(type, __hash__, 1, [](VM* vm, ArgsView args){
     bind_func(type, __hash__, 1, [](VM* vm, ArgsView args){
@@ -1460,7 +1483,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{
     ok = false;
     ok = false;
     i64 hash = vm->py_hash(key);
     i64 hash = vm->py_hash(key);
     i = hash & _mask;
     i = hash & _mask;
-    // std::cout << CAST(Str, vm->py_repr(key)) << " " << hash << " " << i << std::endl;
     for(int j=0; j<_capacity; j++) {
     for(int j=0; j<_capacity; j++) {
         if(_items[i].first != nullptr){
         if(_items[i].first != nullptr){
             if(vm->py_eq(_items[i].first, key)) { ok = true; break; }
             if(vm->py_eq(_items[i].first, key)) { ok = true; break; }
@@ -1469,7 +1491,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{
         }
         }
         // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
         // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
         i = ((5*i) + 1) & _mask;
         i = ((5*i) + 1) & _mask;
-        // std::cout << CAST(Str, vm->py_repr(key)) << " next: " << i << std::endl;
     }
     }
 }
 }
 
 
@@ -1600,7 +1621,7 @@ void VM::__breakpoint(){
             for(PyObject* obj: frame_0->_locals){
             for(PyObject* obj: frame_0->_locals){
                 if(obj == PY_NULL) continue;
                 if(obj == PY_NULL) continue;
                 StrName name = frame_0->co->varnames[i++];
                 StrName name = frame_0->co->varnames[i++];
-                stdout_write(_S(name.sv(), " = ", CAST(Str&, vm->py_repr(obj)), '\n'));
+                stdout_write(_S(name.sv(), " = ", vm->py_repr(obj), '\n'));
             }
             }
             continue;
             continue;
         }
         }
@@ -1644,7 +1665,7 @@ void VM::__breakpoint(){
             if(cmd == "p" || cmd == "print"){
             if(cmd == "p" || cmd == "print"){
                 CodeObject_ code = compile(arg, "<stdin>", EVAL_MODE, true);
                 CodeObject_ code = compile(arg, "<stdin>", EVAL_MODE, true);
                 PyObject* retval = vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals);
                 PyObject* retval = vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals);
-                stdout_write(CAST(Str&, vm->py_repr(retval)));
+                stdout_write(vm->py_repr(retval));
                 stdout_write("\n");
                 stdout_write("\n");
             }else if(cmd == "!"){
             }else if(cmd == "!"){
                 CodeObject_ code = compile(arg, "<stdin>", EXEC_MODE, true);
                 CodeObject_ code = compile(arg, "<stdin>", EXEC_MODE, true);

+ 2 - 2
tests/07_dict.py

@@ -166,7 +166,7 @@ assert repr(a) == "{1: 2, 3: 4, 'a': {...}}"
 # test gc
 # test gc
 import gc
 import gc
 gc.collect()
 gc.collect()
-x = gc.collect()
 for k, v in a.items():
 for k, v in a.items():
     pass
     pass
-assert x+1 == gc.collect()
+assert gc.collect() == 1
+