This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 59bba8d239 [BugFix][Relax][ONNX] Fix ConstantOfShape converter when 
value attr is absent (#19480)
59bba8d239 is described below

commit 59bba8d239e5d8b82a1676135df2521a863a0e75
Author: Soowon Jeong <[email protected]>
AuthorDate: Fri May 1 09:01:09 2026 +0900

    [BugFix][Relax][ONNX] Fix ConstantOfShape converter when value attr is 
absent (#19480)
    
    ## Motivation
    
    The `value` attribute on ONNX `ConstantOfShape` is optional and defaults
    to a zero float32 scalar of the requested shape. The converter calls
    `get_numpy(attr.get("value", 0))`, which feeds the int default into
    `get_numpy` → `onnx.numpy_helper.to_array(0)` → `int.HasField` and
    crashes:
    
    ```
    AttributeError: 'int' object has no attribute 'HasField'
    ```
    
    Minimal repro:
    
    ```python
    from onnx import TensorProto, helper
    node = helper.make_node("ConstantOfShape", ["shape"], ["y"])  # no value 
attr
    shape = helper.make_tensor("shape", TensorProto.INT64, [2], [2, 3])
    graph = helper.make_graph([node], "g", [],
                              [helper.make_tensor_value_info("y", 
TensorProto.FLOAT, None)],
                              initializer=[shape])
    model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 
18)])
    from tvm.relax.frontend.onnx import from_onnx
    from_onnx(model)  # AttributeError
    ```
    
    ORT produces a `(2, 3)` float32 tensor of zeros, matching the spec
    default.
    
    ## Fix
    
    The existing converter was already structured to handle the absent-value
    case: there is an `else` branch that sets `dtype = "float32"` and uses
    `value = 0` in `np.full(shape, value, dtype)`, exactly the spec default.
    The bug is just that `get_numpy(0)` raises before that branch is
    reached.
    
    Dispatch on attribute presence first:
    
    ```python
    attr_value = attr.get("value")
    value = get_numpy(attr_value) if attr_value is not None else 0
    ```
    
    When `value` is supplied as a TensorProto, behavior is unchanged. When
    absent, `value` becomes int `0`, the `isinstance(value, _np.ndarray)`
    check falls through to `dtype = "float32"`, and `np.full(shape, 0,
    "float32")` produces the spec default.
    
    ## Test plan
    
    - [x] `pytest
    
tests/python/relax/test_frontend_onnx.py::test_constantofshape_default_value`
    — new test covering the absent-value case passes.
    - [x] Confirmed the new test fails on `upstream/main` without this patch
    (same `AttributeError`).
---
 python/tvm/relax/frontend/onnx/onnx_frontend.py |  5 ++++-
 tests/python/relax/test_frontend_onnx.py        | 22 ++++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py 
b/python/tvm/relax/frontend/onnx/onnx_frontend.py
index 98571634a7..9f7c09a953 100644
--- a/python/tvm/relax/frontend/onnx/onnx_frontend.py
+++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py
@@ -1847,7 +1847,10 @@ class ConstantOfShape(OnnxOpConverter):
     @classmethod
     def _impl_v9(cls, bb, inputs, attr, params):
         shape = inputs[0]
-        value = get_numpy(attr.get("value", 0))
+        # ONNX spec: `value` is optional and defaults to a zero float32 scalar.
+        # `get_numpy` requires a TensorProto, so dispatch on presence first.
+        attr_value = attr.get("value")
+        value = get_numpy(attr_value) if attr_value is not None else 0
         if isinstance(value, _np.ndarray):
             dtype = str(value.dtype)
         else:
diff --git a/tests/python/relax/test_frontend_onnx.py 
b/tests/python/relax/test_frontend_onnx.py
index d67309d223..136016e823 100644
--- a/tests/python/relax/test_frontend_onnx.py
+++ b/tests/python/relax/test_frontend_onnx.py
@@ -2686,6 +2686,28 @@ def test_constantofshape():
     verify_constantofshape((1, 2, 3), -1, "float32")
 
 
+def test_constantofshape_default_value():
+    # Per ONNX spec, the `value` attribute is optional and defaults to a zero
+    # float32 scalar of the requested shape.
+    shape_init = helper.make_tensor("shape", TensorProto.INT64, [2], [2, 3])
+    node = helper.make_node("ConstantOfShape", ["shape"], ["y"])
+    graph = helper.make_graph(
+        [node],
+        "constantofshape_default_value_test",
+        inputs=[],
+        outputs=[helper.make_tensor_value_info("y", TensorProto.FLOAT, None)],
+        initializer=[shape_init],
+    )
+    model = helper.make_model(graph, 
producer_name="constantofshape_default_value_test")
+
+    tvm_model = from_onnx(model)
+    tvm_model = relax.transform.LegalizeOps()(tvm_model)
+    exe = tvm.compile(tvm_model, target="llvm")
+    vm = relax.VirtualMachine(exe, device=tvm.cpu())
+    out = vm["main"]().numpy()
+    np.testing.assert_array_equal(out, np.zeros((2, 3), dtype="float32"))
+
+
 def test_slice():
     def verify_slice(data_shape, output_shape, starts, ends, axes=None, 
steps=None):
         if isinstance(starts, list):

Reply via email to