From 462ea4e66569efa978e4057ea2d041c69d4a729b Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 10 Oct 2025 14:34:56 -0700 Subject: Fix `specializeRTTIObject` to use non-zero RTTI value to work with `Optional`. (#8677) Closes #8673. The issue is that we use the RTTI field of an existential to check if it is null. We have the logic to help the user to fill in a non-zero value for the RTTI field when such an object is filled from the host. However, when there is slang code creating an existential value, we still have old logic in the compiler that just fills in 0 for the RTTI field, causing an `Optional.hasValue` to always return false in such cases. --- source/slang/slang-emit-vm.cpp | 11 ++++++++++ source/slang/slang-ir-lower-existential.cpp | 8 +++++--- source/slang/slang-ir-lower-generics.cpp | 6 +++--- tests/language-feature/types/optional-ifoo.slang | 26 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 tests/language-feature/types/optional-ifoo.slang diff --git a/source/slang/slang-emit-vm.cpp b/source/slang/slang-emit-vm.cpp index 1602fbfb8..80d3762aa 100644 --- a/source/slang/slang-emit-vm.cpp +++ b/source/slang/slang-emit-vm.cpp @@ -247,6 +247,17 @@ public: operand = addConstantValue(constantInst); mapInstToOperand[inst] = operand; } + else if (auto constantVector = as(inst)) + { + SLANG_ASSERT(constantVector->getOperandCount() > 0); + operand = ensureInst(constantVector->getOperand(0)); + for (UInt i = 1; i < constantVector->getOperandCount(); i++) + { + ensureInst(constantVector->getOperand(i)); + } + operand.size *= (uint32_t)constantVector->getOperandCount(); + mapInstToOperand[inst] = operand; + } else { SLANG_UNEXPECTED("unsupported global inst for vm bytecode emit"); diff --git a/source/slang/slang-ir-lower-existential.cpp b/source/slang/slang-ir-lower-existential.cpp index 076ed57bd..ea7f906a9 100644 --- a/source/slang/slang-ir-lower-existential.cpp +++ b/source/slang/slang-ir-lower-existential.cpp @@ -68,13 +68,15 @@ struct ExistentialLoweringContext auto witnessTableIdType = cast(tupleType->getOperand(1)); auto anyValueType = cast(tupleType->getOperand(2)); - // Create a null value for `rttiObject` for now since it will not be used. + // Create a standin value for `rttiObject` for now since it will not be used + // other than test for null in the case of `Optional`. auto uint2Type = builder->getVectorType( builder->getUIntType(), builder->getIntValue(builder->getIntType(), 2)); + IRInst* standinVal = builder->getIntValue(builder->getUIntType(), 0xFFFFFFFF); IRInst* zero = builder->getIntValue(builder->getUIntType(), 0); - IRInst* zeroVectorArgs[] = {zero, zero}; - IRInst* rttiObject = builder->emitMakeVector(uint2Type, 2, zeroVectorArgs); + IRInst* standinRTTIVectorArgs[] = {standinVal, zero}; + IRInst* rttiObject = builder->emitMakeVector(uint2Type, 2, standinRTTIVectorArgs); // Pack the user provided value into `AnyValue`. IRInst* packedValue = inst->getValue(); diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index be21a66ba..8677973f5 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -23,11 +23,11 @@ namespace Slang { // Replace all uses of RTTI objects with its sequential ID. -// Currently we don't use RTTI objects at all, so all of them -// are 0. +// Currently we don't use RTTI objects other than check for null/invalid value, +// so all of them are 0xFFFFFFFF. void specializeRTTIObjectReferences(SharedGenericsLoweringContext* sharedContext) { - uint32_t id = 0; + uint32_t id = 0xFFFFFFFF; for (auto rtti : sharedContext->mapTypeToRTTIObject) { IRBuilder builder(sharedContext->module); diff --git a/tests/language-feature/types/optional-ifoo.slang b/tests/language-feature/types/optional-ifoo.slang new file mode 100644 index 000000000..d2c9b6a50 --- /dev/null +++ b/tests/language-feature/types/optional-ifoo.slang @@ -0,0 +1,26 @@ +//TEST:INTERPRET(filecheck=CHECK): + +interface IFoo +{ + int get_result(); +} + +struct FooImpl : IFoo +{ + int get_result() { return data; } + int data; +} + +Optional generate_foo(int i) +{ + FooImpl result = {}; + result.data = i; + return { result }; +} + +void main() +{ + // CHECK: hasValue: 1 + let result_foo = generate_foo(100); + printf("hasValue: %d\n", (int)result_foo.hasValue); +} -- cgit v1.2.3