From 1b8731c809761c4e2dbec81dcee207f8a4621903 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Wed, 3 Jun 2020 17:22:48 -0400 Subject: Devirtualize AST types (#1368) * Make getSup work with more general non-virtual 'virtual' mechanism. * WIP: Non virtual AST types. * Project change. * Type doesn't implement equalsImpl * Fix macro invocation Make Overridden functions public to make simply accessible by base types. * Use SLANG_UNEXPECTED. * GetScalarType -> getScalarType Use SLANG_UNEXPECTED instead on ASSERT in NamedExpressionType and TypeType --- source/slang/slang-syntax.cpp | 2556 ++++++++--------------------------------- 1 file changed, 483 insertions(+), 2073 deletions(-) (limited to 'source/slang/slang-syntax.cpp') diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index 1b96aec98..c78de91a5 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -51,19 +51,6 @@ SourceLoc const& getDiagnosticPos(TypeExp const& typeExp) return typeExp.exp->loc; } -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! BasicExpressionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -bool BasicExpressionType::equalsImpl(Type * type) -{ - auto basicType = as(type); - return basicType && basicType->baseType == this->baseType; -} - -RefPtr BasicExpressionType::createCanonicalType() -{ - // A basic type is already canonical, in our setup - return this; -} // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! Free functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -213,149 +200,6 @@ Index getFilterCountImpl(const ReflectClassInfo& clsInfo, MemberFilterStyle filt return type->equals(other); } - // BasicExpressionType - - BasicExpressionType* BasicExpressionType::GetScalarType() - { - return this; - } - - // - - Type::~Type() - { - // If the canonicalType !=nullptr AND it is not set to this (ie the canonicalType is another object) - // then it needs to be released because it's owned by this object. - if (canonicalType && canonicalType != this) - { - canonicalType->releaseReference(); - } - } - - bool Type::equals(Type* type) - { - return getCanonicalType()->equalsImpl(type->getCanonicalType()); - } - - bool Type::equalsVal(Val* val) - { - if (auto type = dynamicCast(val)) - return const_cast(this)->equals(type); - return false; - } - - RefPtr Type::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - int diff = 0; - auto canSubst = getCanonicalType()->substituteImpl(astBuilder, subst, &diff); - - // If nothing changed, then don't drop any sugar that is applied - if (!diff) - return this; - - // If the canonical type changed, then we return a canonical type, - // rather than try to re-construct any amount of sugar - (*ioDiff)++; - return canSubst; - } - - Type* Type::getCanonicalType() - { - Type* et = const_cast(this); - if (!et->canonicalType) - { - // TODO(tfoley): worry about thread safety here? - auto canType = et->createCanonicalType(); - et->canonicalType = canType; - - // TODO(js): That this detachs when canType == this is a little surprising. It would seem - // as if this would create a circular reference on the object, but in practice there are - // no leaks so appears correct. - // That the dtor only releases if != this, also makes it surprising. - canType.detach(); - - SLANG_ASSERT(et->canonicalType); - } - return et->canonicalType; - } - - bool ArrayExpressionType::equalsImpl(Type* type) - { - auto arrType = as(type); - if (!arrType) - return false; - return (areValsEqual(arrayLength, arrType->arrayLength) && baseType->equals(arrType->baseType.Ptr())); - } - - RefPtr ArrayExpressionType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - int diff = 0; - auto elementType = baseType->substituteImpl(astBuilder, subst, &diff).as(); - auto arrlen = arrayLength->substituteImpl(astBuilder, subst, &diff).as(); - SLANG_ASSERT(arrlen); - if (diff) - { - *ioDiff = 1; - auto rsType = getArrayType( - astBuilder, - elementType, - arrlen); - return rsType; - } - return this; - } - - RefPtr ArrayExpressionType::createCanonicalType() - { - auto canonicalElementType = baseType->getCanonicalType(); - auto canonicalArrayType = getASTBuilder()->getArrayType( - canonicalElementType, - arrayLength); - return canonicalArrayType; - } - - HashCode ArrayExpressionType::getHashCode() - { - if (arrayLength) - return (baseType->getHashCode() * 16777619) ^ arrayLength->getHashCode(); - else - return baseType->getHashCode(); - } - Slang::String ArrayExpressionType::toString() - { - if (arrayLength) - return baseType->toString() + "[" + arrayLength->toString() + "]"; - else - return baseType->toString() + "[]"; - } - - // DeclRefType - - String DeclRefType::toString() - { - return declRef.toString(); - } - - HashCode DeclRefType::getHashCode() - { - return (declRef.getHashCode() * 16777619) ^ (HashCode)(typeid(this).hash_code()); - } - - bool DeclRefType::equalsImpl(Type * type) - { - if (auto declRefType = as(type)) - { - return declRef.equals(declRefType->declRef); - } - return false; - } - - RefPtr DeclRefType::createCanonicalType() - { - // A declaration reference is already canonical - return this; - } - // // RequirementWitness // @@ -478,117 +322,7 @@ Index getFilterCountImpl(const ReflectClassInfo& clsInfo, MemberFilterStyle filt return RequirementWitness(); } - RefPtr DeclRefType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - if (!subst) return this; - - // the case we especially care about is when this type references a declaration - // of a generic parameter, since that is what we might be substituting... - if (auto genericTypeParamDecl = as(declRef.getDecl())) - { - // search for a substitution that might apply to us - for(auto s = subst.substitutions; s; s = s->outer) - { - auto genericSubst = s.as(); - if(!genericSubst) - continue; - - // the generic decl associated with the substitution list must be - // the generic decl that declared this parameter - auto genericDecl = genericSubst->genericDecl; - if (genericDecl != genericTypeParamDecl->parentDecl) - continue; - - int index = 0; - for (auto m : genericDecl->members) - { - if (m.Ptr() == genericTypeParamDecl) - { - // We've found it, so return the corresponding specialization argument - (*ioDiff)++; - return genericSubst->args[index]; - } - else if (auto typeParam = as(m)) - { - index++; - } - else if (auto valParam = as(m)) - { - index++; - } - else - { - } - } - } - } - else if (auto globalGenParam = as(declRef.getDecl())) - { - // search for a substitution that might apply to us - for(auto s = subst.substitutions; s; s = s->outer) - { - auto genericSubst = as(s); - if(!genericSubst) - continue; - - if (genericSubst->paramDecl == globalGenParam) - { - (*ioDiff)++; - return genericSubst->actualType; - } - } - } - int diff = 0; - DeclRef substDeclRef = declRef.substituteImpl(astBuilder, subst, &diff); - - if (!diff) - return this; - - // Make sure to record the difference! - *ioDiff += diff; - - // If this type is a reference to an associated type declaration, - // and the substitutions provide a "this type" substitution for - // the outer interface, then try to replace the type with the - // actual value of the associated type for the given implementation. - // - if(auto substAssocTypeDecl = as(substDeclRef.decl)) - { - for(auto s = substDeclRef.substitutions.substitutions; s; s = s->outer) - { - auto thisSubst = s.as(); - if(!thisSubst) - continue; - - if(auto interfaceDecl = as(substAssocTypeDecl->parentDecl)) - { - if(thisSubst->interfaceDecl == interfaceDecl) - { - // We need to look up the declaration that satisfies - // the requirement named by the associated type. - Decl* requirementKey = substAssocTypeDecl; - RequirementWitness requirementWitness = tryLookUpRequirementWitness(astBuilder, thisSubst->witness, requirementKey); - switch(requirementWitness.getFlavor()) - { - default: - // No usable value was found, so there is nothing we can do. - break; - - case RequirementWitness::Flavor::val: - { - auto satisfyingVal = requirementWitness.getVal(); - return satisfyingVal; - } - break; - } - } - } - } - } - - // Re-construct the type in case we are using a specialized sub-class - return DeclRefType::create(astBuilder, substDeclRef); - } + static RefPtr ExtractGenericArgType(RefPtr val) { @@ -848,1812 +582,668 @@ Index getFilterCountImpl(const ReflectClassInfo& clsInfo, MemberFilterStyle filt } } - // OverloadGroupType + // - String OverloadGroupType::toString() + RefPtr findInnerMostGenericSubstitution(Substitutions* subst) { - return "overload group"; + for(RefPtr s = subst; s; s = s->outer) + { + if(auto genericSubst = as(s)) + return genericSubst; + } + return nullptr; } - bool OverloadGroupType::equalsImpl(Type * /*type*/) - { - return false; - } + + // DeclRefBase - RefPtr OverloadGroupType::createCanonicalType() + RefPtr DeclRefBase::substitute(ASTBuilder* astBuilder, RefPtr type) const { - return this; - } + // Note that type can be nullptr, and so this function can return nullptr (although only correctly when no substitutions) - HashCode OverloadGroupType::getHashCode() - { - return (HashCode)(size_t(this)); - } + // No substitutions? Easy. + if (!substitutions) + return type; - // InitializerListType + SLANG_ASSERT(type); - String InitializerListType::toString() - { - return "initializer list"; + // Otherwise we need to recurse on the type structure + // and apply substitutions where it makes sense + return type->substitute(astBuilder, substitutions).as(); } - bool InitializerListType::equalsImpl(Type * /*type*/) + DeclRefBase DeclRefBase::substitute(ASTBuilder* astBuilder, DeclRefBase declRef) const { - return false; - } + if(!substitutions) + return declRef; - RefPtr InitializerListType::createCanonicalType() - { - return this; + int diff = 0; + return declRef.substituteImpl(astBuilder, substitutions, &diff); } - HashCode InitializerListType::getHashCode() + RefPtr DeclRefBase::substitute(ASTBuilder* /* astBuilder*/, RefPtr expr) const { - return (HashCode)(size_t(this)); - } + // No substitutions? Easy. + if (!substitutions) + return expr; - // ErrorType + SLANG_UNIMPLEMENTED_X("generic substitution into expressions"); - String ErrorType::toString() - { - return "error"; + UNREACHABLE_RETURN(expr); } - bool ErrorType::equalsImpl(Type* type) - { - if (auto errorType = as(type)) - return true; - return false; - } + void buildMemberDictionary(ContainerDecl* decl); - RefPtr ErrorType::createCanonicalType() + InterfaceDecl* findOuterInterfaceDecl(Decl* decl) { - return this; - } + Decl* dd = decl; + while(dd) + { + if(auto interfaceDecl = as(dd)) + return interfaceDecl; - RefPtr ErrorType::substituteImpl(ASTBuilder* /* astBuilder */, SubstitutionSet /*subst*/, int* /*ioDiff*/) - { - return this; + dd = dd->parentDecl; + } + return nullptr; } - HashCode ErrorType::getHashCode() + RefPtr findGlobalGenericSubst( + RefPtr substs, + GlobalGenericParamDecl* paramDecl) { - return HashCode(size_t(this)); - } - - - // NamedExpressionType + for(auto s = substs; s; s = s->outer) + { + auto gSubst = s.as(); + if(!gSubst) + continue; - String NamedExpressionType::toString() - { - return getText(declRef.getName()); - } + if(gSubst->paramDecl != paramDecl) + continue; - bool NamedExpressionType::equalsImpl(Type * /*type*/) - { - SLANG_UNEXPECTED("unreachable"); - UNREACHABLE_RETURN(false); - } + return gSubst; + } - RefPtr NamedExpressionType::createCanonicalType() - { - if (!innerType) - innerType = getType(m_astBuilder, declRef); - return innerType->getCanonicalType(); + return nullptr; } - HashCode NamedExpressionType::getHashCode() + RefPtr specializeSubstitutionsShallow( + ASTBuilder* astBuilder, + RefPtr substToSpecialize, + RefPtr substsToApply, + RefPtr restSubst, + int* ioDiff) { - // Type equality is based on comparing canonical types, - // so the hash code for a type needs to come from the - // canonical version of the type. This really means - // that `Type::getHashCode()` should dispatch out to - // something like `Type::getHashCodeImpl()` on the - // canonical version of a type, but it is less invasive - // for now (and hopefully equivalent) to just have any - // named types automaticlaly route hash-code requests - // to their canonical type. - return getCanonicalType()->getHashCode(); + SLANG_ASSERT(substToSpecialize); + return substToSpecialize->applySubstitutionsShallow(astBuilder, substsToApply, restSubst, ioDiff); } - // FuncType - - String FuncType::toString() + RefPtr specializeGlobalGenericSubstitutions( + ASTBuilder* astBuilder, + Decl* declToSpecialize, + RefPtr substsToSpecialize, + RefPtr substsToApply, + int* ioDiff, + HashSet& ioParametersFound) { - StringBuilder sb; - sb << "("; - UInt paramCount = getParamCount(); - for (UInt pp = 0; pp < paramCount; ++pp) + // Any existing global-generic substitutions will trigger + // a recursive case that skips the rest of the function. + for(auto specSubst = substsToSpecialize; specSubst; specSubst = specSubst->outer) { - if (pp != 0) sb << ", "; - sb << getParamType(pp)->toString(); - } - sb << ") -> "; - sb << getResultType()->toString(); - return sb.ProduceString(); - } + auto specGlobalGenericSubst = specSubst.as(); + if(!specGlobalGenericSubst) + continue; - bool FuncType::equalsImpl(Type * type) - { - if (auto funcType = as(type)) - { - auto paramCount = getParamCount(); - auto otherParamCount = funcType->getParamCount(); - if (paramCount != otherParamCount) - return false; + ioParametersFound.Add(specGlobalGenericSubst->paramDecl); - for (UInt pp = 0; pp < paramCount; ++pp) - { - auto paramType = getParamType(pp); - auto otherParamType = funcType->getParamType(pp); - if (!paramType->equals(otherParamType)) - return false; - } + int diff = 0; + auto restSubst = specializeGlobalGenericSubstitutions( + astBuilder, + declToSpecialize, + specSubst->outer, + substsToApply, + &diff, + ioParametersFound); - if(!resultType->equals(funcType->resultType)) - return false; + auto firstSubst = specializeSubstitutionsShallow( + astBuilder, + specGlobalGenericSubst, + substsToApply, + restSubst, + &diff); - // TODO: if we ever introduce other kinds - // of qualification on function types, we'd - // want to consider it here. - return true; + *ioDiff += diff; + return firstSubst; } - return false; - } - RefPtr FuncType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - int diff = 0; + // No more existing substitutions, so we know we can apply + // our global generic substitutions without any special work. - // result type - RefPtr substResultType = resultType->substituteImpl(astBuilder, subst, &diff).as(); + // We expect global generic substitutions to come at + // the end of the list in all cases, so lets advance + // until we see them. + RefPtr appGlobalGenericSubsts = substsToApply; + while(appGlobalGenericSubsts && !appGlobalGenericSubsts.as()) + appGlobalGenericSubsts = appGlobalGenericSubsts->outer; - // parameter types - List> substParamTypes; - for( auto pp : paramTypes ) - { - substParamTypes.add(pp->substituteImpl(astBuilder, subst, &diff).as()); - } - // early exit for no change... - if(!diff) - return this; + // If there is nothing to apply, then we are done + if(!appGlobalGenericSubsts) + return nullptr; + // Otherwise, it seems like something has to change. (*ioDiff)++; - RefPtr substType = astBuilder->create(); - substType->resultType = substResultType; - substType->paramTypes = substParamTypes; - return substType; - } - RefPtr FuncType::createCanonicalType() - { - // result type - RefPtr canResultType = resultType->getCanonicalType(); - - // parameter types - List> canParamTypes; - for( auto pp : paramTypes ) - { - canParamTypes.add(pp->getCanonicalType()); - } - - RefPtr canType = getASTBuilder()->create(); - canType->resultType = resultType; - canType->paramTypes = canParamTypes; - - return canType; - } - - HashCode FuncType::getHashCode() - { - HashCode hashCode = getResultType()->getHashCode(); - UInt paramCount = getParamCount(); - hashCode = combineHash(hashCode, Slang::getHashCode(paramCount)); - for (UInt pp = 0; pp < paramCount; ++pp) - { - hashCode = combineHash( - hashCode, - getParamType(pp)->getHashCode()); - } - return hashCode; - } - - // TypeType - - String TypeType::toString() - { - StringBuilder sb; - sb << "typeof(" << type->toString() << ")"; - return sb.ProduceString(); - } - - bool TypeType::equalsImpl(Type * t) - { - if (auto typeType = as(t)) - { - return t->equals(typeType->type); - } - return false; - } - - RefPtr TypeType::createCanonicalType() - { - return getASTBuilder()->getTypeType(type->getCanonicalType()); - } - - HashCode TypeType::getHashCode() - { - SLANG_UNEXPECTED("unreachable"); - UNREACHABLE_RETURN(0); - } - - // GenericDeclRefType - - String GenericDeclRefType::toString() - { - // TODO: what is appropriate here? - return ">"; - } - - bool GenericDeclRefType::equalsImpl(Type * type) - { - if (auto genericDeclRefType = as(type)) - { - return declRef.equals(genericDeclRefType->declRef); - } - return false; - } - - HashCode GenericDeclRefType::getHashCode() - { - return declRef.getHashCode(); - } - - RefPtr GenericDeclRefType::createCanonicalType() - { - return this; - } - - // NamespaceType - - String NamespaceType::toString() - { - String result; - result.append("namespace "); - result.append(declRef.toString()); - return result; - } - - bool NamespaceType::equalsImpl(Type * type) - { - if (auto namespaceType = as(type)) - { - return declRef.equals(namespaceType->declRef); - } - return false; - } - - HashCode NamespaceType::getHashCode() - { - return declRef.getHashCode(); - } - - RefPtr NamespaceType::createCanonicalType() - { - return this; - } - - // ArithmeticExpressionType - - // VectorExpressionType - - String VectorExpressionType::toString() - { - StringBuilder sb; - sb << "vector<" << elementType->toString() << "," << elementCount->toString() << ">"; - return sb.ProduceString(); - } - - BasicExpressionType* VectorExpressionType::GetScalarType() - { - return as(elementType); - } - - // - - RefPtr findInnerMostGenericSubstitution(Substitutions* subst) - { - for(RefPtr s = subst; s; s = s->outer) - { - if(auto genericSubst = as(s)) - return genericSubst; - } - return nullptr; - } - - // MatrixExpressionType - - String MatrixExpressionType::toString() - { - StringBuilder sb; - sb << "matrix<" << getElementType()->toString() << "," << getRowCount()->toString() << "," << getColumnCount()->toString() << ">"; - return sb.ProduceString(); - } - - BasicExpressionType* MatrixExpressionType::GetScalarType() - { - return as(getElementType()); - } - - Type* MatrixExpressionType::getElementType() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[0]); - } - - IntVal* MatrixExpressionType::getRowCount() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[1]); - } - - IntVal* MatrixExpressionType::getColumnCount() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[2]); - } - - RefPtr MatrixExpressionType::getRowType() - { - if( !rowType ) - { - rowType = m_astBuilder->getVectorType(getElementType(), getColumnCount()); - } - return rowType; - } - - - - - // PtrTypeBase - - Type* PtrTypeBase::getValueType() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[0]); - } - - // GenericParamIntVal - - bool GenericParamIntVal::equalsVal(Val* val) - { - if (auto genericParamVal = as(val)) - { - return declRef.equals(genericParamVal->declRef); - } - return false; - } - - String GenericParamIntVal::toString() - { - return getText(declRef.getName()); - } - - HashCode GenericParamIntVal::getHashCode() - { - return declRef.getHashCode() ^ HashCode(0xFFFF); - } + // If there were no parameters bound by the existing substitution, + // then we can safely use the global generics from the to-apply set. + if(ioParametersFound.Count() == 0) + return appGlobalGenericSubsts; - RefPtr GenericParamIntVal::substituteImpl(ASTBuilder* /* astBuilder */, SubstitutionSet subst, int* ioDiff) - { - // search for a substitution that might apply to us - for(auto s = subst.substitutions; s; s = s->outer) + RefPtr resultSubst; + RefPtr* link = &resultSubst; + for(auto appSubst = appGlobalGenericSubsts; appSubst; appSubst = appSubst->outer) { - auto genSubst = s.as(); - if(!genSubst) + auto appGlobalGenericSubst = appSubst.as(); + if(!appSubst) continue; - // the generic decl associated with the substitution list must be - // the generic decl that declared this parameter - auto genericDecl = genSubst->genericDecl; - if (genericDecl != declRef.getDecl()->parentDecl) + // Don't include substitutions for parameters already handled. + if(ioParametersFound.Contains(appGlobalGenericSubst->paramDecl)) continue; - int index = 0; - for (auto m : genericDecl->members) - { - if (m.Ptr() == declRef.getDecl()) - { - // We've found it, so return the corresponding specialization argument - (*ioDiff)++; - return genSubst->args[index]; - } - else if (auto typeParam = as(m)) - { - index++; - } - else if (auto valParam = as(m)) - { - index++; - } - else - { - } - } - } - - // Nothing found: don't substitute. - return this; - } - - // ErrorIntVal - - bool ErrorIntVal::equalsVal(Val* val) - { - if( auto errorIntVal = as(val) ) - { - return true; - } - return false; - } - - String ErrorIntVal::toString() - { - return ""; - } - - HashCode ErrorIntVal::getHashCode() - { - return HashCode(typeid(this).hash_code()); - } - - RefPtr ErrorIntVal::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - SLANG_UNUSED(astBuilder); - SLANG_UNUSED(subst); - SLANG_UNUSED(ioDiff); - return this; - } - - // Substitutions - - RefPtr GenericSubstitution::applySubstitutionsShallow(ASTBuilder* astBuilder, SubstitutionSet substSet, RefPtr substOuter, int* ioDiff) - { - int diff = 0; - - if(substOuter != outer) diff++; - - List> substArgs; - for (auto a : args) - { - substArgs.add(a->substituteImpl(astBuilder, substSet, &diff)); - } - - if (!diff) return this; - - (*ioDiff)++; - auto substSubst = astBuilder->create(); - substSubst->genericDecl = genericDecl; - substSubst->args = substArgs; - substSubst->outer = substOuter; - return substSubst; - } - - bool GenericSubstitution::equals(Substitutions* subst) - { - // both must be NULL, or non-NULL - if (subst == nullptr) - return false; - if (this == subst) - return true; - - auto genericSubst = as(subst); - if (!genericSubst) - return false; - if (genericDecl != genericSubst->genericDecl) - return false; + RefPtr newSubst = astBuilder->create(); + newSubst->paramDecl = appGlobalGenericSubst->paramDecl; + newSubst->actualType = appGlobalGenericSubst->actualType; + newSubst->constraintArgs = appGlobalGenericSubst->constraintArgs; - Index argCount = args.getCount(); - SLANG_RELEASE_ASSERT(args.getCount() == genericSubst->args.getCount()); - for (Index aa = 0; aa < argCount; ++aa) - { - if (!args[aa]->equalsVal(genericSubst->args[aa].Ptr())) - return false; + *link = newSubst; + link = &newSubst->outer; } - if (!outer) - return !genericSubst->outer; - - if (!outer->equals(genericSubst->outer.Ptr())) - return false; - - return true; - } - - RefPtr ThisTypeSubstitution::applySubstitutionsShallow(ASTBuilder* astBuilder, SubstitutionSet substSet, RefPtr substOuter, int* ioDiff) - { - int diff = 0; - - if(substOuter != outer) diff++; - - // NOTE: Must use .as because we must have a smart pointer here to keep in scope. - auto substWitness = witness->substituteImpl(astBuilder, substSet, &diff).as(); - - if (!diff) return this; - - (*ioDiff)++; - auto substSubst = astBuilder->create(); - substSubst->interfaceDecl = interfaceDecl; - substSubst->witness = substWitness; - substSubst->outer = substOuter; - return substSubst; + return resultSubst; } - bool ThisTypeSubstitution::equals(Substitutions* subst) + RefPtr specializeGlobalGenericSubstitutions( + ASTBuilder* astBuilder, + Decl* declToSpecialize, + RefPtr substsToSpecialize, + RefPtr substsToApply, + int* ioDiff) { - if (!subst) - return false; - if (subst == this) - return true; - - if (auto thisTypeSubst = as(subst)) - { - // For our purposes, two this-type substitutions are - // equivalent if they have the same type as `This`, - // even if the specific witness values they use - // might differ. - // - if(this->interfaceDecl != thisTypeSubst->interfaceDecl) - return false; - - if(!this->witness->sub->equals(thisTypeSubst->witness->sub)) - return false; - - return true; - } - return false; + // Keep track of any parameters already present in the + // existing substitution. + HashSet parametersFound; + return specializeGlobalGenericSubstitutions(astBuilder, declToSpecialize, substsToSpecialize, substsToApply, ioDiff, parametersFound); } - HashCode ThisTypeSubstitution::getHashCode() const - { - return witness->getHashCode(); - } - RefPtr GlobalGenericParamSubstitution::applySubstitutionsShallow(ASTBuilder* astBuilder, SubstitutionSet substSet, RefPtr substOuter, int* ioDiff) + // Construct new substitutions to apply to a declaration, + // based on a provided substitution set to be applied + RefPtr specializeSubstitutions( + ASTBuilder* astBuilder, + Decl* declToSpecialize, + RefPtr substsToSpecialize, + RefPtr substsToApply, + int* ioDiff) { - // if we find a GlobalGenericParamSubstitution in subst that references the same type_param decl - // return a copy of that GlobalGenericParamSubstitution - int diff = 0; - - if(substOuter != outer) diff++; - - auto substActualType = actualType->substituteImpl(astBuilder, substSet, &diff).as(); - - List substConstraintArgs; - for(auto constraintArg : constraintArgs) - { - ConstraintArg substConstraintArg; - substConstraintArg.decl = constraintArg.decl; - substConstraintArg.val = constraintArg.val->substituteImpl(astBuilder, substSet, &diff); - - substConstraintArgs.add(substConstraintArg); - } - - if(!diff) - return this; - - (*ioDiff)++; - - RefPtr substSubst = astBuilder->create(); - substSubst->paramDecl = paramDecl; - substSubst->actualType = substActualType; - substSubst->constraintArgs = substConstraintArgs; - substSubst->outer = substOuter; - return substSubst; - } + // No declaration? Then nothing to specialize. + if(!declToSpecialize) + return nullptr; - bool GlobalGenericParamSubstitution::equals(Substitutions* subst) - { - if (!subst) - return false; - if (subst == this) - return true; + // No (remaining) substitutions to apply? Then we are done. + if(!substsToApply) + return substsToSpecialize; - if (auto genSubst = as(subst)) + // Walk the hierarchy of the declaration to determine what specializations might apply. + // We assume that the `substsToSpecialize` must be aligned with the ancestor + // hierarchy of `declToSpecialize` such that if, e.g., the `declToSpecialize` is + // nested directly in a generic, then `substToSpecialize` will either start with + // the corresponding `GenericSubstitution` or there will be *no* generic substitutions + // corresponding to that decl. + for(Decl* ancestorDecl = declToSpecialize; ancestorDecl; ancestorDecl = ancestorDecl->parentDecl) { - if (paramDecl != genSubst->paramDecl) - return false; - if (!actualType->equalsVal(genSubst->actualType)) - return false; - if (constraintArgs.getCount() != genSubst->constraintArgs.getCount()) - return false; - for (Index i = 0; i < constraintArgs.getCount(); i++) - { - if (!constraintArgs[i].val->equalsVal(genSubst->constraintArgs[i].val)) - return false; - } - return true; - } - return false; - } - - - // DeclRefBase - - RefPtr DeclRefBase::substitute(ASTBuilder* astBuilder, RefPtr type) const - { - // Note that type can be nullptr, and so this function can return nullptr (although only correctly when no substitutions) - - // No substitutions? Easy. - if (!substitutions) - return type; - - SLANG_ASSERT(type); - - // Otherwise we need to recurse on the type structure - // and apply substitutions where it makes sense - return type->substitute(astBuilder, substitutions).as(); - } - - DeclRefBase DeclRefBase::substitute(ASTBuilder* astBuilder, DeclRefBase declRef) const - { - if(!substitutions) - return declRef; - - int diff = 0; - return declRef.substituteImpl(astBuilder, substitutions, &diff); - } - - RefPtr DeclRefBase::substitute(ASTBuilder* /* astBuilder*/, RefPtr expr) const - { - // No substitutions? Easy. - if (!substitutions) - return expr; - - SLANG_UNIMPLEMENTED_X("generic substitution into expressions"); - - UNREACHABLE_RETURN(expr); - } - - void buildMemberDictionary(ContainerDecl* decl); - - InterfaceDecl* findOuterInterfaceDecl(Decl* decl) - { - Decl* dd = decl; - while(dd) - { - if(auto interfaceDecl = as(dd)) - return interfaceDecl; - - dd = dd->parentDecl; - } - return nullptr; - } - - RefPtr findGlobalGenericSubst( - RefPtr substs, - GlobalGenericParamDecl* paramDecl) - { - for(auto s = substs; s; s = s->outer) - { - auto gSubst = s.as(); - if(!gSubst) - continue; - - if(gSubst->paramDecl != paramDecl) - continue; - - return gSubst; - } - - return nullptr; - } - - RefPtr specializeSubstitutionsShallow( - ASTBuilder* astBuilder, - RefPtr substToSpecialize, - RefPtr substsToApply, - RefPtr restSubst, - int* ioDiff) - { - SLANG_ASSERT(substToSpecialize); - return substToSpecialize->applySubstitutionsShallow(astBuilder, substsToApply, restSubst, ioDiff); - } - - RefPtr specializeGlobalGenericSubstitutions( - ASTBuilder* astBuilder, - Decl* declToSpecialize, - RefPtr substsToSpecialize, - RefPtr substsToApply, - int* ioDiff, - HashSet& ioParametersFound) - { - // Any existing global-generic substitutions will trigger - // a recursive case that skips the rest of the function. - for(auto specSubst = substsToSpecialize; specSubst; specSubst = specSubst->outer) - { - auto specGlobalGenericSubst = specSubst.as(); - if(!specGlobalGenericSubst) - continue; - - ioParametersFound.Add(specGlobalGenericSubst->paramDecl); - - int diff = 0; - auto restSubst = specializeGlobalGenericSubstitutions( - astBuilder, - declToSpecialize, - specSubst->outer, - substsToApply, - &diff, - ioParametersFound); - - auto firstSubst = specializeSubstitutionsShallow( - astBuilder, - specGlobalGenericSubst, - substsToApply, - restSubst, - &diff); - - *ioDiff += diff; - return firstSubst; - } - - // No more existing substitutions, so we know we can apply - // our global generic substitutions without any special work. - - // We expect global generic substitutions to come at - // the end of the list in all cases, so lets advance - // until we see them. - RefPtr appGlobalGenericSubsts = substsToApply; - while(appGlobalGenericSubsts && !appGlobalGenericSubsts.as()) - appGlobalGenericSubsts = appGlobalGenericSubsts->outer; - - - // If there is nothing to apply, then we are done - if(!appGlobalGenericSubsts) - return nullptr; - - // Otherwise, it seems like something has to change. - (*ioDiff)++; - - // If there were no parameters bound by the existing substitution, - // then we can safely use the global generics from the to-apply set. - if(ioParametersFound.Count() == 0) - return appGlobalGenericSubsts; - - RefPtr resultSubst; - RefPtr* link = &resultSubst; - for(auto appSubst = appGlobalGenericSubsts; appSubst; appSubst = appSubst->outer) - { - auto appGlobalGenericSubst = appSubst.as(); - if(!appSubst) - continue; - - // Don't include substitutions for parameters already handled. - if(ioParametersFound.Contains(appGlobalGenericSubst->paramDecl)) - continue; - - RefPtr newSubst = astBuilder->create(); - newSubst->paramDecl = appGlobalGenericSubst->paramDecl; - newSubst->actualType = appGlobalGenericSubst->actualType; - newSubst->constraintArgs = appGlobalGenericSubst->constraintArgs; - - *link = newSubst; - link = &newSubst->outer; - } - - return resultSubst; - } - - RefPtr specializeGlobalGenericSubstitutions( - ASTBuilder* astBuilder, - Decl* declToSpecialize, - RefPtr substsToSpecialize, - RefPtr substsToApply, - int* ioDiff) - { - // Keep track of any parameters already present in the - // existing substitution. - HashSet parametersFound; - return specializeGlobalGenericSubstitutions(astBuilder, declToSpecialize, substsToSpecialize, substsToApply, ioDiff, parametersFound); - } - - - // Construct new substitutions to apply to a declaration, - // based on a provided substitution set to be applied - RefPtr specializeSubstitutions( - ASTBuilder* astBuilder, - Decl* declToSpecialize, - RefPtr substsToSpecialize, - RefPtr substsToApply, - int* ioDiff) - { - // No declaration? Then nothing to specialize. - if(!declToSpecialize) - return nullptr; - - // No (remaining) substitutions to apply? Then we are done. - if(!substsToApply) - return substsToSpecialize; - - // Walk the hierarchy of the declaration to determine what specializations might apply. - // We assume that the `substsToSpecialize` must be aligned with the ancestor - // hierarchy of `declToSpecialize` such that if, e.g., the `declToSpecialize` is - // nested directly in a generic, then `substToSpecialize` will either start with - // the corresponding `GenericSubstitution` or there will be *no* generic substitutions - // corresponding to that decl. - for(Decl* ancestorDecl = declToSpecialize; ancestorDecl; ancestorDecl = ancestorDecl->parentDecl) - { - if(auto ancestorGenericDecl = as(ancestorDecl)) - { - // The declaration is nested inside a generic. - // Does it already have a specialization for that generic? - if(auto specGenericSubst = as(substsToSpecialize)) - { - if(specGenericSubst->genericDecl == ancestorGenericDecl) - { - // Yes. We have an existing specialization, so we will - // keep one matching it in place. - int diff = 0; - auto restSubst = specializeSubstitutions( - astBuilder, - ancestorGenericDecl->parentDecl, - specGenericSubst->outer, - substsToApply, - &diff); - - auto firstSubst = specializeSubstitutionsShallow( - astBuilder, - specGenericSubst, - substsToApply, - restSubst, - &diff); - - *ioDiff += diff; - return firstSubst; - } - } - - // If the declaration is not already specialized - // for the given generic, then see if we are trying - // to *apply* such specializations to it. - // - // TODO: The way we handle things right now with - // "default" specializations, this case shouldn't - // actually come up. - // - for(auto s = substsToApply; s; s = s->outer) - { - auto appGenericSubst = as(s); - if(!appGenericSubst) - continue; - - if(appGenericSubst->genericDecl != ancestorGenericDecl) - continue; - - // The substitutions we are applying are trying - // to specialize this generic, but we don't already - // have a generic substitution in place. - // We will need to create one. - - int diff = 0; - auto restSubst = specializeSubstitutions( - astBuilder, - ancestorGenericDecl->parentDecl, - substsToSpecialize, - substsToApply, - &diff); - - RefPtr firstSubst = astBuilder->create(); - firstSubst->genericDecl = ancestorGenericDecl; - firstSubst->args = appGenericSubst->args; - firstSubst->outer = restSubst; - - (*ioDiff)++; - return firstSubst; - } - } - else if(auto ancestorInterfaceDecl = as(ancestorDecl)) - { - // The task is basically the same as for the generic case: - // We want to see if there is any existing substitution that - // applies to this declaration, and use that if possible. - - // The declaration is nested inside a generic. - // Does it already have a specialization for that generic? - if(auto specThisTypeSubst = as(substsToSpecialize)) - { - if(specThisTypeSubst->interfaceDecl == ancestorInterfaceDecl) - { - // Yes. We have an existing specialization, so we will - // keep one matching it in place. - int diff = 0; - auto restSubst = specializeSubstitutions( - astBuilder, - ancestorInterfaceDecl->parentDecl, - specThisTypeSubst->outer, - substsToApply, - &diff); - - auto firstSubst = specializeSubstitutionsShallow( - astBuilder, - specThisTypeSubst, - substsToApply, - restSubst, - &diff); - - *ioDiff += diff; - return firstSubst; - } - } - - // Otherwise, check if we are trying to apply - // a this-type substitution to the given interface - // - for(auto s = substsToApply; s; s = s->outer) - { - auto appThisTypeSubst = s.as(); - if(!appThisTypeSubst) - continue; - - if(appThisTypeSubst->interfaceDecl != ancestorInterfaceDecl) - continue; - - int diff = 0; - auto restSubst = specializeSubstitutions( - astBuilder, - ancestorInterfaceDecl->parentDecl, - substsToSpecialize, - substsToApply, - &diff); - - RefPtr firstSubst = astBuilder->create(); - firstSubst->interfaceDecl = ancestorInterfaceDecl; - firstSubst->witness = appThisTypeSubst->witness; - firstSubst->outer = restSubst; - - (*ioDiff)++; - return firstSubst; - } - } - } - - // If we reach here then we've walked the full hierarchy up from - // `declToSpecialize` and either didn't run into an generic/interface - // declarations, or we didn't find any attempt to specialize them - // in either substitution. - // - // As an invariant, there should *not* be any generic or this-type - // substitutions in `substToSpecialize`, because otherwise they - // would be specializations that don't actually apply to the given - // declaration. - // - // The remaining substitutions to apply, if any, should thus be - // global-generic substitutions. And similarly, those are the - // only remaining substitutions we really care about in - // `substsToApply`. - // - // Note: this does *not* mean that `substsToApply` doesn't have - // any generic or this-type substitutions; it just means that none - // of them were applicable. - // - return specializeGlobalGenericSubstitutions( - astBuilder, - declToSpecialize, - substsToSpecialize, - substsToApply, - ioDiff); - } - - DeclRefBase DeclRefBase::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet substSet, int* ioDiff) - { - // Nothing to do when we have no declaration. - if(!decl) - return *this; - - // Apply the given substitutions to any specializations - // that have already been applied to this declaration. - int diff = 0; - - auto substSubst = specializeSubstitutions( - astBuilder, - decl, - substitutions.substitutions, - substSet.substitutions, - &diff); - - if (!diff) - return *this; - - *ioDiff += diff; - - DeclRefBase substDeclRef; - substDeclRef.decl = decl; - substDeclRef.substitutions = substSubst; - - // TODO: The old code here used to try to translate a decl-ref - // to an associated type in a decl-ref for the concrete type - // in a particular implementation. - // - // I have only kept that logic in `DeclRefType::SubstituteImpl`, - // but it may turn out it is needed here too. - - return substDeclRef; - } - - - // Check if this is an equivalent declaration reference to another - bool DeclRefBase::equals(DeclRefBase const& declRef) const - { - if (decl != declRef.decl) - return false; - if (!substitutions.equals(declRef.substitutions)) - return false; - - return true; - } - - // Convenience accessors for common properties of declarations - Name* DeclRefBase::getName() const - { - return decl->nameAndLoc.name; - } - - SourceLoc DeclRefBase::getLoc() const - { - return decl->loc; - } - - DeclRefBase DeclRefBase::getParent() const - { - // Want access to the free function (the 'as' method by default gets priority) - // Can access as method with this->as because it removes any ambiguity. - using Slang::as; - - auto parentDecl = decl->parentDecl; - if (!parentDecl) - return DeclRefBase(); - - // Default is to apply the same set of substitutions/specializations - // to the parent declaration as were applied to the child. - RefPtr substToApply = substitutions.substitutions; - - if(auto interfaceDecl = as(decl)) - { - // The declaration being referenced is an `interface` declaration, - // and there might be a this-type substitution in place. - // A reference to the parent of the interface declaration - // should not include that substitution. - if(auto thisTypeSubst = as(substToApply)) - { - if(thisTypeSubst->interfaceDecl == interfaceDecl) - { - // Strip away that specializations that apply to the interface. - substToApply = thisTypeSubst->outer; - } - } - } - - if (auto parentGenericDecl = as(parentDecl)) - { - // The parent of this declaration is a generic, which means - // that the decl-ref to the current declaration might include - // substitutions that specialize the generic parameters. - // A decl-ref to the parent generic should *not* include - // those substitutions. - // - if(auto genericSubst = as(substToApply)) - { - if(genericSubst->genericDecl == parentGenericDecl) - { - // Strip away the specializations that were applied to the parent. - substToApply = genericSubst->outer; - } - } - } - - return DeclRefBase(parentDecl, substToApply); - } - - HashCode DeclRefBase::getHashCode() const - { - return combineHash(PointerHash<1>::getHashCode(decl), substitutions.getHashCode()); - } - - // Val - - RefPtr Val::substitute(ASTBuilder* astBuilder, SubstitutionSet subst) - { - if (!subst) return this; - int diff = 0; - return substituteImpl(astBuilder, subst, &diff); - } - - RefPtr Val::substituteImpl(ASTBuilder* /* astBuilder */, SubstitutionSet /*subst*/, int* /*ioDiff*/) - { - // Default behavior is to not substitute at all - return this; - } - - // IntVal - - IntegerLiteralValue getIntVal(RefPtr val) - { - if (auto constantVal = as(val)) - { - return constantVal->value; - } - SLANG_UNEXPECTED("needed a known integer value"); - return 0; - } - - // ConstantIntVal - - bool ConstantIntVal::equalsVal(Val* val) - { - if (auto intVal = as(val)) - return value == intVal->value; - return false; - } - - String ConstantIntVal::toString() - { - return String(value); - } - - HashCode ConstantIntVal::getHashCode() - { - return (HashCode) value; - } - - - - // - - // HLSLPatchType - - Type* HLSLPatchType::getElementType() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[0]); - } - - IntVal* HLSLPatchType::getElementCount() - { - return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[1]); - } - - // Constructors for types - - RefPtr getArrayType( - ASTBuilder* astBuilder, - Type* elementType, - IntVal* elementCount) - { - auto arrayType = astBuilder->create(); - arrayType->baseType = elementType; - arrayType->arrayLength = elementCount; - return arrayType; - } - - RefPtr getArrayType( - ASTBuilder* astBuilder, - Type* elementType) - { - auto arrayType = astBuilder->create(); - arrayType->baseType = elementType; - return arrayType; - } - - RefPtr getNamedType( - ASTBuilder* astBuilder, - DeclRef const& declRef) - { - DeclRef specializedDeclRef = createDefaultSubstitutionsIfNeeded(astBuilder, declRef).as(); - - return astBuilder->create(specializedDeclRef); - } - - - RefPtr getFuncType( - ASTBuilder* astBuilder, - DeclRef const& declRef) - { - RefPtr funcType = astBuilder->create(); - - funcType->resultType = getResultType(astBuilder, declRef); - for (auto paramDeclRef : getParameters(declRef)) - { - auto paramDecl = paramDeclRef.getDecl(); - auto paramType = getType(astBuilder, paramDeclRef); - if( paramDecl->findModifier() ) - { - paramType = astBuilder->getRefType(paramType); - } - else if( paramDecl->findModifier() ) - { - if(paramDecl->findModifier() || paramDecl->findModifier()) - { - paramType = astBuilder->getInOutType(paramType); - } - else - { - paramType = astBuilder->getOutType(paramType); - } - } - funcType->paramTypes.add(paramType); - } - - return funcType; - } - - RefPtr getGenericDeclRefType( - ASTBuilder* astBuilder, - DeclRef const& declRef) - { - return astBuilder->create(declRef); - } - - RefPtr getNamespaceType( - ASTBuilder* astBuilder, - DeclRef const& declRef) - { - auto type = astBuilder->create(); - type->declRef = declRef; - return type; - } - - RefPtr getSamplerStateType( - ASTBuilder* astBuilder) - { - return astBuilder->create(); - } - - // TODO: should really have a `type.cpp` and a `witness.cpp` - - bool TypeEqualityWitness::equalsVal(Val* val) - { - auto otherWitness = as(val); - if (!otherWitness) - return false; - return sub->equals(otherWitness->sub); - } - - RefPtr TypeEqualityWitness::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int * ioDiff) - { - RefPtr rs = astBuilder->create(); - rs->sub = sub->substituteImpl(astBuilder, subst, ioDiff).as(); - rs->sup = sup->substituteImpl(astBuilder, subst, ioDiff).as(); - return rs; - } - - String TypeEqualityWitness::toString() - { - return "TypeEqualityWitness(" + sub->toString() + ")"; - } - - HashCode TypeEqualityWitness::getHashCode() - { - return sub->getHashCode(); - } - - bool DeclaredSubtypeWitness::equalsVal(Val* val) - { - auto otherWitness = as(val); - if(!otherWitness) - return false; - - return sub->equals(otherWitness->sub) - && sup->equals(otherWitness->sup) - && declRef.equals(otherWitness->declRef); - } - - RefPtr findThisTypeSubstitution( - Substitutions* substs, - InterfaceDecl* interfaceDecl) - { - for(RefPtr s = substs; s; s = s->outer) - { - auto thisTypeSubst = as(s); - if(!thisTypeSubst) - continue; - - if(thisTypeSubst->interfaceDecl != interfaceDecl) - continue; - - return thisTypeSubst; - } - - return nullptr; - } - - RefPtr DeclaredSubtypeWitness::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int * ioDiff) - { - if (auto genConstraintDeclRef = declRef.as()) - { - auto genConstraintDecl = genConstraintDeclRef.getDecl(); - - // search for a substitution that might apply to us - for(auto s = subst.substitutions; s; s = s->outer) + if(auto ancestorGenericDecl = as(ancestorDecl)) { - if(auto genericSubst = as(s)) + // The declaration is nested inside a generic. + // Does it already have a specialization for that generic? + if(auto specGenericSubst = as(substsToSpecialize)) { - // the generic decl associated with the substitution list must be - // the generic decl that declared this parameter - auto genericDecl = genericSubst->genericDecl; - if (genericDecl != genConstraintDecl->parentDecl) - continue; - - bool found = false; - Index index = 0; - for (auto m : genericDecl->members) - { - if (auto constraintParam = as(m)) - { - if (constraintParam == declRef.getDecl()) - { - found = true; - break; - } - index++; - } - } - if (found) + if(specGenericSubst->genericDecl == ancestorGenericDecl) { - (*ioDiff)++; - auto ordinaryParamCount = genericDecl->getMembersOfType().getCount() + - genericDecl->getMembersOfType().getCount(); - SLANG_ASSERT(index + ordinaryParamCount < genericSubst->args.getCount()); - return genericSubst->args[index + ordinaryParamCount]; + // Yes. We have an existing specialization, so we will + // keep one matching it in place. + int diff = 0; + auto restSubst = specializeSubstitutions( + astBuilder, + ancestorGenericDecl->parentDecl, + specGenericSubst->outer, + substsToApply, + &diff); + + auto firstSubst = specializeSubstitutionsShallow( + astBuilder, + specGenericSubst, + substsToApply, + restSubst, + &diff); + + *ioDiff += diff; + return firstSubst; } } - else if(auto globalGenericSubst = s.as()) + + // If the declaration is not already specialized + // for the given generic, then see if we are trying + // to *apply* such specializations to it. + // + // TODO: The way we handle things right now with + // "default" specializations, this case shouldn't + // actually come up. + // + for(auto s = substsToApply; s; s = s->outer) { - // check if the substitution is really about this global generic type parameter - if (globalGenericSubst->paramDecl != genConstraintDecl->parentDecl) + auto appGenericSubst = as(s); + if(!appGenericSubst) continue; - for(auto constraintArg : globalGenericSubst->constraintArgs) - { - if(constraintArg.decl.Ptr() != genConstraintDecl) - continue; + if(appGenericSubst->genericDecl != ancestorGenericDecl) + continue; - (*ioDiff)++; - return constraintArg.val; - } - } - } - } + // The substitutions we are applying are trying + // to specialize this generic, but we don't already + // have a generic substitution in place. + // We will need to create one. - // Perform substitution on the constituent elements. - int diff = 0; - auto substSub = sub->substituteImpl(astBuilder, subst, &diff).as(); - auto substSup = sup->substituteImpl(astBuilder, subst, &diff).as(); - auto substDeclRef = declRef.substituteImpl(astBuilder, subst, &diff); - if (!diff) - return this; + int diff = 0; + auto restSubst = specializeSubstitutions( + astBuilder, + ancestorGenericDecl->parentDecl, + substsToSpecialize, + substsToApply, + &diff); - (*ioDiff)++; + RefPtr firstSubst = astBuilder->create(); + firstSubst->genericDecl = ancestorGenericDecl; + firstSubst->args = appGenericSubst->args; + firstSubst->outer = restSubst; - // If we have a reference to a type constraint for an - // associated type declaration, then we can replace it - // with the concrete conformance witness for a concrete - // type implementing the outer interface. - // - // TODO: It is a bit gross that we use `GenericTypeConstraintDecl` for - // associated types, when they aren't really generic type *parameters*, - // so we'll need to change this location in the code if we ever clean - // up the hierarchy. - // - if (auto substTypeConstraintDecl = as(substDeclRef.decl)) - { - if (auto substAssocTypeDecl = as(substTypeConstraintDecl->parentDecl)) + (*ioDiff)++; + return firstSubst; + } + } + else if(auto ancestorInterfaceDecl = as(ancestorDecl)) { - if (auto interfaceDecl = as(substAssocTypeDecl->parentDecl)) + // The task is basically the same as for the generic case: + // We want to see if there is any existing substitution that + // applies to this declaration, and use that if possible. + + // The declaration is nested inside a generic. + // Does it already have a specialization for that generic? + if(auto specThisTypeSubst = as(substsToSpecialize)) { - // At this point we have a constraint decl for an associated type, - // and we nee to see if we are dealing with a concrete substitution - // for the interface around that associated type. - if(auto thisTypeSubst = findThisTypeSubstitution(substDeclRef.substitutions, interfaceDecl)) + if(specThisTypeSubst->interfaceDecl == ancestorInterfaceDecl) { - // We need to look up the declaration that satisfies - // the requirement named by the associated type. - Decl* requirementKey = substTypeConstraintDecl; - RequirementWitness requirementWitness = tryLookUpRequirementWitness(astBuilder, thisTypeSubst->witness, requirementKey); - switch(requirementWitness.getFlavor()) - { - default: - break; - - case RequirementWitness::Flavor::val: - { - auto satisfyingVal = requirementWitness.getVal(); - return satisfyingVal; - } - } + // Yes. We have an existing specialization, so we will + // keep one matching it in place. + int diff = 0; + auto restSubst = specializeSubstitutions( + astBuilder, + ancestorInterfaceDecl->parentDecl, + specThisTypeSubst->outer, + substsToApply, + &diff); + + auto firstSubst = specializeSubstitutionsShallow( + astBuilder, + specThisTypeSubst, + substsToApply, + restSubst, + &diff); + + *ioDiff += diff; + return firstSubst; } } - } - } + // Otherwise, check if we are trying to apply + // a this-type substitution to the given interface + // + for(auto s = substsToApply; s; s = s->outer) + { + auto appThisTypeSubst = s.as(); + if(!appThisTypeSubst) + continue; + if(appThisTypeSubst->interfaceDecl != ancestorInterfaceDecl) + continue; + int diff = 0; + auto restSubst = specializeSubstitutions( + astBuilder, + ancestorInterfaceDecl->parentDecl, + substsToSpecialize, + substsToApply, + &diff); - RefPtr rs = astBuilder->create(); - rs->sub = substSub; - rs->sup = substSup; - rs->declRef = substDeclRef; - return rs; - } + RefPtr firstSubst = astBuilder->create(); + firstSubst->interfaceDecl = ancestorInterfaceDecl; + firstSubst->witness = appThisTypeSubst->witness; + firstSubst->outer = restSubst; - String DeclaredSubtypeWitness::toString() - { - StringBuilder sb; - sb << "DeclaredSubtypeWitness("; - sb << this->sub->toString(); - sb << ", "; - sb << this->sup->toString(); - sb << ", "; - sb << this->declRef.toString(); - sb << ")"; - return sb.ProduceString(); - } + (*ioDiff)++; + return firstSubst; + } + } + } - HashCode DeclaredSubtypeWitness::getHashCode() - { - return declRef.getHashCode(); + // If we reach here then we've walked the full hierarchy up from + // `declToSpecialize` and either didn't run into an generic/interface + // declarations, or we didn't find any attempt to specialize them + // in either substitution. + // + // As an invariant, there should *not* be any generic or this-type + // substitutions in `substToSpecialize`, because otherwise they + // would be specializations that don't actually apply to the given + // declaration. + // + // The remaining substitutions to apply, if any, should thus be + // global-generic substitutions. And similarly, those are the + // only remaining substitutions we really care about in + // `substsToApply`. + // + // Note: this does *not* mean that `substsToApply` doesn't have + // any generic or this-type substitutions; it just means that none + // of them were applicable. + // + return specializeGlobalGenericSubstitutions( + astBuilder, + declToSpecialize, + substsToSpecialize, + substsToApply, + ioDiff); } - // TransitiveSubtypeWitness - - bool TransitiveSubtypeWitness::equalsVal(Val* val) + DeclRefBase DeclRefBase::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet substSet, int* ioDiff) { - auto otherWitness = as(val); - if(!otherWitness) - return false; - - return sub->equals(otherWitness->sub) - && sup->equals(otherWitness->sup) - && subToMid->equalsVal(otherWitness->subToMid) - && midToSup.equals(otherWitness->midToSup); - } + // Nothing to do when we have no declaration. + if(!decl) + return *this; - RefPtr TransitiveSubtypeWitness::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int * ioDiff) - { + // Apply the given substitutions to any specializations + // that have already been applied to this declaration. int diff = 0; - RefPtr substSub = sub->substituteImpl(astBuilder, subst, &diff).as(); - RefPtr substSup = sup->substituteImpl(astBuilder, subst, &diff).as(); - RefPtr substSubToMid = subToMid->substituteImpl(astBuilder, subst, &diff).as(); - DeclRef substMidToSup = midToSup.substituteImpl(astBuilder, subst, &diff); + auto substSubst = specializeSubstitutions( + astBuilder, + decl, + substitutions.substitutions, + substSet.substitutions, + &diff); - // If nothing changed, then we can bail out early. if (!diff) - return this; + return *this; - // Something changes, so let the caller know. - (*ioDiff)++; + *ioDiff += diff; - // TODO: are there cases where we can simplify? - // - // In principle, if either `subToMid` or `midToSub` turns into - // a reflexive subtype witness, then we could drop that side, - // and just return the other one (this would imply that `sub == mid` - // or `mid == sup` after substitutions). - // - // In the long run, is it also possible that if `sub` gets resolved - // to a concrete type *and* we decide to flatten out the inheritance - // graph into a linearized "class precedence list" stored in any - // aggregate type, then we could potentially just redirect to point - // to the appropriate inheritance decl in the original type. + DeclRefBase substDeclRef; + substDeclRef.decl = decl; + substDeclRef.substitutions = substSubst; + + // TODO: The old code here used to try to translate a decl-ref + // to an associated type in a decl-ref for the concrete type + // in a particular implementation. // - // For now I'm going to ignore those possibilities and hope for the best. - - // In the simple case, we just construct a new transitive subtype - // witness, and we move on with life. - RefPtr result = astBuilder->create(); - result->sub = substSub; - result->sup = substSup; - result->subToMid = substSubToMid; - result->midToSup = substMidToSup; - return result; + // I have only kept that logic in `DeclRefType::SubstituteImpl`, + // but it may turn out it is needed here too. + + return substDeclRef; } - String TransitiveSubtypeWitness::toString() + + // Check if this is an equivalent declaration reference to another + bool DeclRefBase::equals(DeclRefBase const& declRef) const { - // Note: we only print the constituent - // witnesses, and rely on them to print - // the starting and ending types. - StringBuilder sb; - sb << "TransitiveSubtypeWitness("; - sb << this->subToMid->toString(); - sb << ", "; - sb << this->midToSup.toString(); - sb << ")"; - return sb.ProduceString(); + if (decl != declRef.decl) + return false; + if (!substitutions.equals(declRef.substitutions)) + return false; + + return true; } - HashCode TransitiveSubtypeWitness::getHashCode() + // Convenience accessors for common properties of declarations + Name* DeclRefBase::getName() const { - auto hash = sub->getHashCode(); - hash = combineHash(hash, sup->getHashCode()); - hash = combineHash(hash, subToMid->getHashCode()); - hash = combineHash(hash, midToSup.getHashCode()); - return hash; + return decl->nameAndLoc.name; } - // + SourceLoc DeclRefBase::getLoc() const + { + return decl->loc; + } - String DeclRefBase::toString() const + DeclRefBase DeclRefBase::getParent() const { - if (!decl) return ""; + // Want access to the free function (the 'as' method by default gets priority) + // Can access as method with this->as because it removes any ambiguity. + using Slang::as; - auto name = decl->getName(); - if (!name) return ""; + auto parentDecl = decl->parentDecl; + if (!parentDecl) + return DeclRefBase(); - // TODO: need to print out substitutions too! - return name->text; - } + // Default is to apply the same set of substitutions/specializations + // to the parent declaration as were applied to the child. + RefPtr substToApply = substitutions.substitutions; - bool SubstitutionSet::equals(const SubstitutionSet& substSet) const - { - if (substitutions == substSet.substitutions) + if(auto interfaceDecl = as(decl)) { - return true; + // The declaration being referenced is an `interface` declaration, + // and there might be a this-type substitution in place. + // A reference to the parent of the interface declaration + // should not include that substitution. + if(auto thisTypeSubst = as(substToApply)) + { + if(thisTypeSubst->interfaceDecl == interfaceDecl) + { + // Strip away that specializations that apply to the interface. + substToApply = thisTypeSubst->outer; + } + } } - if (substitutions == nullptr || substSet.substitutions == nullptr) + + if (auto parentGenericDecl = as(parentDecl)) { - return false; + // The parent of this declaration is a generic, which means + // that the decl-ref to the current declaration might include + // substitutions that specialize the generic parameters. + // A decl-ref to the parent generic should *not* include + // those substitutions. + // + if(auto genericSubst = as(substToApply)) + { + if(genericSubst->genericDecl == parentGenericDecl) + { + // Strip away the specializations that were applied to the parent. + substToApply = genericSubst->outer; + } + } } - return substitutions->equals(substSet.substitutions); - } - HashCode SubstitutionSet::getHashCode() const - { - HashCode rs = 0; - if (substitutions) - rs = combineHash(rs, substitutions->getHashCode()); - return rs; + return DeclRefBase(parentDecl, substToApply); } - // ExtractExistentialType - - String ExtractExistentialType::toString() + HashCode DeclRefBase::getHashCode() const { - String result; - result.append(declRef.toString()); - result.append(".This"); - return result; + return combineHash(PointerHash<1>::getHashCode(decl), substitutions.getHashCode()); } - bool ExtractExistentialType::equalsImpl(Type* type) + // IntVal + + IntegerLiteralValue getIntVal(RefPtr val) { - if( auto extractExistential = as(type) ) + if (auto constantVal = as(val)) { - return declRef.equals(extractExistential->declRef); + return constantVal->value; } - return false; + SLANG_UNEXPECTED("needed a known integer value"); + return 0; } - HashCode ExtractExistentialType::getHashCode() - { - return declRef.getHashCode(); - } + // + + // HLSLPatchType - RefPtr ExtractExistentialType::createCanonicalType() + Type* HLSLPatchType::getElementType() { - return this; + return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[0]); } - RefPtr ExtractExistentialType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) + IntVal* HLSLPatchType::getElementCount() { - int diff = 0; - auto substDeclRef = declRef.substituteImpl(astBuilder, subst, &diff); - if(!diff) - return this; - - (*ioDiff)++; - - RefPtr substValue = astBuilder->create(); - substValue->declRef = declRef; - return substValue; + return as(findInnerMostGenericSubstitution(declRef.substitutions)->args[1]); } - // ExtractExistentialSubtypeWitness + // Constructors for types - bool ExtractExistentialSubtypeWitness::equalsVal(Val* val) + RefPtr getArrayType( + ASTBuilder* astBuilder, + Type* elementType, + IntVal* elementCount) { - if( auto extractWitness = as(val) ) - { - return declRef.equals(extractWitness->declRef); - } - return false; + auto arrayType = astBuilder->create(); + arrayType->baseType = elementType; + arrayType->arrayLength = elementCount; + return arrayType; } - String ExtractExistentialSubtypeWitness::toString() + RefPtr getArrayType( + ASTBuilder* astBuilder, + Type* elementType) { - String result; - result.append("extractExistentialValue("); - result.append(declRef.toString()); - result.append(")"); - return result; + auto arrayType = astBuilder->create(); + arrayType->baseType = elementType; + return arrayType; } - HashCode ExtractExistentialSubtypeWitness::getHashCode() + RefPtr getNamedType( + ASTBuilder* astBuilder, + DeclRef const& declRef) { - return declRef.getHashCode(); + DeclRef specializedDeclRef = createDefaultSubstitutionsIfNeeded(astBuilder, declRef).as(); + + return astBuilder->create(specializedDeclRef); } - RefPtr ExtractExistentialSubtypeWitness::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) + + RefPtr getFuncType( + ASTBuilder* astBuilder, + DeclRef const& declRef) { - int diff = 0; - - auto substDeclRef = declRef.substituteImpl(astBuilder, subst, &diff); - auto substSub = sub->substituteImpl(astBuilder, subst, &diff).as(); - auto substSup = sup->substituteImpl(astBuilder, subst, &diff).as(); - - if(!diff) - return this; + RefPtr funcType = astBuilder->create(); - (*ioDiff)++; + funcType->resultType = getResultType(astBuilder, declRef); + for (auto paramDeclRef : getParameters(declRef)) + { + auto paramDecl = paramDeclRef.getDecl(); + auto paramType = getType(astBuilder, paramDeclRef); + if( paramDecl->findModifier() ) + { + paramType = astBuilder->getRefType(paramType); + } + else if( paramDecl->findModifier() ) + { + if(paramDecl->findModifier() || paramDecl->findModifier()) + { + paramType = astBuilder->getInOutType(paramType); + } + else + { + paramType = astBuilder->getOutType(paramType); + } + } + funcType->paramTypes.add(paramType); + } - RefPtr substValue = astBuilder->create(); - substValue->declRef = declRef; - substValue->sub = substSub; - substValue->sup = substSup; - return substValue; + return funcType; } - // - // TaggedUnionType - // - - String TaggedUnionType::toString() + RefPtr getGenericDeclRefType( + ASTBuilder* astBuilder, + DeclRef const& declRef) { - String result; - result.append("__TaggedUnion("); - bool first = true; - for( auto caseType : caseTypes ) - { - if(!first) result.append(", "); - first = false; - - result.append(caseType->toString()); - } - result.append(")"); - return result; + return astBuilder->create(declRef); } - bool TaggedUnionType::equalsImpl(Type* type) + RefPtr getNamespaceType( + ASTBuilder* astBuilder, + DeclRef const& declRef) { - auto taggedUnion = as(type); - if(!taggedUnion) - return false; - - auto caseCount = caseTypes.getCount(); - if(caseCount != taggedUnion->caseTypes.getCount()) - return false; - - for( Index ii = 0; ii < caseCount; ++ii ) - { - if(!caseTypes[ii]->equals(taggedUnion->caseTypes[ii])) - return false; - } - return true; + auto type = astBuilder->create(); + type->declRef = declRef; + return type; } - HashCode TaggedUnionType::getHashCode() + RefPtr getSamplerStateType( + ASTBuilder* astBuilder) { - HashCode hashCode = 0; - for( auto caseType : caseTypes ) - { - hashCode = combineHash(hashCode, caseType->getHashCode()); - } - return hashCode; + return astBuilder->create(); } - RefPtr TaggedUnionType::createCanonicalType() + RefPtr findThisTypeSubstitution( + Substitutions* substs, + InterfaceDecl* interfaceDecl) { - RefPtr canType = m_astBuilder->create(); - - for( auto caseType : caseTypes ) + for(RefPtr s = substs; s; s = s->outer) { - auto canCaseType = caseType->getCanonicalType(); - canType->caseTypes.add(canCaseType); - } - - return canType; - } + auto thisTypeSubst = as(s); + if(!thisTypeSubst) + continue; - RefPtr TaggedUnionType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) - { - int diff = 0; + if(thisTypeSubst->interfaceDecl != interfaceDecl) + continue; - List> substCaseTypes; - for( auto caseType : caseTypes ) - { - substCaseTypes.add(caseType->substituteImpl(astBuilder, subst, &diff).as()); + return thisTypeSubst; } - if(!diff) - return this; - - (*ioDiff)++; - RefPtr substType = astBuilder->create(); - substType->caseTypes.swapWith(substCaseTypes); - return substType; + return nullptr; } -// -// TaggedUnionSubtypeWitness -// - - -bool TaggedUnionSubtypeWitness::equalsVal(Val* val) -{ - auto taggedUnionWitness = as(val); - if(!taggedUnionWitness) - return false; - - auto caseCount = caseWitnesses.getCount(); - if(caseCount != taggedUnionWitness->caseWitnesses.getCount()) - return false; + // - for(Index ii = 0; ii < caseCount; ++ii) + String DeclRefBase::toString() const { - if(!caseWitnesses[ii]->equalsVal(taggedUnionWitness->caseWitnesses[ii])) - return false; - } - - return true; -} + if (!decl) return ""; -String TaggedUnionSubtypeWitness::toString() -{ - String result; - result.append("TaggedUnionSubtypeWitness("); - bool first = true; - for( auto caseWitness : caseWitnesses ) - { - if(!first) result.append(", "); - first = false; + auto name = decl->getName(); + if (!name) return ""; - result.append(caseWitness->toString()); + // TODO: need to print out substitutions too! + return name->text; } - return result; -} -HashCode TaggedUnionSubtypeWitness::getHashCode() -{ - HashCode hash = 0; - for( auto caseWitness : caseWitnesses ) + bool SubstitutionSet::equals(const SubstitutionSet& substSet) const { - hash = combineHash(hash, caseWitness->getHashCode()); + if (substitutions == substSet.substitutions) + { + return true; + } + if (substitutions == nullptr || substSet.substitutions == nullptr) + { + return false; + } + return substitutions->equals(substSet.substitutions); } - return hash; -} -RefPtr TaggedUnionSubtypeWitness::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) -{ - int diff = 0; - - auto substSub = sub->substituteImpl(astBuilder, subst, &diff).as(); - auto substSup = sup->substituteImpl(astBuilder, subst, &diff).as(); - - List> substCaseWitnesses; - for( auto caseWitness : caseWitnesses ) + HashCode SubstitutionSet::getHashCode() const { - substCaseWitnesses.add(caseWitness->substituteImpl(astBuilder, subst, &diff)); + HashCode rs = 0; + if (substitutions) + rs = combineHash(rs, substitutions->getHashCode()); + return rs; } - if(!diff) - return this; - - (*ioDiff)++; - - RefPtr substWitness = astBuilder->create(); - substWitness->sub = substSub; - substWitness->sup = substSup; - substWitness->caseWitnesses.swapWith(substCaseWitnesses); - return substWitness; -} - + + Module* getModule(Decl* decl) { for( auto dd = decl; dd; dd = dd->parentDecl ) @@ -2698,184 +1288,4 @@ char const* getGLSLNameForImageFormat(ImageFormat format) } } -// -// ExistentialSpecializedType -// - -String ExistentialSpecializedType::toString() -{ - String result; - result.append("__ExistentialSpecializedType("); - result.append(baseType->toString()); - for( auto arg : args ) - { - result.append(", "); - result.append(arg.val->toString()); - } - result.append(")"); - return result; -} - -bool ExistentialSpecializedType::equalsImpl(Type * type) -{ - auto other = as(type); - if(!other) - return false; - - if(!baseType->equals(other->baseType)) - return false; - - auto argCount = args.getCount(); - if(argCount != other->args.getCount()) - return false; - - for( Index ii = 0; ii < argCount; ++ii ) - { - auto arg = args[ii]; - auto otherArg = other->args[ii]; - - if(!arg.val->equalsVal(otherArg.val)) - return false; - - if(!areValsEqual(arg.witness, otherArg.witness)) - return false; - } - return true; -} - -HashCode ExistentialSpecializedType::getHashCode() -{ - Hasher hasher; - hasher.hashObject(baseType); - for(auto arg : args) - { - hasher.hashObject(arg.val); - if(auto witness = arg.witness) - hasher.hashObject(witness); - } - return hasher.getResult(); -} - -RefPtr getCanonicalValue(Val* val) -{ - if(!val) - return nullptr; - if(auto type = as(val)) - { - return type->getCanonicalType(); - } - // TODO: We may eventually need/want some sort of canonicalization - // for non-type values, but for now there is nothing to do. - return val; -} - -RefPtr ExistentialSpecializedType::createCanonicalType() -{ - RefPtr canType = m_astBuilder->create(); - - canType->baseType = baseType->getCanonicalType(); - for( auto arg : args ) - { - ExpandedSpecializationArg canArg; - canArg.val = getCanonicalValue(arg.val); - canArg.witness = getCanonicalValue(arg.witness); - canType->args.add(canArg); - } - return canType; -} - -RefPtr substituteImpl(ASTBuilder* astBuilder, Val* val, SubstitutionSet subst, int* ioDiff) -{ - if(!val) return nullptr; - return val->substituteImpl(astBuilder, subst, ioDiff); -} - -RefPtr ExistentialSpecializedType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) -{ - int diff = 0; - - auto substBaseType = baseType->substituteImpl(astBuilder, subst, &diff).as(); - - ExpandedSpecializationArgs substArgs; - for( auto arg : args ) - { - ExpandedSpecializationArg substArg; - substArg.val = Slang::substituteImpl(astBuilder, arg.val, subst, &diff); - substArg.witness = Slang::substituteImpl(astBuilder, arg.witness, subst, &diff); - substArgs.add(substArg); - } - - if(!diff) - return this; - - (*ioDiff)++; - - RefPtr substType = astBuilder->create(); - substType->baseType = substBaseType; - substType->args = substArgs; - return substType; -} - -// -// ThisType -// - -String ThisType::toString() -{ - String result; - result.append(interfaceDeclRef.toString()); - result.append(".This"); - return result; -} - -bool ThisType::equalsImpl(Type * type) -{ - auto other = as(type); - if(!other) - return false; - - if(!interfaceDeclRef.equals(other->interfaceDeclRef)) - return false; - - return true; -} - -HashCode ThisType::getHashCode() -{ - return combineHash( - HashCode(typeid(*this).hash_code()), - interfaceDeclRef.getHashCode()); -} - -RefPtr ThisType::createCanonicalType() -{ - RefPtr canType = m_astBuilder->create(); - - // TODO: need to canonicalize the decl-ref - canType->interfaceDeclRef = interfaceDeclRef; - return canType; -} - -RefPtr ThisType::substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff) -{ - int diff = 0; - - auto substInterfaceDeclRef = interfaceDeclRef.substituteImpl(astBuilder, subst, &diff); - - auto thisTypeSubst = findThisTypeSubstitution(subst.substitutions, substInterfaceDeclRef.getDecl()); - if( thisTypeSubst ) - { - return thisTypeSubst->witness->sub; - } - - if(!diff) - return this; - - (*ioDiff)++; - - RefPtr substType = m_astBuilder->create(); - substType->interfaceDeclRef = substInterfaceDeclRef; - return substType; -} - } // namespace Slang -- cgit v1.2.3