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):