diff options
| -rw-r--r-- | source/core/slang-hash.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.cpp | 43 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.h | 37 | ||||
| -rw-r--r-- | source/slang/slang-check-conformance.cpp | 12 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 16 | ||||
| -rw-r--r-- | source/slang/slang-lookup.cpp | 8 | ||||
| -rw-r--r-- | tests/bugs/gh-1990.slang | 63 | ||||
| -rw-r--r-- | tests/bugs/gh-1990.slang.expected.txt | 2 |
8 files changed, 162 insertions, 27 deletions
diff --git a/source/core/slang-hash.h b/source/core/slang-hash.h index b2a583744..248d11303 100644 --- a/source/core/slang-hash.h +++ b/source/core/slang-hash.h @@ -169,6 +169,14 @@ namespace Slang return (left * 16777619) ^ right; } + inline HashCode combineHash(HashCode hash0, HashCode hash1, HashCode hash2) + { + auto h = hash0; + h = combineHash(h, hash1); + h = combineHash(h, hash2); + return h; + } + struct Hasher { public: diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp index 64b8c7515..45a736f23 100644 --- a/source/slang/slang-ast-type.cpp +++ b/source/slang/slang-ast-type.cpp @@ -655,7 +655,7 @@ bool ExtractExistentialType::_equalsImplOverride(Type* type) HashCode ExtractExistentialType::_getHashCodeOverride() { - return combineHash(declRef.getHashCode(), interfaceDeclRef.getHashCode()); + return combineHash(declRef.getHashCode(), originalInterfaceType->getHashCode(), originalInterfaceDeclRef.getHashCode()); } Type* ExtractExistentialType::_createCanonicalTypeOverride() @@ -667,7 +667,8 @@ Val* ExtractExistentialType::_substituteImplOverride(ASTBuilder* astBuilder, Sub { int diff = 0; auto substDeclRef = declRef.substituteImpl(astBuilder, subst, &diff); - auto interfaceSubstDeclRef = interfaceDeclRef.substituteImpl(astBuilder, subst, &diff); + auto substOriginalInterfaceType = originalInterfaceType->substituteImpl(astBuilder, subst, &diff); + auto substOriginalInterfaceDeclRef = originalInterfaceDeclRef.substituteImpl(astBuilder, subst, &diff); if (!diff) return this; @@ -675,10 +676,46 @@ Val* ExtractExistentialType::_substituteImplOverride(ASTBuilder* astBuilder, Sub ExtractExistentialType* substValue = astBuilder->create<ExtractExistentialType>(); substValue->declRef = substDeclRef; - substValue->interfaceDeclRef = interfaceSubstDeclRef; + substValue->originalInterfaceType = as<Type>(substOriginalInterfaceType); + substValue->originalInterfaceDeclRef = substOriginalInterfaceDeclRef; return substValue; } +SubtypeWitness* ExtractExistentialType::getSubtypeWitness() +{ + if (auto cachedValue = this->cachedSubtypeWitness) + return cachedValue; + + ExtractExistentialSubtypeWitness* openedWitness = m_astBuilder->create<ExtractExistentialSubtypeWitness>(); + openedWitness->sub = this; + openedWitness->sup = originalInterfaceType; + openedWitness->declRef = this->declRef; + + this->cachedSubtypeWitness = openedWitness; + return openedWitness; +} + +DeclRef<InterfaceDecl> ExtractExistentialType::getSpecializedInterfaceDeclRef() +{ + if (auto cachedValue = this->cachedSpecializedInterfaceDeclRef) + return cachedValue; + + auto interfaceDecl = originalInterfaceDeclRef.getDecl(); + + SubtypeWitness* openedWitness = getSubtypeWitness(); + + ThisTypeSubstitution* openedThisType = m_astBuilder->create<ThisTypeSubstitution>(); + openedThisType->outer = originalInterfaceDeclRef.substitutions.substitutions; + openedThisType->interfaceDecl = interfaceDecl; + openedThisType->witness = openedWitness; + + DeclRef<InterfaceDecl> specialiedInterfaceDeclRef = DeclRef<InterfaceDecl>(interfaceDecl, openedThisType); + + this->cachedSpecializedInterfaceDeclRef = specialiedInterfaceDeclRef; + return specialiedInterfaceDeclRef; +} + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TaggedUnionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! void TaggedUnionType::_toTextOverride(StringBuilder& out) diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index f4aca515c..5b44a5087 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -616,7 +616,29 @@ class ExtractExistentialType : public Type SLANG_AST_CLASS(ExtractExistentialType) DeclRef<VarDeclBase> declRef; - DeclRef<InterfaceDecl> interfaceDeclRef; + + // A reference to the original interface this type is known + // to be a subtype of. + // + Type* originalInterfaceType; + DeclRef<InterfaceDecl> originalInterfaceDeclRef; + +// Following fields will not be reflected (and thus won't be serialized, etc.) +SLANG_UNREFLECTED + + // A cached decl-ref to the original interface above, with + // a this-type substitution that refers to the type extracted here. + // + // This field is optional and can be filled in on-demand. It does *not* + // represent part of the logical value of this `Type`, and should not + // be serialized, included in hashes, etc. + // + DeclRef<InterfaceDecl> cachedSpecializedInterfaceDeclRef; + + // A cached pointer to a witness that shows how this type is a subtype + // of `originalInterfaceType`. + // + SubtypeWitness* cachedSubtypeWitness = nullptr; // Overrides should be public so base classes can access void _toTextOverride(StringBuilder& out); @@ -624,6 +646,19 @@ class ExtractExistentialType : public Type HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); + + /// Get a witness that shows how this type is a subtype of `originalInterfaceType`. + /// + /// This operation may create the witness on demand and cache it. + /// + SubtypeWitness* getSubtypeWitness(); + + /// Get a interface decl-ref for the original interface specialized to this type + /// (using a type-type substitution). + /// + /// This operation may create the decl-ref on demand and cache it. + /// + DeclRef<InterfaceDecl> getSpecializedInterfaceDeclRef(); }; /// A tagged union of zero or more other types. diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp index b95df520d..e77ea4981 100644 --- a/source/slang/slang-check-conformance.cpp +++ b/source/slang/slang-check-conformance.cpp @@ -301,19 +301,13 @@ namespace Slang // is a subtype of I. // We need to check and make sure the interface type of the `ExtractExistentialType` // is equal to `superType`. - auto interfaceDeclRef = extractExistentialType->interfaceDeclRef; - auto thisTypeSubst = findThisTypeSubstitution(interfaceDeclRef.substitutions.substitutions, interfaceDeclRef.getDecl()); - SLANG_ASSERT(thisTypeSubst && thisTypeSubst == interfaceDeclRef.substitutions.substitutions); - // The interfaceDeclRef in `extractExistentialType` contains a `ThisTypeSubstitution` - // to allow member lookup to return correct substituted types. Here we just need - // to know if that interface is the same as the superType, so we need to exclude - // the `ThisTypeSubstitution` from comparison. - interfaceDeclRef.substitutions.substitutions = thisTypeSubst->outer; + // + auto interfaceDeclRef = extractExistentialType->originalInterfaceDeclRef; if (interfaceDeclRef.equals(superTypeDeclRef)) { if (outWitness) { - *outWitness = thisTypeSubst->witness; + *outWitness = extractExistentialType->getSubtypeWitness(); } return true; } diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index fbbfcd473..6b1ab13cc 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -101,24 +101,14 @@ namespace Slang // immutable temporary so that we can use // it directly. // - auto interfaceDecl = interfaceDeclRef.getDecl(); return maybeMoveTemp(expr, [&](DeclRef<VarDeclBase> varDeclRef) { ExtractExistentialType* openedType = m_astBuilder->create<ExtractExistentialType>(); openedType->declRef = varDeclRef; + openedType->originalInterfaceType = expr->type.type; + openedType->originalInterfaceDeclRef = interfaceDeclRef; - ExtractExistentialSubtypeWitness* openedWitness = m_astBuilder->create<ExtractExistentialSubtypeWitness>(); - openedWitness->sub = openedType; - openedWitness->sup = expr->type.type; - openedWitness->declRef = varDeclRef; - - ThisTypeSubstitution* openedThisType = m_astBuilder->create<ThisTypeSubstitution>(); - openedThisType->outer = interfaceDeclRef.substitutions.substitutions; - openedThisType->interfaceDecl = interfaceDecl; - openedThisType->witness = openedWitness; - - DeclRef<InterfaceDecl> substDeclRef = DeclRef<InterfaceDecl>(interfaceDecl, openedThisType); - openedType->interfaceDeclRef = substDeclRef; + DeclRef<InterfaceDecl> substDeclRef = openedType->getSpecializedInterfaceDeclRef(); ExtractExistentialValueExpr* openedValue = m_astBuilder->create<ExtractExistentialValueExpr>(); openedValue->declRef = varDeclRef; diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index 20670e7b3..e3093a3db 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -613,7 +613,13 @@ static void _lookUpMembersInSuperTypeImpl( } else if (auto extractExistentialType = as<ExtractExistentialType>(superType)) { - _lookUpMembersInSuperTypeDeclImpl(astBuilder, name, leafType, superType, leafIsSuperWitness, extractExistentialType->interfaceDeclRef, request, ioResult, inBreadcrumbs); + // We want lookup to be performed on the underlying interface type of the existential, + // but we need to have a this-type substitution applied to ensure that the result of + // lookup will have a comparable substitution applied (allowing things like associated + // types, etc. used in the signature of a method to resolve correctly). + // + auto interfaceDeclRef = extractExistentialType->getSpecializedInterfaceDeclRef(); + _lookUpMembersInSuperTypeDeclImpl(astBuilder, name, leafType, superType, leafIsSuperWitness, interfaceDeclRef, request, ioResult, inBreadcrumbs); } else if( auto thisType = as<ThisType>(superType) ) { diff --git a/tests/bugs/gh-1990.slang b/tests/bugs/gh-1990.slang new file mode 100644 index 000000000..758caf1a9 --- /dev/null +++ b/tests/bugs/gh-1990.slang @@ -0,0 +1,63 @@ +// Stack overflow bug in type checking when using auto type deduction in a generic method. + +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx11 -profile sm_5_0 -output-using-type +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -output-using-type + +[anyValueSize(12)] +interface IBSDF +{ + uint getLobes(); +} + +[anyValueSize(16)] +interface IMaterial +{ + associatedtype BSDF : IBSDF; + BSDF setupBSDF(); +} + +struct StandardBSDF : IBSDF +{ + uint getLobes() + { + return 0; + } +} + +struct StandardMaterial : IMaterial +{ + typedef StandardBSDF BSDF; + StandardBSDF setupBSDF() { StandardBSDF g; return g; } +}; + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=gOutputBuffer +RWStructuredBuffer<float> gOutputBuffer; + +//TEST_INPUT: type_conformance StandardMaterial:IMaterial = 0 + +interface IInterface +{ + void randomStuff(); +}; +struct Impl : IInterface +{ + void randomStuff(){} +}; + +void test<R : IInterface>(R r, int id) +{ + float result = 0.0; + // Use of auto type deduction from a generic method leads to stack overflow during + // type checking for `bsdf.getLobes`. + let bsdf = createDynamicObject<IMaterial, int>(id, 0).setupBSDF(); + result = bsdf.getLobes(); + gOutputBuffer[0] = float(result); +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + Impl impl; + test(impl, dispatchThreadID.x); +} diff --git a/tests/bugs/gh-1990.slang.expected.txt b/tests/bugs/gh-1990.slang.expected.txt new file mode 100644 index 000000000..4b1f4c0d9 --- /dev/null +++ b/tests/bugs/gh-1990.slang.expected.txt @@ -0,0 +1,2 @@ +type: float +0.000000 |
