diff options
| -rw-r--r-- | source/slang/check.cpp | 97 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 8 | ||||
| -rw-r--r-- | source/slang/ir-insts.h | 10 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 285 | ||||
| -rw-r--r-- | source/slang/ir.h | 10 | ||||
| -rw-r--r-- | source/slang/lookup.cpp | 5 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 66 | ||||
| -rw-r--r-- | source/slang/mangle.cpp | 16 | ||||
| -rw-r--r-- | source/slang/mangle.h | 3 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 32 | ||||
| -rw-r--r-- | source/slang/slang.natvis | 42 | ||||
| -rw-r--r-- | source/slang/syntax.cpp | 27 | ||||
| -rw-r--r-- | tests/compute/nested-generics.slang | 40 | ||||
| -rw-r--r-- | tests/compute/nested-generics.slang.expected.txt | 4 | ||||
| -rw-r--r-- | tests/compute/nested-generics2.slang | 43 | ||||
| -rw-r--r-- | tests/compute/nested-generics2.slang.expected.txt | 4 | ||||
| -rw-r--r-- | tests/compute/struct-in-generic.slang | 36 | ||||
| -rw-r--r-- | tests/compute/struct-in-generic.slang.expected.txt | 4 | ||||
| -rw-r--r-- | tools/render-test/test.txt | 4 |
19 files changed, 515 insertions, 221 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index e7bd6d711..6c484b493 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -120,6 +120,15 @@ namespace Slang auto typeRepr = TranslateTypeNodeImpl(node); return ExtractTypeFromTypeRepr(typeRepr); } + TypeExp TranslateTypeNodeForced(TypeExp const& typeExp) + { + auto typeRepr = TranslateTypeNodeImpl(typeExp.exp); + + TypeExp result; + result.exp = typeRepr; + result.type = ExtractTypeFromTypeRepr(typeRepr); + return result; + } TypeExp TranslateTypeNode(TypeExp const& typeExp) { // HACK(tfoley): It seems that in some cases we end up re-checking @@ -130,14 +139,7 @@ namespace Slang { return typeExp; } - - - auto typeRepr = TranslateTypeNodeImpl(typeExp.exp); - - TypeExp result; - result.exp = typeRepr; - result.type = ExtractTypeFromTypeRepr(typeRepr); - return result; + return TranslateTypeNodeForced(typeExp); } RefPtr<DeclRefType> getExprDeclRefType(Expr * expr) @@ -153,8 +155,6 @@ namespace Slang RefPtr<Expr> baseExpr, SourceLoc loc) { - if (declRef.As<AssocTypeDecl>()) - getNewThisTypeSubst(declRef); if (baseExpr) { RefPtr<Expr> expr; @@ -1287,6 +1287,20 @@ namespace Slang varDecl->initExpr = initExpr; } + // Fill in default substitutions for the 'subtype' part of a type constraint decl + void CheckConstraintSubType(TypeExp & typeExp) + { + if (auto sharedTypeExpr = typeExp.exp.As<SharedTypeExpr>()) + { + if (auto declRefType = sharedTypeExpr->base->AsDeclRefType()) + { + declRefType->declRef.substitutions = createDefaultSubstitutions(getSession(), declRefType->declRef.getDecl()); + if (auto typetype = typeExp.exp->type.type.As<TypeType>()) + typetype->type = declRefType; + } + } + } + void CheckGenericConstraintDecl(GenericTypeConstraintDecl* decl) { // TODO: are there any other validations we can do at this point? @@ -1294,9 +1308,13 @@ namespace Slang // There probably needs to be a kind of "occurs check" to make // sure that the constraint actually applies to at least one // of the parameters of the generic. - - decl->sub = TranslateTypeNode(decl->sub); - decl->sup = TranslateTypeNode(decl->sup); + if (decl->checkState == DeclCheckState::Unchecked) + { + decl->checkState = DeclCheckState::Checked; + CheckConstraintSubType(decl->sub); + decl->sub = TranslateTypeNodeForced(decl->sub); + decl->sup = TranslateTypeNodeForced(decl->sup); + } } void checkDecl(Decl* decl) @@ -1343,6 +1361,7 @@ namespace Slang { // check the type being inherited from auto base = inheritanceDecl->base; + CheckConstraintSubType(base); base = TranslateTypeNode(base); inheritanceDecl->base = base; @@ -1677,7 +1696,7 @@ namespace Slang // An associated type requirement should be allowed // to be satisfied by any type declaration: // a typedef, a `struct`, etc. - auto checkSubTypeMember = [&](DeclRef<AggTypeDecl> subStructTypeDeclRef) -> bool + auto checkSubTypeMember = [&](DeclRef<ContainerDecl> subStructTypeDeclRef) -> bool { EnsureDecl(subStructTypeDeclRef.getDecl()); // this is a sub type (e.g. nested struct declaration) in an aggregate type @@ -1685,10 +1704,10 @@ namespace Slang if (auto requiredTypeDeclRef = requiredMemberDeclRef.As<AssocTypeDecl>()) { bool conformance = true; - auto inheritanceReqDeclRefs = getMembersOfType<InheritanceDecl>(requiredTypeDeclRef); + auto inheritanceReqDeclRefs = getMembersOfType<TypeConstraintDecl>(requiredTypeDeclRef); for (auto inheritanceReqDeclRef : inheritanceReqDeclRefs) { - auto interfaceDeclRefType = inheritanceReqDeclRef.getDecl()->base.type.As<DeclRefType>(); + auto interfaceDeclRefType = inheritanceReqDeclRef.getDecl()->getSup().type.As<DeclRefType>(); SLANG_ASSERT(interfaceDeclRefType); auto interfaceDeclRef = interfaceDeclRefType->declRef.As<InterfaceDecl>(); SLANG_ASSERT(interfaceDeclRef); @@ -1744,20 +1763,22 @@ namespace Slang // check if the specified type satisfies the constraints defined by the associated type if (auto requiredTypeDeclRef = requiredMemberDeclRef.As<AssocTypeDecl>()) { - auto constraintList = getMembersOfType<InheritanceDecl>(requiredTypeDeclRef); - if (constraintList.Count()) + auto declRefType = GetType(typedefDeclRef)->GetCanonicalType()->As<DeclRefType>(); + if (!declRefType) + return false; + + if (auto genTypeParamDeclRef = declRefType->declRef.As<GenericTypeParamDecl>()) { - auto declRefType = GetType(typedefDeclRef)->GetCanonicalType()->As<DeclRefType>(); - if (!declRefType) - return false; + // TODO: check generic type parameter satisfies constraints + return true; + } + - auto structTypeDeclRef = declRefType->declRef.As<AggTypeDecl>(); - if (!structTypeDeclRef) - return false; + auto containerDeclRef = declRefType->declRef.As<ContainerDecl>(); + if (!containerDeclRef) + return false; - return checkSubTypeMember(structTypeDeclRef); - } - return true; + return checkSubTypeMember(containerDeclRef); } } // Default: just assume that thing aren't being satisfied. @@ -2496,6 +2517,7 @@ namespace Slang // TODO: This needs to bottleneck through the common variable checks para->type = CheckUsableType(para->type); + if (para->type.Equals(getSession()->getVoidType())) { if (!isRewriteMode()) @@ -6154,7 +6176,6 @@ namespace Slang return expr; expr->type = QualType(getSession()->getErrorType()); - auto lookupResult = lookUp( getSession(), this, expr->name, expr->scope); @@ -6970,29 +6991,34 @@ namespace Slang Decl* decl, SubstitutionSet parentSubst) { + SubstitutionSet resultSubst = parentSubst; + if (auto interfaceDecl = dynamic_cast<InterfaceDecl*>(decl)) + { + resultSubst.thisTypeSubstitution = new ThisTypeSubstitution(); + } auto dd = decl->ParentDecl; if( auto genericDecl = dynamic_cast<GenericDecl*>(dd) ) { // We don't want to specialize references to anything // other than the "inner" declaration itself. if(decl != genericDecl->inner) - return parentSubst; + return resultSubst; - SubstitutionSet resultSubst = parentSubst; RefPtr<GenericSubstitution> subst = new GenericSubstitution(); subst->genericDecl = genericDecl; subst->outer = parentSubst.genericSubstitutions; resultSubst.genericSubstitutions = subst; - + SubstitutionSet outerSubst = resultSubst; + outerSubst.genericSubstitutions = outerSubst.genericSubstitutions?outerSubst.genericSubstitutions->outer:nullptr; for( auto mm : genericDecl->Members ) { if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() ) { - subst->args.Add(DeclRefType::Create(session, makeDeclRef(genericTypeParamDecl.Ptr()))); + subst->args.Add(DeclRefType::Create(session, DeclRef<Decl>(genericTypeParamDecl.Ptr(), outerSubst))); } else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() ) { - subst->args.Add(new GenericParamIntVal(makeDeclRef(genericValueParamDecl.Ptr()))); + subst->args.Add(new GenericParamIntVal(DeclRef<GenericValueParamDecl>(genericValueParamDecl.Ptr(), outerSubst))); } } @@ -7002,15 +7028,14 @@ namespace Slang if (auto genericTypeConstraintDecl = mm.As<GenericTypeConstraintDecl>()) { RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness(); - witness->declRef = makeDeclRef(genericTypeConstraintDecl.Ptr()); + witness->declRef = DeclRef<Decl>(genericTypeConstraintDecl.Ptr(), outerSubst); witness->sub = genericTypeConstraintDecl->sub.type; witness->sup = genericTypeConstraintDecl->sup.type; subst->args.Add(witness); } } - return resultSubst; } - return parentSubst; + return resultSubst; } SubstitutionSet createDefaultSubstitutions( diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 41ae819d9..33328fbb1 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -4947,7 +4947,7 @@ emitDeclImpl(decl, nullptr); { auto type = inst->getType(); - if(type->As<UniformParameterGroupType>()) + if(type->As<UniformParameterGroupType>() && !type->As<ParameterBlockType>()) { // TODO: we need to be careful here, because // HLSL shader model 6 allows these as explicit @@ -6341,7 +6341,7 @@ emitDeclImpl(decl, nullptr); { // We don't want to declare generic functions, // because none of our targets actually support them. - if(func->genericDecl) + if(func->getGenericDecl()) return; // We also don't want to emit declarations for operations @@ -6453,7 +6453,7 @@ emitDeclImpl(decl, nullptr); EmitContext* ctx, IRFunc* func) { - if(func->genericDecl) + if(func->getGenericDecl()) { Emit("/* "); emitIRFuncDecl(ctx, func); @@ -7110,7 +7110,7 @@ emitDeclImpl(decl, nullptr); // Don't emit anything for a generic function, // since we only care about the types used by // the actual specializations. - if (irFunc->genericDecl) + if (irFunc->getGenericDecl()) return; emitIRUsedType(ctx, irFunc->getResultType()); diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index 55ff26185..23e948b3a 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -315,16 +315,6 @@ struct IRWitnessTable : IRGlobalValue IRValueList<IRWitnessTableEntry> entries; }; -// An abstract witness table is a global value that -// represents an inheritance relationship that can't -// be resolved to a witness table at IR-generation time. -struct IRAbstractWitness : IRGlobalValue -{ - RefPtr<SubtypeWitness> witness; - DeclRef<Decl> subTypeDeclRef, supTypeDeclRef; -}; - - // Description of an instruction to be used for global value numbering struct IRInstKey { diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 626de3d7a..46eff33e9 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -2104,10 +2104,10 @@ namespace Slang dump(context, "ir_func "); dumpID(context, func); - if (func->genericDecl) + if (func->getGenericDecl()) { dump(context, " "); - dumpGenericSignature(context, func->genericDecl); + dumpGenericSignature(context, func->getGenericDecl()); } dumpInstTypeClause(context, func->getType()); @@ -3084,6 +3084,9 @@ namespace Slang SharedIRBuilder sharedBuilderStorage; IRBuilder builderStorage; + + // Non-generic functions to be processed (for generic specialization context) + List<IRFunc*> workList; }; struct IRSpecContextBase @@ -3297,23 +3300,28 @@ namespace Slang { int diff = 0; newDeclRef = od->declRef.SubstituteImpl(subst, &diff); - if (newDeclRef.getDecl() == subst.globalGenParamSubstitutions->paramDecl) - return builder->getTypeVal(subst.globalGenParamSubstitutions->actualType.As<Type>()); - else if (auto genConstraint = newDeclRef.As<GenericTypeConstraintDecl>()) + for (auto globalGenSubst = subst.globalGenParamSubstitutions; globalGenSubst; globalGenSubst = globalGenSubst->outer) { - // a decl-ref to GenericTypeConstraintDecl as a result of - // referencing a generic parameter type should be replaced with - // the actual witness table - if (genConstraint.getDecl()->ParentDecl == subst.globalGenParamSubstitutions->paramDecl) + if (!globalGenSubst) + continue; + if (newDeclRef.getDecl() == globalGenSubst->paramDecl) + return builder->getTypeVal(globalGenSubst->actualType.As<Type>()); + else if (auto genConstraint = newDeclRef.As<GenericTypeConstraintDecl>()) { - // find the witness table from subst - for (auto witness : subst.globalGenParamSubstitutions->witnessTables) + // a decl-ref to GenericTypeConstraintDecl as a result of + // referencing a generic parameter type should be replaced with + // the actual witness table + if (genConstraint.getDecl()->ParentDecl == globalGenSubst->paramDecl) { - if (witness.Key->EqualsVal(GetSup(genConstraint))) + // find the witness table from subst + for (auto witness : globalGenSubst->witnessTables) { - auto proxyVal = witness.Value.As<IRProxyVal>(); - SLANG_ASSERT(proxyVal); - return proxyVal->inst.usedValue; + if (witness.Key->EqualsVal(GetSup(genConstraint))) + { + auto proxyVal = witness.Value.As<IRProxyVal>(); + SLANG_ASSERT(proxyVal); + return proxyVal->inst.usedValue; + } } } } @@ -3515,9 +3523,10 @@ namespace Slang IRWitnessTable* cloneWitnessTableImpl( IRSpecContextBase* context, IRWitnessTable* originalTable, - IROriginalValuesForClone const& originalValues) + IROriginalValuesForClone const& originalValues, + IRWitnessTable* dstTable = nullptr) { - auto clonedTable = context->builder->createWitnessTable(); + auto clonedTable = dstTable ? dstTable : context->builder->createWitnessTable(); registerClonedValue(context, clonedTable, originalValues); auto mangledName = originalTable->mangledName; @@ -3543,9 +3552,10 @@ namespace Slang IRWitnessTable* cloneWitnessTableWithoutRegistering( IRSpecContextBase* context, - IRWitnessTable* originalTable) + IRWitnessTable* originalTable, + IRWitnessTable* dstTable = nullptr) { - return cloneWitnessTableImpl(context, originalTable, IROriginalValuesForClone()); + return cloneWitnessTableImpl(context, originalTable, IROriginalValuesForClone(), dstTable); } void cloneGlobalValueWithCodeCommon( @@ -3614,7 +3624,8 @@ namespace Slang { // First clone all the simple properties. clonedFunc->mangledName = originalFunc->mangledName; - clonedFunc->genericDecl = originalFunc->genericDecl; + clonedFunc->genericDecls = originalFunc->genericDecls; + clonedFunc->specializedGenericLevel = originalFunc->specializedGenericLevel; clonedFunc->type = context->maybeCloneType(originalFunc->type); cloneDecorations(context, clonedFunc, originalFunc); @@ -3678,13 +3689,13 @@ namespace Slang // and their instructions. cloneFunctionCommon(context, clonedFunc, originalFunc); - //// for now, clone all unreferenced witness tables - //for (auto gv = context->getOriginalModule()->getFirstGlobalValue(); - // gv; gv = gv->getNextValue()) - //{ - // if (gv->op == kIROp_witness_table) - // cloneGlobalValue(context, (IRWitnessTable*)gv); - //} + // for now, clone all unreferenced witness tables + /*for (auto gv = context->getOriginalModule()->getFirstGlobalValue(); + gv; gv = gv->getNextValue()) + { + if (gv->op == kIROp_witness_table) + cloneGlobalValue(context, (IRWitnessTable*)gv); + }*/ // We need to attach the layout information for // the entry point to this declaration, so that @@ -4055,54 +4066,10 @@ namespace Slang SubstitutionSet typeSubst); RefPtr<GlobalGenericParamSubstitution> createGlobalGenericParamSubstitution( - EntryPointRequest * entryPointRequest, + EntryPointRequest * entryPointRequest, ProgramLayout * programLayout, IRSpecContext* context, - IRModule* originalIRModule) - { - RefPtr<GlobalGenericParamSubstitution> globalParamSubst; - GlobalGenericParamSubstitution * curTailSubst = nullptr; - for (auto param : programLayout->globalGenericParams) - { - auto paramSubst = new GlobalGenericParamSubstitution(); - if (!globalParamSubst) - globalParamSubst = paramSubst; - if (curTailSubst) - curTailSubst->outer = paramSubst; - curTailSubst = paramSubst; - paramSubst->paramDecl = param->decl; - SLANG_ASSERT((UInt)param->index < entryPointRequest->genericParameterTypes.Count()); - paramSubst->actualType = entryPointRequest->genericParameterTypes[param->index]; - // find witness tables - for (auto witness : entryPointRequest->genericParameterWitnesses) - { - if (auto subtypeWitness = witness.As<SubtypeWitness>()) - { - if (subtypeWitness->sub->EqualsVal(paramSubst->actualType)) - { - auto witnessTableName = getMangledNameForConformanceWitness(subtypeWitness->sub, subtypeWitness->sup); - auto globalVar = originalIRModule->getFirstGlobalValue(); - IRGlobalValue * table = nullptr; - while (globalVar) - { - if (globalVar->mangledName == witnessTableName) - { - table = globalVar; - break; - } - globalVar = globalVar->getNextValue(); - } - SLANG_ASSERT(table); - table = cloneGlobalValue(context, (IRWitnessTable*)(table)); - IRProxyVal * tableVal = new IRProxyVal(); - tableVal->inst.init(nullptr, table); - paramSubst->witnessTables.Add(KeyValuePair<RefPtr<Type>, RefPtr<Val>>(subtypeWitness->sup, tableVal)); - } - } - } - } - return globalParamSubst; - } + IRModule* originalIRModule); struct IRSpecializationState { @@ -4266,19 +4233,11 @@ namespace Slang break; } } - - // - - struct IRSharedGenericSpecContext : IRSharedSpecContext - { - // Non-generic functions to be processed - List<IRFunc*> workList; - }; - + struct IRGenericSpecContext : IRSpecContextBase { - IRSharedGenericSpecContext* getShared() { return (IRSharedGenericSpecContext*) shared; } - + IRSharedSpecContext* getShared() { return shared; } + // Override the "maybe clone" logic so that we always clone virtual IRValue* maybeCloneValue(IRValue* originalVal) override; @@ -4298,13 +4257,12 @@ namespace Slang subtypeWitness->sup); RefPtr<IRSpecSymbol> symbol; - if( !context->getSymbols().TryGetValue(mangledName, symbol) ) + if (context->getSymbols().TryGetValue(mangledName, symbol)) { - SLANG_UNEXPECTED("couldn't find symbol for conformance!"); - return nullptr; + return symbol->irGlobalValue; } - - return symbol->irGlobalValue; + else + return nullptr; } else if (auto proxyVal = dynamic_cast<IRProxyVal*>(val)) { @@ -4385,7 +4343,8 @@ namespace Slang // the `Val` we have been given. if(declRef.getDecl()->ParentDecl == genSubst->genericDecl) { - return getSubstValue(this, declRef); + if (auto substVal = getSubstValue(this, declRef)) + return substVal; } int diff = 0; @@ -4442,7 +4401,7 @@ namespace Slang // overloads in the mix, and produces a new set of // substitutiosn without this issue. RefPtr<GenericSubstitution> cloneSubstitutionsForSpecialization( - IRSharedGenericSpecContext* sharedContext, + IRSharedSpecContext* sharedContext, RefPtr<GenericSubstitution> oldSubst, Decl* newDecl) { @@ -4452,15 +4411,26 @@ namespace Slang if(!oldGenericSubst) return nullptr; + auto innerGenericName = oldGenericSubst->genericDecl->inner->getName(); + // We will also peel back layers of declarations until // we find our first generic decl. - auto newGenericDecl = getInnermostGenericDecl(newDecl); - if( !newGenericDecl ) + GenericDecl* newGenericDecl = nullptr; + + for (Decl* d = newDecl; d; d = d->ParentDecl) { -// SLANG_UNEXPECTED("generic subst without generic decl"); - return nullptr; + if (auto gd = dynamic_cast<GenericDecl*>(d)) + { + if (gd->inner->getName() == innerGenericName) + { + newGenericDecl = gd; + break; + } + } } + SLANG_ASSERT(newGenericDecl); + RefPtr<GenericSubstitution> newSubst = new GenericSubstitution(); newSubst->genericDecl = newGenericDecl; newSubst->args = oldGenericSubst->args; @@ -4474,11 +4444,11 @@ namespace Slang } IRFunc* getSpecializedFunc( - IRSharedGenericSpecContext* sharedContext, + IRSharedSpecContext* sharedContext, IRFunc* genericFunc, DeclRef<Decl> specDeclRef); - IRWitnessTable* specializeWitnessTable(IRSharedGenericSpecContext * sharedContext, IRWitnessTable* originalTable, DeclRef<Decl> specDeclRef) + IRWitnessTable* specializeWitnessTable(IRSharedSpecContext * sharedContext, IRWitnessTable* originalTable, DeclRef<Decl> specDeclRef, IRWitnessTable* dstTable) { // First, we want to see if an existing specialization // has already been made. To do that we will need to @@ -4488,6 +4458,9 @@ namespace Slang String specializedMangledName = getMangledNameForConformanceWitness(specDeclRef.Substitute(originalTable->subTypeDeclRef), specDeclRef.Substitute(originalTable->supTypeDeclRef)); + if (dstTable && dstTable->mangledName.Length()) + specializedMangledName = dstTable->mangledName; + // TODO: This is a terrible linear search, and we should // avoid it by building a dictionary ahead of time, // as is being done for the `IRSpecContext` used above. @@ -4512,7 +4485,7 @@ namespace Slang // TODO: other initialization is needed here... - auto specTable = cloneWitnessTableWithoutRegistering(&context, originalTable); + auto specTable = cloneWitnessTableWithoutRegistering(&context, originalTable, dstTable); // Set up the clone to recognize that it is no longer generic specTable->mangledName = specMangledName; @@ -4525,7 +4498,7 @@ namespace Slang if (entry->satisfyingVal.usedValue->op == kIROp_Func) { IRFunc* func = (IRFunc*)entry->satisfyingVal.usedValue; - if (func->genericDecl) + if (func->getGenericDecl()) entry->satisfyingVal.set(getSpecializedFunc(sharedContext, func, specDeclRef)); } @@ -4537,9 +4510,9 @@ namespace Slang return specTable; } - + IRFunc* getSpecializedFunc( - IRSharedGenericSpecContext* sharedContext, + IRSharedSpecContext* sharedContext, IRFunc* genericFunc, DeclRef<Decl> specDeclRef) { @@ -4548,7 +4521,7 @@ namespace Slang // compute the mangled name of the specialized function, // so that we can look for existing declarations. String specMangledName; - if (genericFunc->genericDecl == specDeclRef.decl) + if (genericFunc->getGenericDecl() == specDeclRef.decl) specMangledName = getMangledName(specDeclRef); else specMangledName = mangleSpecializedFuncName(genericFunc->mangledName, specDeclRef.substitutions); @@ -4574,7 +4547,10 @@ namespace Slang RefPtr<GenericSubstitution> newSubst = cloneSubstitutionsForSpecialization( sharedContext, specDeclRef.substitutions.genericSubstitutions, - genericFunc->genericDecl); + genericFunc->getGenericDecl()); + + if (!newSubst) + return genericFunc; IRGenericSpecContext context; context.shared = sharedContext; @@ -4586,9 +4562,11 @@ namespace Slang auto specFunc = cloneSimpleFuncWithoutRegistering(&context, genericFunc); - // Set up the clone to recognize that it is no longer generic specFunc->mangledName = specMangledName; - specFunc->genericDecl = nullptr; + + // reduce specialized generic level by 1 + if (specFunc->specializedGenericLevel >= 0) + specFunc->specializedGenericLevel--; // Put the function into the global sequence right after // the function it specializes. @@ -4601,7 +4579,8 @@ namespace Slang // At this point we've created a new non-generic function, // which means we should add it to our work list for // subsequent processing. - sharedContext->workList.Add(specFunc); + if (specFunc->specializedGenericLevel == -1) + sharedContext->workList.Add(specFunc); // We also need to make sure that we register this specialized // function under its mangled name, so that later lookup @@ -4661,7 +4640,7 @@ namespace Slang void specializeGenerics( IRModule* module) { - IRSharedGenericSpecContext sharedContextStorage; + IRSharedSpecContext sharedContextStorage; auto sharedContext = &sharedContextStorage; initializeSharedSpecContext( @@ -4688,7 +4667,7 @@ namespace Slang auto func = (IRFunc*) gv; // Is it generic? If so, skip. - if(func->genericDecl) + if(func->getGenericDecl()) continue; sharedContext->workList.Add(func); @@ -4760,7 +4739,7 @@ namespace Slang if (genericVal->op == kIROp_Func) { auto genericFunc = (IRFunc*)genericVal; - if (!genericFunc->genericDecl) + if (!genericFunc->getGenericDecl()) continue; // Okay, we have a candidate for specialization here. @@ -4780,7 +4759,7 @@ namespace Slang { // specialize a witness table auto originalTable = (IRWitnessTable*)genericVal; - auto specWitnessTable = specializeWitnessTable(sharedContext, originalTable, specDeclRef); + auto specWitnessTable = specializeWitnessTable(sharedContext, originalTable, specDeclRef, nullptr); witnessTables.AddIfNotExists(specWitnessTable->mangledName, specWitnessTable); specInst->replaceUsesWith(specWitnessTable); specInst->removeAndDeallocate(); @@ -4856,6 +4835,90 @@ namespace Slang // functions that in turn reference a generic function. } - // + RefPtr<GlobalGenericParamSubstitution> createGlobalGenericParamSubstitution( + EntryPointRequest * entryPointRequest, + ProgramLayout * programLayout, + IRSpecContext* context, + IRModule* originalIRModule) + { + RefPtr<GlobalGenericParamSubstitution> globalParamSubst; + GlobalGenericParamSubstitution * curTailSubst = nullptr; + struct WitnessTableSpecializationWorkItem + { + IRWitnessTable* dstTable; + IRWitnessTable* srcTable; + DeclRef<Decl> specDeclRef; + }; + List<WitnessTableSpecializationWorkItem> witnessTablesToSpecailize; + for (auto param : programLayout->globalGenericParams) + { + auto paramSubst = new GlobalGenericParamSubstitution(); + if (!globalParamSubst) + globalParamSubst = paramSubst; + if (curTailSubst) + curTailSubst->outer = paramSubst; + curTailSubst = paramSubst; + paramSubst->paramDecl = param->decl; + SLANG_ASSERT((UInt)param->index < entryPointRequest->genericParameterTypes.Count()); + paramSubst->actualType = entryPointRequest->genericParameterTypes[param->index]; + // find witness tables + for (auto witness : entryPointRequest->genericParameterWitnesses) + { + if (auto subtypeWitness = witness.As<SubtypeWitness>()) + { + if (subtypeWitness->sub->EqualsVal(paramSubst->actualType)) + { + auto witnessTableName = getMangledNameForConformanceWitness(subtypeWitness->sub, subtypeWitness->sup); + auto findWitnessTableByName = [&](String name) + { + auto globalVar = originalIRModule->getFirstGlobalValue(); + IRGlobalValue * rs = nullptr; + while (globalVar) + { + if (globalVar->mangledName == name) + { + rs = globalVar; + break; + } + globalVar = globalVar->getNextValue(); + } + return rs; + }; + auto table = findWitnessTableByName(witnessTableName); + if (!table) + { + if (auto subDeclRefType = subtypeWitness->sub.As<DeclRefType>()) + { + auto genericWitnessTableName = getMangledNameForConformanceWitness(DeclRef<Decl>(subDeclRefType->declRef.getDecl(), nullptr), subtypeWitness->sup); + table = findWitnessTableByName(genericWitnessTableName); + WitnessTableSpecializationWorkItem workItem; + workItem.srcTable = (IRWitnessTable*)table; + workItem.dstTable = context->builder->createWitnessTable(); + workItem.dstTable->mangledName = getMangledNameForConformanceWitness(subDeclRefType->declRef, subtypeWitness->sup); + workItem.specDeclRef = subDeclRefType->declRef; + registerClonedValue(context, workItem.dstTable, workItem.srcTable); + witnessTablesToSpecailize.Add(workItem); + table = workItem.dstTable; + } + } + else + table = cloneGlobalValue(context, (IRWitnessTable*)(table)); + SLANG_ASSERT(table); + IRProxyVal * tableVal = new IRProxyVal(); + tableVal->inst.init(nullptr, table); + paramSubst->witnessTables.Add(KeyValuePair<RefPtr<Type>, RefPtr<Val>>(subtypeWitness->sup, tableVal)); + } + } + } + } + for (auto workItem : witnessTablesToSpecailize) + { + int diff = 0; + specializeWitnessTable(context->shared, workItem.srcTable, + workItem.specDeclRef.SubstituteImpl(SubstitutionSet(nullptr, nullptr, globalParamSubst), &diff), workItem.dstTable); + } + return globalParamSubst; + } + } diff --git a/source/slang/ir.h b/source/slang/ir.h index c56f8fb62..fe6fab5f6 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -438,7 +438,15 @@ struct IRFunc : IRGlobalValueWithCode // If this function is generic, then we store a reference // to the AST-level generic that defines its parameters // and their constraints. - RefPtr<GenericDecl> genericDecl; + List<RefPtr<GenericDecl>> genericDecls; + int specializedGenericLevel = -1; + + GenericDecl* getGenericDecl() + { + if (specializedGenericLevel != -1) + return genericDecls[specializedGenericLevel].Ptr(); + return nullptr; + } // Convenience accessors for working with the // function's type. diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp index 901661667..376d9bd6d 100644 --- a/source/slang/lookup.cpp +++ b/source/slang/lookup.cpp @@ -1,5 +1,6 @@ // lookup.cpp #include "lookup.h" +#include "name.h" namespace Slang { @@ -320,8 +321,8 @@ void DoLookupImpl( continue; DeclRef<ContainerDecl> containerDeclRef = - DeclRef<Decl>(containerDecl, nullptr).As<ContainerDecl>(); - + DeclRef<Decl>(containerDecl, createDefaultSubstitutions(session, containerDecl)).As<ContainerDecl>(); + BreadcrumbInfo breadcrumb; BreadcrumbInfo* breadcrumbs = nullptr; diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 4af36f718..61ca53278 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -480,9 +480,10 @@ LoweredValInfo emitWitnessTableRef( { if (auto mbrExpr = dynamic_cast<MemberExpr*>(expr)) { - if (auto inheritanceDeclRef = mbrExpr->declRef.As<InheritanceDecl>()) + if (auto typeConstraintDeclRef = mbrExpr->declRef.As<TypeConstraintDecl>()) { - if (inheritanceDeclRef.getDecl()->ParentDecl->As<InterfaceDecl>() || inheritanceDeclRef.getDecl()->ParentDecl->As<AssocTypeDecl>()) + if (mbrExpr->declRef.getDecl()->ParentDecl->As<InterfaceDecl>() + || mbrExpr->declRef.getDecl()->ParentDecl->As<AssocTypeDecl>()) { RefPtr<Type> exprType = nullptr; if (auto tt = mbrExpr->BaseExpression->type->As<TypeType>()) @@ -499,16 +500,19 @@ LoweredValInfo emitWitnessTableRef( // and generate specialize instruction srcDeclRef.substitutions = SubstitutionSet(); } - witnessTableVal = context->irBuilder->emitFindWitnessTable(srcDeclRef, inheritanceDeclRef.getDecl()->base.type); + witnessTableVal = context->irBuilder->emitFindWitnessTable(srcDeclRef, mbrExpr->declRef.As<TypeConstraintDecl>().getDecl()->getSup().type); return maybeEmitSpecializeInst(context, LoweredValInfo::simple(witnessTableVal), declRefType->declRef); } - else if (inheritanceDeclRef.getDecl()->ParentDecl->As<AggTypeDeclBase>()) + } + if (auto inheritanceDecl = mbrExpr->declRef.As<InheritanceDecl>()) + { + if (mbrExpr->declRef.getDecl()->ParentDecl->As<AggTypeDeclBase>()) { - return LoweredValInfo::simple(findWitnessTable(context, inheritanceDeclRef)); + return LoweredValInfo::simple(findWitnessTable(context, mbrExpr->declRef)); } - } - else if (auto genConstraintDeclRef = mbrExpr->declRef.As<GenericTypeConstraintDecl>()) + + if (auto genConstraintDeclRef = mbrExpr->declRef.As<GenericTypeConstraintDecl>()) { return LoweredValInfo::simple(context->irBuilder->getDeclRefVal(genConstraintDeclRef)); } @@ -549,7 +553,6 @@ LoweredValInfo emitFuncRef( // a body, so we don't want to emit or call it), and // we actually want to perform a lookup step to // find the corresponding member on our chosen type. - RefPtr<Type> type = funcExpr->type; auto loweredBaseWitnessTable = emitWitnessTableRef(context, baseMemberExpr); auto loweredVal = LoweredValInfo::simple(context->irBuilder->emitLookupInterfaceMethodInst( @@ -2751,6 +2754,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> SLANG_UNIMPLEMENTED_X("decl catch-all"); } + LoweredValInfo visitExtensionDecl(ExtensionDecl* decl) + { + for (auto & member : decl->Members) + ensureDecl(context, member); + return LoweredValInfo(); + } + LoweredValInfo visitImportDecl(ImportDecl* /*decl*/) { return LoweredValInfo(); @@ -2789,7 +2799,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // Construct the mangled name for the witness table, which depends // on the type that is conforming, and the type that it conforms to. String mangledName = getMangledNameForConformanceWitness( - type, + makeDeclRef(parentDecl), superType); // Build an IR level witness table, which will represent the @@ -3466,11 +3476,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { if(auto genericAncestor = dynamic_cast<GenericDecl*>(pp)) { - irFunc->genericDecl = genericAncestor; - break; + irFunc->genericDecls.Add(genericAncestor); } } - + irFunc->specializedGenericLevel = (int)irFunc->genericDecls.Count() - 1; for( auto paramInfo : parameterLists.params ) { RefPtr<Type> irParamType = lowerSimpleType(context, paramInfo.type); @@ -3904,28 +3913,49 @@ LoweredValInfo maybeEmitSpecializeInst(IRGenContext* context, if (loweredDecl.flavor == LoweredValInfo::Flavor::None) return loweredDecl; + if (!declRef.As<FuncDecl>() && !declRef.As<TypeConstraintDecl>()) + return loweredDecl; + auto val = getSimpleVal(context, loweredDecl); + + RefPtr<GenericSubstitution> outterMostSubst, secondOutterMostSubst; + for (auto subst = declRef.substitutions.genericSubstitutions; subst; subst = subst->outer) + { + if (!subst->outer) + outterMostSubst = subst; + else + secondOutterMostSubst = subst; + } + auto newSubst = outterMostSubst; // We have the "raw" substitutions from the AST, but we may // need to walk through those and replace things in // cases where the `Val`s used for substitution should // lower to something other than their original form. - auto newSubst = lowerSubstitutions(context, declRef.substitutions); - declRef.substitutions = newSubst; - + auto lowedNewSubst = lowerGenericSubstitutions(context, newSubst); + DeclRef<Decl> newDeclRef = DeclRef<Decl>(declRef.decl, + SubstitutionSet(lowedNewSubst, declRef.substitutions.thisTypeSubstitution, + declRef.substitutions.globalGenParamSubstitutions)); RefPtr<Type> type; if (auto declType = val->getType()) { - type = declType->Substitute(declRef.substitutions).As<Type>(); + type = declType->Substitute(newDeclRef.substitutions).As<Type>(); } // Otherwise, we need to construct a specialization of the // given declaration. - return LoweredValInfo::simple((IRInst*)context->irBuilder->emitSpecializeInst( + auto specializedVal = LoweredValInfo::simple((IRInst*)context->irBuilder->emitSpecializeInst( type, val, - declRef)); + newDeclRef)); + if (secondOutterMostSubst) + { + newDeclRef.substitutions.genericSubstitutions = new GenericSubstitution(*secondOutterMostSubst); + newDeclRef.substitutions.genericSubstitutions->outer = nullptr; + return maybeEmitSpecializeInst(context, specializedVal, newDeclRef); + } + return specializedVal; } diff --git a/source/slang/mangle.cpp b/source/slang/mangle.cpp index 070c2b172..1a412d041 100644 --- a/source/slang/mangle.cpp +++ b/source/slang/mangle.cpp @@ -408,6 +408,22 @@ namespace Slang } String getMangledNameForConformanceWitness( + DeclRef<Decl> sub, + Type* sup) + { + // The mangled form for a witness that `sub` + // conforms to `sup` will be named: + // + // {Conforms(sub,sup)} => _SW{sub}{sup} + // + ManglingContext context; + emitRaw(&context, "_SW"); + emitQualifiedName(&context, sub); + emitType(&context, sup); + return context.sb.ProduceString(); + } + + String getMangledNameForConformanceWitness( Type* sub, Type* sup) { diff --git a/source/slang/mangle.h b/source/slang/mangle.h index 45b1d4bd1..8f4c6d1d0 100644 --- a/source/slang/mangle.h +++ b/source/slang/mangle.h @@ -19,6 +19,9 @@ namespace Slang String getMangledNameForConformanceWitness( DeclRef<Decl> sub, DeclRef<Decl> sup); + String getMangledNameForConformanceWitness( + DeclRef<Decl> sub, + Type* sup); String getMangledTypeName(Type* type); } diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 11e3eb159..1378191ca 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -2216,20 +2216,9 @@ namespace Slang return blockVarDecl; } - static RefPtr<RefObject> ParseExtensionDecl(Parser* parser, void* /*userData*/) - { - RefPtr<ExtensionDecl> decl = new ExtensionDecl(); - parser->FillPosition(decl.Ptr()); - decl->targetType = parser->ParseTypeExp(); - - parseAggTypeDeclBody(parser, decl.Ptr()); - - return decl; - } - - static void parseOptionalInheritanceClause(Parser* parser, AggTypeDecl* decl) + static void parseOptionalInheritanceClause(Parser* parser, AggTypeDeclBase* decl) { - if( AdvanceIf(parser, TokenType::Colon) ) + if (AdvanceIf(parser, TokenType::Colon)) { do { @@ -2242,10 +2231,22 @@ namespace Slang AddMember(decl, inheritanceDecl); - } while( AdvanceIf(parser, TokenType::Comma) ); + } while (AdvanceIf(parser, TokenType::Comma)); } } + static RefPtr<RefObject> ParseExtensionDecl(Parser* parser, void* /*userData*/) + { + RefPtr<ExtensionDecl> decl = new ExtensionDecl(); + parser->FillPosition(decl.Ptr()); + decl->targetType = parser->ParseTypeExp(); + parseOptionalInheritanceClause(parser, decl); + parseAggTypeDeclBody(parser, decl.Ptr()); + + return decl; + } + + void parseOptionalGenericConstraints(Parser * parser, ContainerDecl* decl) { if (AdvanceIf(parser, TokenType::Colon)) @@ -2255,6 +2256,7 @@ namespace Slang RefPtr<GenericTypeConstraintDecl> paramConstraint = new GenericTypeConstraintDecl(); parser->FillPosition(paramConstraint); + // substitution needs to be filled during check RefPtr<DeclRefType> paramType = DeclRefType::Create( parser->getSession(), DeclRef<Decl>(decl, nullptr)); @@ -2279,7 +2281,7 @@ namespace Slang auto nameToken = parser->ReadToken(TokenType::Identifier); assocTypeDecl->nameAndLoc = NameLoc(nameToken); assocTypeDecl->loc = nameToken.loc; - parseOptionalInheritanceClause(parser, assocTypeDecl); + parseOptionalGenericConstraints(parser, assocTypeDecl); parser->ReadToken(TokenType::Semicolon); return assocTypeDecl; } diff --git a/source/slang/slang.natvis b/source/slang/slang.natvis index d58e733fa..93979f473 100644 --- a/source/slang/slang.natvis +++ b/source/slang/slang.natvis @@ -12,23 +12,59 @@ <Type Name="Slang::DeclRef<*>"> <SmartPointer Usage="Minimal">decl ? ($T1*)(decl) : ($T1*)0</SmartPointer> <DisplayString Condition="decl == 0">DeclRef nullptr</DisplayString> - <DisplayString Condition="decl != 0">DeclRef {(*(*(Slang::DeclRefBase*)this).decl).nameAndLoc}</DisplayString> + <DisplayString Condition="decl != 0">{(*(*(Slang::DeclRefBase*)this).decl).nameAndLoc}</DisplayString> <Expand> <ExpandedItem>decl ? ($T1*)(decl) : ($T1*)0</ExpandedItem> <Item Name="[Substitutions]:">"========================="</Item> <LinkedListItems> - <HeadPointer>substitutions.pointer</HeadPointer> + <HeadPointer>substitutions.genericSubstitutions.pointer</HeadPointer> <NextPointer>outer.pointer</NextPointer> <ValueNode>this</ValueNode> </LinkedListItems> + <LinkedListItems> + <HeadPointer>substitutions.globalGenParamSubstitutions.pointer</HeadPointer> + <NextPointer>outer.pointer</NextPointer> + <ValueNode>this</ValueNode> + </LinkedListItems> + <Item Name ="thisSubst">substitutions.thisTypeSubstitution</Item> </Expand> </Type> + <Type Name="Slang::DeclRefBase"> + <SmartPointer Usage="Minimal">decl</SmartPointer> + <DisplayString Condition="decl == 0">DeclRefBase nullptr</DisplayString> + <DisplayString Condition="decl != 0">{(*(*(Slang::DeclRefBase*)this).decl).nameAndLoc}</DisplayString> + <Expand> + <ExpandedItem>decl</ExpandedItem> + <Item Name="[Substitutions]:">"========================="</Item> + <LinkedListItems> + <HeadPointer>substitutions.genericSubstitutions.pointer</HeadPointer> + <NextPointer>outer.pointer</NextPointer> + <ValueNode>this</ValueNode> + </LinkedListItems> + <LinkedListItems> + <HeadPointer>substitutions.globalGenParamSubstitutions.pointer</HeadPointer> + <NextPointer>outer.pointer</NextPointer> + <ValueNode>this</ValueNode> + </LinkedListItems> + <Item Name ="thisSubst">substitutions.thisTypeSubstitution</Item> + </Expand> + </Type> + <Type Name="Slang::GenericSubstitution"> + <DisplayString>GenSubst {(*genericDecl).nameAndLoc}</DisplayString> + <Expand> + <Item Name="genericDecl">genericDecl</Item> + <ExpandedItem>args</ExpandedItem> + </Expand> + </Type> <Type Name="Slang::DeclRefType"> <DisplayString>DeclRefType {declRef}</DisplayString> <Expand> <ExpandedItem>declRef</ExpandedItem> </Expand> </Type> + <Type Name="Slang::FuncDecl"> + <DisplayString>FuncDecl {nameAndLoc}</DisplayString> + </Type> <Type Name="Slang::Name"> <DisplayString>{{name={(char*)(text.buffer.pointer+1), s}}}</DisplayString> </Type> @@ -88,6 +124,8 @@ <Item Name="[ResultType]">(*(IRFuncType*)(type.pointer)).resultType</Item> <Item Name="[ParameterTypes]">(*(IRFuncType*)(type.pointer)).paramTypes</Item> <Item Name="[FirstBlock]">firstBlock</Item> + <Item Name="[SpecLevel]">specializedGenericLevel</Item> + <Item Name="genericDecls">genericDecls</Item> </Expand> </Type> diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp index c63dfc781..2ed10e138 100644 --- a/source/slang/syntax.cpp +++ b/source/slang/syntax.cpp @@ -1194,6 +1194,7 @@ void Type::accept(IValVisitor* visitor, void* extra) auto substSubst = new GenericSubstitution(); substSubst->genericDecl = genericDecl; substSubst->args = substArgs; + substSubst->outer = outerSubst.As<GenericSubstitution>(); return substSubst; } @@ -1897,27 +1898,9 @@ void Type::accept(IValVisitor* visitor, void* extra) SubstitutionSet substituteSubstitutions(SubstitutionSet oldSubst, SubstitutionSet subst, int * ioDiff) { - if (oldSubst) - oldSubst = oldSubst.substituteImpl(subst, ioDiff); - - // if oldSubst does not have ThisTypeSubst (which means `this_type` is free variable) - // and subst has a ThisTypeSubst (which means `this_type` is bound to a type), - // then copy that ThisTypeSubst over (to bind the this_type to the specified type) - SubstitutionSet newSubst = oldSubst; - insertGlobalGenericSubstitutions(newSubst, subst, ioDiff); - /*if (!hasThisTypeSubstitutions(oldSubst)) - { - auto thisTypeSubst = findThisTypeSubst(subst); - if (thisTypeSubst) - { - auto cpyThisTypeSubst = new ThisTypeSubstitution(); - cpyThisTypeSubst->sourceType = thisTypeSubst->sourceType; - insertSubstAtBottom(newSubst, cpyThisTypeSubst); - *ioDiff = 1; - } - }*/ - return newSubst; + return oldSubst.substituteImpl(subst, ioDiff); } + bool SubstitutionSet::Equals(SubstitutionSet substSet) const { if (genericSubstitutions) @@ -1951,8 +1934,8 @@ void Type::accept(IValVisitor* visitor, void* extra) rs.globalGenParamSubstitutions = globalGenParamSubstitutions->SubstituteImpl(subst, ioDiff).As<GlobalGenericParamSubstitution>(); if (thisTypeSubstitution) rs.thisTypeSubstitution = thisTypeSubstitution->SubstituteImpl(subst, ioDiff).As<ThisTypeSubstitution>(); - else - rs.thisTypeSubstitution = subst.thisTypeSubstitution; + + insertGlobalGenericSubstitutions(rs, subst, ioDiff); return rs; } int SubstitutionSet::GetHashCode() const diff --git a/tests/compute/nested-generics.slang b/tests/compute/nested-generics.slang new file mode 100644 index 000000000..63b6db4fe --- /dev/null +++ b/tests/compute/nested-generics.slang @@ -0,0 +1,40 @@ +//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +// test specialization of nested generic functions + +RWStructuredBuffer<int> outputBuffer; + +interface IGetF +{ + float getF(); +} + +struct GetFImpl : IGetF +{ + float getF() { return 1.0; } +}; + +struct GetFImpl2 : IGetF +{ + float getF() { return -1.0; } +}; + +struct GenStruct<T : IGetF> +{ + T x; + float genGet<U : IGetF>(U y) + { + return x.getF() + y.getF(); + } +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + GenStruct<GetFImpl> obj1; + GetFImpl2 obj2; + float outVal = obj1.genGet(obj2); + outputBuffer[tid] = int(outVal); +}
\ No newline at end of file diff --git a/tests/compute/nested-generics.slang.expected.txt b/tests/compute/nested-generics.slang.expected.txt new file mode 100644 index 000000000..ae25f7400 --- /dev/null +++ b/tests/compute/nested-generics.slang.expected.txt @@ -0,0 +1,4 @@ +0 +0 +0 +0
\ No newline at end of file diff --git a/tests/compute/nested-generics2.slang b/tests/compute/nested-generics2.slang new file mode 100644 index 000000000..6a14c7678 --- /dev/null +++ b/tests/compute/nested-generics2.slang @@ -0,0 +1,43 @@ +//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +// test specialization of nested generic functions + +RWStructuredBuffer<int> outputBuffer; + +interface IBRDF +{ + float getF(); +} + +interface ILight +{ + float illum<B:IBRDF>(B b); +}; + +struct B0 : IBRDF +{ + float getF() { return 1.0; } +}; + +struct L0 : ILight +{ + float illum<B:IBRDF>(B b) { return b.getF(); } +}; + +struct L1<L:ILight> : ILight +{ + L l; + float illum<BXX:IBRDF>(BXX bxx) { return l.illum<BXX>(bxx); } +}; + + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + L1<L0> light; + B0 b0; + float outVal = light.illum<B0>(b0); + outputBuffer[tid] = int(outVal); +}
\ No newline at end of file diff --git a/tests/compute/nested-generics2.slang.expected.txt b/tests/compute/nested-generics2.slang.expected.txt new file mode 100644 index 000000000..ef529012e --- /dev/null +++ b/tests/compute/nested-generics2.slang.expected.txt @@ -0,0 +1,4 @@ +1 +1 +1 +1
\ No newline at end of file diff --git a/tests/compute/struct-in-generic.slang b/tests/compute/struct-in-generic.slang new file mode 100644 index 000000000..6f65f2ee3 --- /dev/null +++ b/tests/compute/struct-in-generic.slang @@ -0,0 +1,36 @@ +//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +// Confirm that a struct type defined in a generic parent works + +RWStructuredBuffer<float> outputBuffer; + +struct GenStruct<T> +{ + struct SubType + { + T x; + }; + T getVal(SubType v) + { + return v.x; + } +}; + +T test<T>(T val) +{ + GenStruct<T>.SubType sb; + sb.x = val; + GenStruct<T> obj; + return obj.getVal(sb); +} + + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + float inVal = float(tid); + float outVal = test(inVal); + outputBuffer[tid] = outVal.x; +}
\ No newline at end of file diff --git a/tests/compute/struct-in-generic.slang.expected.txt b/tests/compute/struct-in-generic.slang.expected.txt new file mode 100644 index 000000000..ae6b9920b --- /dev/null +++ b/tests/compute/struct-in-generic.slang.expected.txt @@ -0,0 +1,4 @@ +0 +3F800000 +40000000 +40400000 diff --git a/tools/render-test/test.txt b/tools/render-test/test.txt new file mode 100644 index 000000000..376ef42c4 --- /dev/null +++ b/tools/render-test/test.txt @@ -0,0 +1,4 @@ +0 +0 +80000000 +3F800000 |
