summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2022-08-10 15:37:19 -0700
committerGitHub <noreply@github.com>2022-08-10 15:37:19 -0700
commita083a37ee58dc48d92cf2b844466a295eb3e643e (patch)
tree69028f3d9f92fa7e2085c76bc234e1949ef645c5
parent88f04c29244af23c1cdd472d8d1ae3e5a650494e (diff)
Add `none` literal that is convertible to `Optional`. (#2356)
* Add `none` literal that is convertible to `Optional`. * Fix cpu code gen. * Include vk and cpu test for is-as operator test. * Inline comparison operators. Co-authored-by: Yong He <yhe@nvidia.com>
-rw-r--r--build/visual-studio/slang/slang.vcxproj2
-rw-r--r--build/visual-studio/slang/slang.vcxproj.filters6
-rw-r--r--source/slang/core.meta.slang34
-rw-r--r--source/slang/slang-ast-builder.cpp10
-rw-r--r--source/slang/slang-ast-builder.h4
-rw-r--r--source/slang/slang-ast-expr.h5
-rw-r--r--source/slang/slang-ast-iterator.h4
-rw-r--r--source/slang/slang-ast-support-types.h3
-rw-r--r--source/slang/slang-ast-type.h5
-rw-r--r--source/slang/slang-check-conversion.cpp19
-rw-r--r--source/slang/slang-check-expr.cpp6
-rw-r--r--source/slang/slang-check-impl.h1
-rw-r--r--source/slang/slang-emit.cpp3
-rw-r--r--source/slang/slang-ir-cleanup-void.cpp159
-rw-r--r--source/slang/slang-ir-cleanup-void.h15
-rw-r--r--source/slang/slang-language-server-completion.cpp2
-rw-r--r--source/slang/slang-lower-to-ir.cpp5
-rw-r--r--source/slang/slang-parser.cpp6
-rw-r--r--tests/language-feature/interfaces/is-as-dynamic.slang2
-rw-r--r--tests/language-feature/types/optional.slang53
-rw-r--r--tests/language-feature/types/optional.slang.expected.txt2
21 files changed, 343 insertions, 3 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj
index 7e4144240..39e5f9083 100644
--- a/build/visual-studio/slang/slang.vcxproj
+++ b/build/visual-studio/slang/slang.vcxproj
@@ -345,6 +345,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
<ClInclude Include="..\..\..\source\slang\slang-ir-augment-make-existential.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-bind-existentials.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-byte-address-legalize.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-ir-cleanup-void.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-clone.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-collect-global-uniforms.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-com-interface.h" />
@@ -508,6 +509,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
<ClCompile Include="..\..\..\source\slang\slang-ir-augment-make-existential.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-bind-existentials.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-byte-address-legalize.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-ir-cleanup-void.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-clone.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-collect-global-uniforms.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-com-interface.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters
index 27589ffee..739a6a442 100644
--- a/build/visual-studio/slang/slang.vcxproj.filters
+++ b/build/visual-studio/slang/slang.vcxproj.filters
@@ -132,6 +132,9 @@
<ClInclude Include="..\..\..\source\slang\slang-ir-byte-address-legalize.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-ir-cleanup-void.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-ir-clone.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -617,6 +620,9 @@
<ClCompile Include="..\..\..\source\slang\slang-ir-byte-address-legalize.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-ir-cleanup-void.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-ir-clone.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 616511b01..d912fe8ce 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -349,6 +349,12 @@ struct NullPtr
{
};
+__magic_type(NoneType)
+__intrinsic_type($(kIROp_VoidType))
+struct __none_t
+{
+};
+
__generic<T>
__magic_type(PtrType)
__intrinsic_type($(kIROp_PtrType))
@@ -472,8 +478,36 @@ struct Optional
__intrinsic_op($(kIROp_GetOptionalValue))
get;
}
+
+ __implicit_conversion($(kConversionCost_ValToOptional))
+ __intrinsic_op($(kIROp_MakeOptionalValue))
+ __init(T val);
};
+__generic<T>
+[__unsafeForceInlineEarly]
+bool operator==(Optional<T> val, __none_t noneVal)
+{
+ return !val.hasValue;
+}
+__generic<T>
+[__unsafeForceInlineEarly]
+bool operator!=(Optional<T> val, __none_t noneVal)
+{
+ return val.hasValue;
+}
+__generic<T>
+[__unsafeForceInlineEarly]
+bool operator==(__none_t noneVal, Optional<T> val)
+{
+ return !val.hasValue;
+}
+__generic<T>
+[__unsafeForceInlineEarly]
+bool operator!=(__none_t noneVal, Optional<T> val)
+{
+ return val.hasValue;
+}
__magic_type(StringType)
__intrinsic_type($(kIROp_StringType))
diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp
index 2acdc26d3..fa8051171 100644
--- a/source/slang/slang-ast-builder.cpp
+++ b/source/slang/slang-ast-builder.cpp
@@ -131,6 +131,16 @@ Type* SharedASTBuilder::getNullPtrType()
return m_nullPtrType;
}
+Type* SharedASTBuilder::getNoneType()
+{
+ if (!m_noneType)
+ {
+ auto noneTypeDecl = findMagicDecl("NoneType");
+ m_noneType = DeclRefType::create(m_astBuilder, makeDeclRef<Decl>(noneTypeDecl));
+ }
+ return m_noneType;
+}
+
SharedASTBuilder::~SharedASTBuilder()
{
// Release built in types..
diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h
index e62e92a7b..d363cb8f6 100644
--- a/source/slang/slang-ast-builder.h
+++ b/source/slang/slang-ast-builder.h
@@ -33,6 +33,8 @@ public:
Type* getDynamicType();
/// Get the NullPtr type
Type* getNullPtrType();
+ /// Get the NullPtr type
+ Type* getNoneType();
const ReflectClassInfo* findClassInfo(Name* name);
SyntaxClass<NodeBase> findSyntaxClass(Name* name);
@@ -74,6 +76,7 @@ protected:
Type* m_enumTypeType = nullptr;
Type* m_dynamicType = nullptr;
Type* m_nullPtrType = nullptr;
+ Type* m_noneType = nullptr;
Type* m_builtinTypes[Index(BaseType::CountOf)];
@@ -139,6 +142,7 @@ public:
Type* getBottomType() { return m_sharedASTBuilder->m_bottomType; }
Type* getStringType() { return m_sharedASTBuilder->getStringType(); }
Type* getNullPtrType() { return m_sharedASTBuilder->getNullPtrType(); }
+ Type* getNoneType() { return m_sharedASTBuilder->getNoneType(); }
Type* getEnumTypeType() { return m_sharedASTBuilder->getEnumTypeType(); }
// Construct the type `Ptr<valueType>`, where `Ptr`
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 2dfd937a4..6f10f2d82 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -101,6 +101,11 @@ class NullPtrLiteralExpr : public LiteralExpr
SLANG_AST_CLASS(NullPtrLiteralExpr)
};
+class NoneLiteralExpr : public LiteralExpr
+{
+ SLANG_AST_CLASS(NoneLiteralExpr)
+};
+
class StringLiteralExpr : public LiteralExpr
{
SLANG_AST_CLASS(StringLiteralExpr)
diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h
index 8461ff7a3..233ce9a17 100644
--- a/source/slang/slang-ast-iterator.h
+++ b/source/slang/slang-ast-iterator.h
@@ -46,6 +46,10 @@ struct ASTIterator
{
iterator->maybeDispatchCallback(expr);
}
+ void visitNoneLiteralExpr(NoneLiteralExpr* expr)
+ {
+ iterator->maybeDispatchCallback(expr);
+ }
void visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
{
iterator->maybeDispatchCallback(expr);
diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h
index f74abc08f..00a4a482c 100644
--- a/source/slang/slang-ast-support-types.h
+++ b/source/slang/slang-ast-support-types.h
@@ -88,6 +88,9 @@ namespace Slang
// Conversion that is lossless and keeps the "kind" of the value the same
kConversionCost_RankPromotion = 150,
+ kConversionCost_NoneToOptional = 150,
+ kConversionCost_ValToOptional = 150,
+ kConversionCost_NullPtrToPtr = 150,
// Conversions that are lossless, but change "kind"
kConversionCost_UnsignedToSignedPromotion = 200,
diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h
index 7b94cbe6d..0ee0fce2c 100644
--- a/source/slang/slang-ast-type.h
+++ b/source/slang/slang-ast-type.h
@@ -529,6 +529,11 @@ class PtrTypeBase : public BuiltinType
Type* getValueType();
};
+class NoneType : public BuiltinType
+{
+ SLANG_AST_CLASS(NoneType)
+};
+
class NullPtrType : public BuiltinType
{
SLANG_AST_CLASS(NullPtrType)
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index 6290acbd9..4bcf07b2b 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -773,13 +773,28 @@ namespace Slang
{
if (outCost)
{
- *outCost = kConversionCost_None;
+ *outCost = kConversionCost_NullPtrToPtr;
}
if (outToExpr)
*outToExpr = fromExpr;
return true;
}
-
+ // none_t can be cast into any Optional<T> type.
+ if (as<NoneType>(fromType) && as<OptionalType>(toType))
+ {
+ if (outCost)
+ {
+ *outCost = kConversionCost_NoneToOptional;
+ }
+ if (outToExpr)
+ {
+ auto resultExpr = getASTBuilder()->create<MakeOptionalExpr>();
+ resultExpr->loc = fromExpr->loc;
+ resultExpr->type = toType;
+ *outToExpr = resultExpr;
+ }
+ return true;
+ }
// If we are casting to an interface type, then that will succeed
// if the "from" type conforms to the interface.
//
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 714eba9a3..cb75e3078 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -822,6 +822,12 @@ namespace Slang
return expr;
}
+ Expr* SemanticsExprVisitor::visitNoneLiteralExpr(NoneLiteralExpr* expr)
+ {
+ expr->type = m_astBuilder->getNoneType();
+ return expr;
+ }
+
Expr* SemanticsExprVisitor::visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
{
// The expression might already have a type, determined by its suffix.
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index df713d80f..345652263 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1697,6 +1697,7 @@ namespace Slang
Expr* visitIncompleteExpr(IncompleteExpr* expr);
Expr* visitBoolLiteralExpr(BoolLiteralExpr* expr);
Expr* visitNullPtrLiteralExpr(NullPtrLiteralExpr* expr);
+ Expr* visitNoneLiteralExpr(NoneLiteralExpr* expr);
Expr* visitIntegerLiteralExpr(IntegerLiteralExpr* expr);
Expr* visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr);
Expr* visitStringLiteralExpr(StringLiteralExpr* expr);
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index da8739a49..f3e78e48f 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -8,6 +8,7 @@
#include "slang-ir-bind-existentials.h"
#include "slang-ir-byte-address-legalize.h"
#include "slang-ir-collect-global-uniforms.h"
+#include "slang-ir-cleanup-void.h"
#include "slang-ir-dce.h"
#include "slang-ir-dll-export.h"
#include "slang-ir-dll-import.h"
@@ -745,6 +746,8 @@ Result linkAndOptimizeIR(
#endif
validateIRModuleIfEnabled(codeGenContext, irModule);
+ cleanUpVoidType(irModule);
+
// Lower all bit_cast operations on complex types into leaf-level
// bit_cast on basic types.
lowerBitCast(targetRequest, irModule);
diff --git a/source/slang/slang-ir-cleanup-void.cpp b/source/slang/slang-ir-cleanup-void.cpp
new file mode 100644
index 000000000..ac520c1d5
--- /dev/null
+++ b/source/slang/slang-ir-cleanup-void.cpp
@@ -0,0 +1,159 @@
+// slang-ir-cleanup-void.cpp
+
+#include "slang-ir-cleanup-void.h"
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+
+namespace Slang
+{
+ struct CleanUpVoidContext
+ {
+ IRModule* module;
+
+ SharedIRBuilder sharedBuilderStorage;
+
+ List<IRInst*> workList;
+ HashSet<IRInst*> workListSet;
+
+ void addToWorkList(
+ IRInst* inst)
+ {
+ for (auto ii = inst->getParent(); ii; ii = ii->getParent())
+ {
+ if (as<IRGeneric>(ii))
+ return;
+ }
+
+ if (workListSet.Contains(inst))
+ return;
+
+ workList.add(inst);
+ workListSet.Add(inst);
+ }
+
+ void processInst(IRInst* inst)
+ {
+ switch (inst->getOp())
+ {
+ case kIROp_Call:
+ {
+ // Remove void argument.
+ auto call = as<IRCall>(inst);
+ List<IRInst*> newArgs;
+ for (UInt i = 0; i < call->getArgCount(); i++)
+ {
+ auto arg = call->getArg(i);
+ if (arg->getDataType() && arg->getDataType()->getOp() == kIROp_VoidType)
+ {
+ continue;
+ }
+ newArgs.add(arg);
+ }
+ if (newArgs.getCount() != (Index)call->getArgCount())
+ {
+ IRBuilder builder(&sharedBuilderStorage);
+ builder.setInsertBefore(call);
+ auto newCall = builder.emitCallInst(call->getFullType(), call->getCallee(), newArgs);
+ call->replaceUsesWith(newCall);
+ call->removeAndDeallocate();
+ inst = newCall;
+ }
+ }
+ break;
+ case kIROp_Func:
+ {
+ // Remove void parameter.
+ List<IRParam*> paramsToRemove;
+ auto func = as<IRFunc>(inst);
+ for (auto param : func->getParams())
+ {
+ if (param->getDataType()->getOp() == kIROp_VoidType)
+ {
+ paramsToRemove.add(param);
+ }
+ }
+ IRBuilder builder(&sharedBuilderStorage);
+ builder.setInsertBefore(func);
+ for (auto param : paramsToRemove)
+ {
+ auto voidVal = builder.getVoidValue();
+ param->replaceUsesWith(voidVal);
+ param->removeAndDeallocate();
+ }
+ }
+ break;
+ case kIROp_FuncType:
+ {
+ auto funcType = as<IRFuncType>(inst);
+ List<IRInst*> newOperands;
+ for (UInt i = 1; i < funcType->getOperandCount(); i++)
+ {
+ auto operand = funcType->getOperand(i);
+ if (operand->getOp() == kIROp_VoidType)
+ {
+ continue;
+ }
+ newOperands.add(operand);
+ }
+ if (newOperands.getCount() != (Index)funcType->getParamCount())
+ {
+ IRBuilder builder(&sharedBuilderStorage);
+ builder.setInsertBefore(funcType);
+ auto newFuncType = builder.getFuncType(newOperands.getCount(), (IRType**)newOperands.getBuffer(), funcType->getResultType());
+ if (newFuncType != funcType)
+ {
+ funcType->replaceUsesWith(newFuncType);
+ funcType->removeAndDeallocate();
+ }
+ inst = newFuncType;
+ }
+ }
+ break;
+ case kIROp_StructType:
+ {
+ // TODO: cleanup void fields.
+ }
+ break;
+ default:
+ break;
+ }
+
+ // TODO: If inst has void type, all uses of it should be replaced with void val.
+ // We should do this only for a subset of opcodes known to be safe.
+
+ }
+
+ void processModule()
+ {
+ SharedIRBuilder* sharedBuilder = &sharedBuilderStorage;
+ sharedBuilder->init(module);
+
+ // Deduplicate equivalent types.
+ sharedBuilder->deduplicateAndRebuildGlobalNumberingMap();
+
+ addToWorkList(module->getModuleInst());
+
+ while (workList.getCount() != 0)
+ {
+ IRInst* inst = workList.getLast();
+
+ workList.removeLast();
+ workListSet.Remove(inst);
+
+ processInst(inst);
+
+ for (auto child = inst->getLastChild(); child; child = child->getPrevInst())
+ {
+ addToWorkList(child);
+ }
+ }
+ }
+ };
+
+ void cleanUpVoidType(IRModule* module)
+ {
+ CleanUpVoidContext context;
+ context.module = module;
+ context.processModule();
+ }
+}
diff --git a/source/slang/slang-ir-cleanup-void.h b/source/slang/slang-ir-cleanup-void.h
new file mode 100644
index 000000000..b6c6fc5f7
--- /dev/null
+++ b/source/slang/slang-ir-cleanup-void.h
@@ -0,0 +1,15 @@
+// slang-ir-cleanup-void.h
+#pragma once
+
+#include "slang-ir.h"
+
+namespace Slang
+{
+ struct IRModule;
+ class DiagnosticSink;
+
+ /// Cleanup all uses of void and void types.
+ void cleanUpVoidType(
+ IRModule* module);
+
+}
diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp
index 4ae5bcb37..ac3cdefc0 100644
--- a/source/slang/slang-language-server-completion.cpp
+++ b/source/slang/slang-language-server-completion.cpp
@@ -31,7 +31,7 @@ static const char* kStmtKeywords[] = {
"extension", "associatedtype", "this", "namespace", "This", "using",
"__generic", "__exported", "import", "enum", "break", "continue",
"discard", "defer", "cbuffer", "tbuffer", "func", "is",
- "as", "nullptr", "true", "false"};
+ "as", "nullptr", "none", "true", "false"};
static const char* hlslSemanticNames[] = {
"register",
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index aef60c9d9..c7fbfda77 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -3365,6 +3365,11 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
return LoweredValInfo::simple(context->irBuilder->getPtrValue(nullptr));
}
+ LoweredValInfo visitNoneLiteralExpr(NoneLiteralExpr*)
+ {
+ return LoweredValInfo::simple(context->irBuilder->getVoidValue());
+ }
+
LoweredValInfo visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
{
auto type = lowerType(context, expr->type);
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 4baa7211e..7447859c7 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -5031,6 +5031,11 @@ namespace Slang
return parser->astBuilder->create<NullPtrLiteralExpr>();
}
+ static NodeBase* parseNoneExpr(Parser* parser, void* /*userData*/)
+ {
+ return parser->astBuilder->create<NoneLiteralExpr>();
+ }
+
static NodeBase* parseTryExpr(Parser* parser, void* /*userData*/)
{
auto tryExpr = parser->astBuilder->create<TryExpr>();
@@ -6587,6 +6592,7 @@ namespace Slang
_makeParseExpr("true", parseTrueExpr),
_makeParseExpr("false", parseFalseExpr),
_makeParseExpr("nullptr", parseNullPtrExpr),
+ _makeParseExpr("none", parseNoneExpr),
_makeParseExpr("try", parseTryExpr),
_makeParseExpr("__TaggedUnion", parseTaggedUnionType),
_makeParseExpr("__jvp", parseJVPDifferentiate)
diff --git a/tests/language-feature/interfaces/is-as-dynamic.slang b/tests/language-feature/interfaces/is-as-dynamic.slang
index 4499db53a..1b163361a 100644
--- a/tests/language-feature/interfaces/is-as-dynamic.slang
+++ b/tests/language-feature/interfaces/is-as-dynamic.slang
@@ -3,6 +3,8 @@
// Test that `is` and `as` operators works as intended in dynamic dispatch.
//TEST(compute):COMPARE_COMPUTE: -shaderobj
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -vk -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -cpu -compute
//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
RWStructuredBuffer<int> outputBuffer;
diff --git a/tests/language-feature/types/optional.slang b/tests/language-feature/types/optional.slang
new file mode 100644
index 000000000..ae1711cd7
--- /dev/null
+++ b/tests/language-feature/types/optional.slang
@@ -0,0 +1,53 @@
+// optional.slang
+
+// Test that `Optional` construction and conversion works.
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -vk -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -cpu -compute
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<int> outputBuffer;
+
+[anyValueSize(8)]
+interface IFoo
+{
+ int method();
+}
+
+//TEST_INPUT: type_conformance Impl1:IFoo = 0
+struct Impl1 : IFoo
+{
+ int data;
+ int method() { return data; }
+}
+
+Optional<Impl1> getVal(bool shouldHaveVal)
+{
+ if (shouldHaveVal)
+ {
+ Impl1 val;
+ val.data = 1;
+ return val;
+ }
+ return none;
+}
+
+[numthreads(1, 1, 1)]
+void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID)
+{
+ let v1 = getVal(true);
+ let v2 = getVal(false);
+ int result = 0;
+ if (v1.hasValue)
+ result += v1.value.data;
+ if (v2.hasValue)
+ result += v2.value.data;
+ outputBuffer[0] = result;
+ if (v1 != none)
+ result += v1.value.data;
+ if (!(v2 == none))
+ result += v2.value.data;
+ outputBuffer[1] = result;
+
+}
diff --git a/tests/language-feature/types/optional.slang.expected.txt b/tests/language-feature/types/optional.slang.expected.txt
new file mode 100644
index 000000000..1191247b6
--- /dev/null
+++ b/tests/language-feature/types/optional.slang.expected.txt
@@ -0,0 +1,2 @@
+1
+2