summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-08-06 12:14:52 -0700
committerGitHub <noreply@github.com>2019-08-06 12:14:52 -0700
commit81ce78d08a7e3fbe74f2fd41c5a258ea4b078245 (patch)
tree06e0c3cae361ee85cbee513ee539d9f9bde647f9 /source
parent89758544c45a15e546a1eb08891e2787bb88de4a (diff)
Add support for the HLSL "cast from zero" idiom (#1008)
If the user writes code like this: MyStruct s = (MyStruct) 0; then we will interpret it as if they had written: MyStruct s = {}; That is, the "cast from zero" idiom will be taken as a legacy syntax for default construction (using an empty initializer list). This will be semantically equivalent to zero-initialization for all existing HLSL code (where `struct` fields can't have default initialization expressions defined), and is the easiest option for us to support in Slang (since we already support default-initialization using empty initializer lists). The implementation of this feature is narrowly scoped: * It only targets explicit cast expressions like `(MyStruct) 0` and not "constructor" syntax like `MyStruct(0)` * It only applies when there is a single argument that is exactly an integer literal with a zero value (not a reference to a `static const int` that happens to be zero). This change adds a test case to make sure that the feature works as expected. Because it relies on our existing initializer-list handling, the "cast from zero" idiom should work for any user-defined type where an initializer list would work.
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check.cpp80
1 files changed, 80 insertions, 0 deletions
diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp
index 534642a1c..edf41fff0 100644
--- a/source/slang/slang-check.cpp
+++ b/source/slang/slang-check.cpp
@@ -8464,6 +8464,21 @@ namespace Slang
context.baseExpr = funcOverloadExpr2->base;
}
+ // TODO: We should have a special case here where an `InvokeExpr`
+ // with a single argument where the base/func expression names
+ // a type should always be treated as an explicit type coercion
+ // (and hence bottleneck through `coerce()`) instead of just
+ // as a constructor call.
+ //
+ // Such a special-case would help us handle cases of identity
+ // casts (casting an expression to the type it already has),
+ // without needing dummy initializer/constructor declarations.
+ //
+ // Handling that special casing here (rather than in, say,
+ // `visitTypeCastExpr`) would allow us to continue to ensure
+ // that `(T) expr` and `T(expr)` continue to be semantically
+ // equivalent in (almost) all cases.
+
if (!context.bestCandidate)
{
AddOverloadCandidates(funcExpr, context);
@@ -8885,6 +8900,71 @@ namespace Slang
arg = CheckExpr(arg);
}
+ // LEGACY FEATURE: As a backwards-compatibility feature
+ // for HLSL, we will allow for a cast to a `struct` type
+ // from a literal zero, with the semantics of default
+ // initialization.
+ //
+ if( auto declRefType = typeExp.type.as<DeclRefType>() )
+ {
+ if(auto structDeclRef = declRefType->declRef.as<StructDecl>())
+ {
+ if( expr->Arguments.getCount() == 1 )
+ {
+ auto arg = expr->Arguments[0];
+ if( auto intLitArg = arg.as<IntegerLiteralExpr>() )
+ {
+ if(getIntegerLiteralValue(intLitArg->token) == 0)
+ {
+ // At this point we have confirmed that the cast
+ // has the right form, so we want to apply our special case.
+ //
+ // TODO: If/when we allow for user-defined initializer/constructor
+ // definitions we would have to be careful here because it is
+ // possible that the target type has defined an initializer/constructor
+ // that takes a single `int` parmaeter and means to call that instead.
+ //
+ // For now that should be a non-issue, and in a pinch such a user
+ // could use `T(0)` instead of `(T) 0` to get around this special
+ // HLSL legacy feature.
+
+ // We will type-check code like:
+ //
+ // MyStruct s = (MyStruct) 0;
+ //
+ // the same as:
+ //
+ // MyStruct s = {};
+ //
+ // That is, we construct an empty initializer list, and then coerce
+ // that initializer list expression to the desired type (letting
+ // the code for handling initializer lists work out all of the
+ // details of what is/isn't valid). This choice means we get
+ // to benefit from the existing codegen support for initializer
+ // lists, rather than needing the `(MyStruct) 0` idiom to be
+ // special-cased in later stages of the compiler.
+ //
+ // Note: we use an empty initializer list `{}` instead of an
+ // initializer list with a single zero `{0}`, which is semantically
+ // significant if the first field of `MyStruct` had its own
+ // default initializer defined as part of the `struct` definition.
+ // Basically we have chosen to interpret the "cast from zero" syntax
+ // as sugar for default initialization, and *not* specifically
+ // for zero-initialization. That choice could be revisited if
+ // users express displeasure. For now there isn't enough usage
+ // of explicit default initializers for `struct` fields to
+ // make this a major concern (since they aren't supported in HLSL).
+ //
+ RefPtr<InitializerListExpr> initListExpr = new InitializerListExpr();
+ auto checkedInitListExpr = visitInitializerListExpr(initListExpr);
+ return coerce(typeExp.type, initListExpr);
+ }
+ }
+ }
+ }
+ }
+
+
// Now process this like any other explicit call (so casts
// and constructor calls are semantically equivalent).
return CheckInvokeExprWithCheckedOperands(expr);