Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cpp/csp/cppnodes/baselibimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,9 @@ DECLARE_CPPNODE( struct_field )
START()
{
auto * structType = static_cast<const CspStructType *>( x.type() );
//Special check for null meta ( csp.Struct type ) which can end up here from a csp.static_cast
if( !structType -> meta() )
CSP_THROW( TypeError, "Struct csp.Struct has no field named " << field.value() );
m_fieldAccess = structType -> meta() -> field( field );
if( !m_fieldAccess )
CSP_THROW( TypeError, "Struct " << structType -> meta() -> name() << " has no field named " << field.value() );
Expand Down
7 changes: 5 additions & 2 deletions csp/baselib.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,11 @@ def static_cast(x: ts["T"], outType: "U") -> ts["U"]:
This should only be used when the caller knows with 100% certainty that the type conversion is always valid
as there will be no runtime type checking.
"""
# Special case bool / int which are native types, but bool evaluates as a subclass of int
if not issubclass(outType, x.tstype.typ) or (outType is bool and x.tstype.typ is int):
# Allow static_cast on subclass types except for these exceptions:
# - type object which is a base of everything
# - case bool / int which are native types, but bool evaluates as a subclass of int

if x.tstype.typ is object or not issubclass(outType, x.tstype.typ) or (outType is bool and x.tstype.typ is int):
raise TypeError(f"Unable to csp.static_cast edge of type {x.tstype.typ.__name__} to {outType.__name__}")
return Edge(ts[outType], nodedef=x.nodedef, output_idx=x.output_idx, basket_idx=x.basket_idx)

Expand Down
13 changes: 13 additions & 0 deletions csp/tests/test_baselib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,7 @@ class D(Base):
x_int = csp.const(1)
x_bool = csp.const(True)
x_float = csp.const(123.456)
x_object = csp.const.using(T=object)(1)

x_b = csp.const.using(T=Base)(D(a=1, b=2.1))

Expand All @@ -1228,10 +1229,22 @@ def g():
with self.assertRaisesRegex(TypeError, "Unable to csp.static_cast edge of type int to bool"):
csp.run(csp.static_cast(x_int, bool), starttime=utc_now(), endtime=timedelta())

with self.assertRaisesRegex(TypeError, "Unable to csp.static_cast edge of type object to int"):
csp.run(csp.static_cast(x_object, int), starttime=utc_now(), endtime=timedelta())

# Runtime type check
with self.assertRaisesRegex(TypeError, 'expected output type on .* to be of type "int" got type "float"'):
csp.run(csp.dynamic_cast(x_float, int), starttime=utc_now(), endtime=timedelta())

class S(csp.Struct):
a: int

with self.assertRaisesRegex(TypeError, "Struct csp.Struct has no field named a"):
# Was a crash before, dont crash on struct_field access when upcast from csp.Struct which has no metadata
csp.run(
csp.static_cast(csp.const.using(T=csp.Struct)(S(a=1)), S).a, starttime=utc_now(), endtime=timedelta()
)


if __name__ == "__main__":
unittest.main()
Loading