Repository: taichi-dev/taichi (language: python)

Issue:
Better error message when creating sparse snodes on backends that do not support sparse

```
>>> import taichi as ti
[Taichi] version 1.5.0, llvm 15.0.5, commit 66948bc5, osx, python 3.8.12
>>> ti.init(ti.metal)
[Taichi] Starting on arch=metal
>>> a = ti.root.pointer(ti.i, 10)
>>> b = ti.field(ti.i32)
>>> a.place(b)
<ti.SNode of type pointer>
>>> b[1] = 1
[E 01/17/23 14:01:13.313 455093] [spirv_codegen.cpp:visit@424] Not supported.


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/lin/taichi/python/taichi/lang/util.py", line 310, in wrapped
    return func(*args, **kwargs)
  File "/Users/lin/taichi/python/taichi/lang/field.py", line 362, in __setitem__
    self.host_accessors[0].setter(value, *self._pad_key(key))
  File "/Users/lin/taichi/python/taichi/lang/field.py", line 418, in setter
    snode.write_int(key, value)
RuntimeError: [spirv_codegen.cpp:visit@424] Not supported.
```


Discussion / hints:

Fix patch:
diff --git a/python/taichi/_snode/fields_builder.py b/python/taichi/_snode/fields_builder.py
index da576aa8d4b9a..67d9ad4dd2450 100644
--- a/python/taichi/_snode/fields_builder.py
+++ b/python/taichi/_snode/fields_builder.py
@@ -74,9 +74,10 @@ def dense(self, indices: Union[Sequence[_Axis], _Axis],
     def pointer(self, indices: Union[Sequence[_Axis], _Axis],
                 dimensions: Union[Sequence[int], int]):
         """Same as :func:`taichi.lang.snode.SNode.pointer`"""
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Pointer SNode on metal backend is deprecated and removed.")
+                "Pointer SNode is not supported on this backend.")
         self._check_not_finalized()
         self.empty = False
         return self.root.pointer(indices, dimensions)
@@ -90,9 +91,10 @@ def dynamic(self,
                 dimension: Union[Sequence[int], int],
                 chunk_size: Optional[int] = None):
         """Same as :func:`taichi.lang.snode.SNode.dynamic`"""
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Dynamic SNode on metal backend is deprecated and removed.")
+                "Dynamic SNode is not supported on this backend.")
         self._check_not_finalized()
         self.empty = False
         return self.root.dynamic(index, dimension, chunk_size)
@@ -100,9 +102,10 @@ def dynamic(self,
     def bitmasked(self, indices: Union[Sequence[_Axis], _Axis],
                   dimensions: Union[Sequence[int], int]):
         """Same as :func:`taichi.lang.snode.SNode.bitmasked`"""
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Bitmasked SNode on metal backend is deprecated and removed.")
+                "Bitmasked SNode is not supported on this backend.")
         self._check_not_finalized()
         self.empty = False
         return self.root.bitmasked(indices, dimensions)
diff --git a/python/taichi/lang/snode.py b/python/taichi/lang/snode.py
index 41a5f99376dc3..c893fafbe3dd2 100644
--- a/python/taichi/lang/snode.py
+++ b/python/taichi/lang/snode.py
@@ -46,9 +46,10 @@ def pointer(self, axes, dimensions):
         Returns:
             The added :class:`~taichi.lang.SNode` instance.
         """
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Pointer SNode on metal backend is deprecated and removed.")
+                "Pointer SNode is not supported on this backend.")
         if isinstance(dimensions, numbers.Number):
             dimensions = [dimensions] * len(axes)
         return SNode(self.ptr.pointer(axes, dimensions, get_traceback()))
@@ -73,9 +74,10 @@ def dynamic(self, axis, dimension, chunk_size=None):
         Returns:
             The added :class:`~taichi.lang.SNode` instance.
         """
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Dynamic SNode on metal backend is deprecated and removed.")
+                "Dynamic SNode is not supported on this backend.")
         assert len(axis) == 1
         if chunk_size is None:
             chunk_size = dimension
@@ -92,9 +94,10 @@ def bitmasked(self, axes, dimensions):
         Returns:
             The added :class:`~taichi.lang.SNode` instance.
         """
-        if impl.current_cfg().arch == _ti_core.metal:
+        if not _ti_core.is_extension_supported(impl.current_cfg().arch,
+                                               _ti_core.Extension.sparse):
             raise TaichiRuntimeError(
-                "Bitmasked SNode on metal backend is deprecated and removed.")
+                "Bitmasked SNode is not supported on this backend.")
         if isinstance(dimensions, numbers.Number):
             dimensions = [dimensions] * len(axes)
         return SNode(self.ptr.bitmasked(axes, dimensions, get_traceback()))


Test patch:
diff --git a/tests/python/test_deprecation.py b/tests/python/test_deprecation.py
index 9d0df01189b55..3affa785d8493 100644
--- a/tests/python/test_deprecation.py
+++ b/tests/python/test_deprecation.py
@@ -74,24 +74,6 @@ def test_deprecate_element_shape_ndarray_arg():
                      element_shape=(1, ))
 
 
-# Remove this before v1.5.0
-@test_utils.test(arch=ti.metal)
-def test_deprecate_metal_sparse():
-    with pytest.raises(
-            ti.TaichiRuntimeError,
-            match="Pointer SNode on metal backend is deprecated and removed."):
-        ti.root.pointer(ti.i, 10)
-    with pytest.raises(
-            ti.TaichiRuntimeError,
-            match="Bitmasked SNode on metal backend is deprecated and removed."
-    ):
-        ti.root.bitmasked(ti.j, 10)
-    with pytest.raises(
-            ti.TaichiRuntimeError,
-            match="Dynamic SNode on metal backend is deprecated and removed."):
-        ti.root.dynamic(ti.i, 10)
-
-
 @test_utils.test(arch=ti.vulkan)
 def test_deprecated_rwtexture_type():
     n = 128
diff --git a/tests/python/test_field.py b/tests/python/test_field.py
index d502c7621bb51..1c5582bc78d1f 100644
--- a/tests/python/test_field.py
+++ b/tests/python/test_field.py
@@ -324,3 +324,39 @@ def foo():
             b = a * x
 
         foo()
+
+
+@test_utils.test(exclude=[ti.x64, ti.arm64, ti.cuda])
+def test_sparse_not_supported():
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Pointer SNode is not supported on this backend."):
+        ti.root.pointer(ti.i, 10)
+
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Pointer SNode is not supported on this backend."):
+        a = ti.root.dense(ti.i, 10)
+        a.pointer(ti.j, 10)
+
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Dynamic SNode is not supported on this backend."):
+        ti.root.dynamic(ti.i, 10)
+
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Dynamic SNode is not supported on this backend."):
+        a = ti.root.dense(ti.i, 10)
+        a.dynamic(ti.j, 10)
+
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Bitmasked SNode is not supported on this backend."):
+        ti.root.bitmasked(ti.i, 10)
+
+    with pytest.raises(
+            ti.TaichiRuntimeError,
+            match="Bitmasked SNode is not supported on this backend."):
+        a = ti.root.dense(ti.i, 10)
+        a.bitmasked(ti.j, 10)