summaryrefslogtreecommitdiffstats
path: root/source/slang/check.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/check.cpp')
-rw-r--r--source/slang/check.cpp971
1 files changed, 662 insertions, 309 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index eb15d0889..67b628596 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -168,64 +168,54 @@ namespace Slang
RefPtr<Expr> baseExpr,
SourceLoc loc)
{
+ // Compute the type that this declaration reference will have in context.
+ //
+ auto type = GetTypeForDeclRef(declRef);
+
+ // Construct an appropriate expression based on teh structured of
+ // the declaration reference.
+ //
if (baseExpr)
{
- RefPtr<Expr> expr;
- DeclRef<Decl> *declRefOut;
+ // If there was a base expression, we will have some kind of
+ // member expression.
+ //
if (baseExpr->type->As<TypeType>())
{
- auto sexpr = new StaticMemberExpr();
- sexpr->loc = loc;
- sexpr->BaseExpression = baseExpr;
- sexpr->name = declRef.GetName();
- sexpr->declRef = declRef;
- declRefOut = &sexpr->declRef;
- expr = sexpr;
+ // If the base expression was a type, then that means we
+ // are constructing a static member reference.
+ //
+ auto expr = new StaticMemberExpr();
+ expr->loc = loc;
+ expr->type = type;
+ expr->BaseExpression = baseExpr;
+ expr->name = declRef.GetName();
+ expr->declRef = declRef;
+ return expr;
}
else
{
- auto sexpr = new MemberExpr();
- sexpr->loc = loc;
- sexpr->BaseExpression = baseExpr;
- sexpr->name = declRef.GetName();
- sexpr->declRef = declRef;
- declRefOut = &sexpr->declRef;
- expr = sexpr;
- }
-
- RefPtr<ThisTypeSubstitution> baseThisTypeSubst;
- if (auto baseDeclRefExpr = baseExpr->As<DeclRefExpr>())
- {
- baseThisTypeSubst = getThisTypeSubst(baseDeclRefExpr->declRef, false);
- }
- if (declRef.As<TypeConstraintDecl>())
- {
- // if this is a reference to type constraint, insert a this-type substitution
- RefPtr<Type> expType;
- expType = baseExpr->type;
- if (auto baseExprTT = baseExpr->type->As<TypeType>())
- expType = baseExprTT->type;
- auto thisTypeSubst = getNewThisTypeSubst(*declRefOut);
- thisTypeSubst->sourceType = expType;
- baseThisTypeSubst = nullptr;
- }
- // propagate "this-type" substitutions
- if (baseThisTypeSubst)
- {
- if (auto declRefExpr = expr.As<DeclRefExpr>())
- {
- getNewThisTypeSubst(declRefExpr->declRef)->sourceType = baseThisTypeSubst->sourceType;
- }
+ // If the base expression wasn't a type, then this
+ // is a normal member expression.
+ //
+ auto expr = new MemberExpr();
+ expr->loc = loc;
+ expr->type = type;
+ expr->BaseExpression = baseExpr;
+ expr->name = declRef.GetName();
+ expr->declRef = declRef;
+ return expr;
}
- expr->type = GetTypeForDeclRef(*declRefOut);
- return expr;
}
else
{
+ // If there is no base expression, then the result must
+ // be an ordinary variable expression.
+ //
auto expr = new VarExpr();
expr->loc = loc;
expr->name = declRef.GetName();
- expr->type = GetTypeForDeclRef(declRef);
+ expr->type = type;
expr->declRef = declRef;
return expr;
}
@@ -444,12 +434,12 @@ namespace Slang
// The arguments should already be checked against
// the declaration.
RefPtr<Type> InstantiateGenericType(
- DeclRef<GenericDecl> genericDeclRef,
- List<RefPtr<Expr>> const& args)
+ DeclRef<GenericDecl> genericDeclRef,
+ List<RefPtr<Expr>> const& args)
{
RefPtr<GenericSubstitution> subst = new GenericSubstitution();
subst->genericDecl = genericDeclRef.getDecl();
- subst->outer = genericDeclRef.substitutions.genericSubstitutions;
+ subst->outer = genericDeclRef.substitutions.substitutions;
for (auto argExpr : args)
{
@@ -458,8 +448,7 @@ namespace Slang
DeclRef<Decl> innerDeclRef;
innerDeclRef.decl = GetInner(genericDeclRef);
- innerDeclRef.substitutions = SubstitutionSet(subst, genericDeclRef.substitutions.thisTypeSubstitution,
- genericDeclRef.substitutions.globalGenParamSubstitutions);
+ innerDeclRef.substitutions = SubstitutionSet(subst);
return DeclRefType::Create(
getSession(),
@@ -874,7 +863,7 @@ namespace Slang
auto arg = fromInitializerListExpr->args[argIndex++];
- //
+ //
RefPtr<Expr> coercedArg;
ConversionCost argCost;
@@ -1066,7 +1055,7 @@ namespace Slang
overloadContext.baseExpr = nullptr;
overloadContext.mode = OverloadResolveContext::Mode::JustTrying;
-
+
AddTypeOverloadCandidates(toType, overloadContext, toType);
if(overloadContext.bestCandidates.Count() != 0)
@@ -1821,7 +1810,7 @@ namespace Slang
for (int pass = 0; pass < 2; pass++)
{
checkingPhase = pass == 0 ? CheckingPhase::Header : CheckingPhase::Body;
-
+
for (auto & s : programNode->getMembersOfType<AggTypeDecl>())
{
checkDecl(s.Ptr());
@@ -1866,7 +1855,7 @@ namespace Slang
{
checkModifiers(d.Ptr());
}
-
+
if (pass == 0)
{
// now we can check all interface conformances
@@ -1896,20 +1885,22 @@ namespace Slang
}
bool doesSignatureMatchRequirement(
- DeclRef<CallableDecl> memberDecl,
+ DeclRef<CallableDecl> satisfyingMemberDeclRef,
DeclRef<CallableDecl> requiredMemberDeclRef,
- Dictionary<DeclRef<Decl>, DeclRef<Decl>> & requirementDict)
+ RefPtr<WitnessTable> witnessTable)
{
// TODO: actually implement matching here. For now we'll
// just pretend that things are satisfied in order to make progress..
- requirementDict.AddIfNotExists(requiredMemberDeclRef, memberDecl);
+ witnessTable->requirementDictionary.Add(
+ requiredMemberDeclRef.getDecl(),
+ RequirementWitness(satisfyingMemberDeclRef));
return true;
}
bool doesGenericSignatureMatchRequirement(
- DeclRef<GenericDecl> genDecl,
- DeclRef<GenericDecl> requirementGenDecl,
- Dictionary<DeclRef<Decl>, DeclRef<Decl>> & requirementDict)
+ DeclRef<GenericDecl> genDecl,
+ DeclRef<GenericDecl> requirementGenDecl,
+ RefPtr<WitnessTable> witnessTable)
{
if (genDecl.getDecl()->Members.Count() != requirementGenDecl.getDecl()->Members.Count())
return false;
@@ -1948,20 +1939,81 @@ namespace Slang
return false;
}
}
- return doesMemberSatisfyRequirement(DeclRef<Decl>(genDecl.getDecl()->inner.Ptr(), genDecl.substitutions),
+
+ // TODO: this isn't right, because we need to specialize the
+ // declarations of the generics to a common set of substitutions,
+ // so that their types are comparable (e.g., foo<T> and foo<U>
+ // need to have substutition applies so that they are both foo<X>,
+ // after which uses of the type X in their parameter lists can
+ // be compared).
+
+ return doesMemberSatisfyRequirement(
+ DeclRef<Decl>(genDecl.getDecl()->inner.Ptr(), genDecl.substitutions),
DeclRef<Decl>(requirementGenDecl.getDecl()->inner.Ptr(), requirementGenDecl.substitutions),
- requirementDict);
+ witnessTable);
+ }
+
+ bool doesTypeSatisfyAssociatedTypeRequirement(
+ RefPtr<Type> satisfyingType,
+ DeclRef<AssocTypeDecl> requiredAssociatedTypeDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ // We need to confirm that the chosen type `satisfyingType`,
+ // meets all the constraints placed on the associated type
+ // requirement `requiredAssociatedTypeDeclRef`.
+ //
+ // We will enumerate the type constraints placed on the
+ // associated type and see if they can be satisfied.
+ //
+ bool conformance = true;
+ for (auto requiredConstraintDeclRef : getMembersOfType<TypeConstraintDecl>(requiredAssociatedTypeDeclRef))
+ {
+ // Grab the type we expect to conform to from the constraint.
+ auto requiredSuperType = GetSup(requiredConstraintDeclRef);
+
+ // Perform a search for a witness to the subtype relationship.
+ auto witness = tryGetSubtypeWitness(satisfyingType, requiredSuperType);
+ if(witness)
+ {
+ // If a subtype witness was found, then the conformance
+ // appears to hold, and we can satisfy that requirement.
+ witnessTable->requirementDictionary.Add(requiredConstraintDeclRef, RequirementWitness(witness));
+ }
+ else
+ {
+ // If a witness couldn't be found, then the conformance
+ // seems like it will fail.
+ conformance = false;
+ }
+ }
+
+ // TODO: if any conformance check failed, we should probably include
+ // that in an error message produced about not satisfying the requirement.
+
+ if(conformance)
+ {
+ // If all the constraints were satsified, then the chosen
+ // type can indeed satisfy the interface requirement.
+ witnessTable->requirementDictionary.Add(
+ requiredAssociatedTypeDeclRef.getDecl(),
+ RequirementWitness(satisfyingType));
+ }
+
+ return conformance;
}
// Does the given `memberDecl` work as an implementation
// to satisfy the requirement `requiredMemberDeclRef`
// from an interface?
+ //
+ // If it does, then inserts a witness into `witnessTable`
+ // and returns `true`, otherwise returns `false`
bool doesMemberSatisfyRequirement(
- DeclRef<Decl> memberDeclRef,
- DeclRef<Decl> requiredMemberDeclRef,
- Dictionary<DeclRef<Decl>, DeclRef<Decl>> & requirementDictionary)
+ DeclRef<Decl> memberDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
{
- // At a high level, we want to chack that the
+ // At a high level, we want to check that the
// `memberDecl` and the `requiredMemberDeclRef`
// have the same AST node class, and then also
// check that their signatures match.
@@ -1979,34 +2031,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<ContainerDecl> subStructTypeDeclRef) -> bool
- {
- checkDecl(subStructTypeDeclRef.getDecl());
- // this is a sub type (e.g. nested struct declaration) in an aggregate type
- // check if this sub type declaration satisfies the constraints defined by the associated type
- if (auto requiredTypeDeclRef = requiredMemberDeclRef.As<AssocTypeDecl>())
- {
- bool conformance = true;
- auto inheritanceReqDeclRefs = getMembersOfType<TypeConstraintDecl>(requiredTypeDeclRef);
- for (auto inheritanceReqDeclRef : inheritanceReqDeclRefs)
- {
- auto interfaceDeclRefType = inheritanceReqDeclRef.getDecl()->getSup().type.As<DeclRefType>();
- SLANG_ASSERT(interfaceDeclRefType);
- auto interfaceDeclRef = interfaceDeclRefType->declRef.As<InterfaceDecl>();
- SLANG_ASSERT(interfaceDeclRef);
- RefPtr<DeclRefType> declRefType = new DeclRefType();
- declRefType->declRef = subStructTypeDeclRef;
- auto witness = tryGetInterfaceConformanceWitness(declRefType,
- interfaceDeclRef).As<SubtypeWitness>();
- if (witness)
- requirementDictionary.Add(inheritanceReqDeclRef, witness->getLastStepDeclRef());
- else
- conformance = false;
- }
- return conformance;
- }
- return false;
- };
+ //
if (auto memberFuncDecl = memberDeclRef.As<FuncDecl>())
{
if (auto requiredFuncDeclRef = requiredMemberDeclRef.As<FuncDecl>())
@@ -2015,7 +2040,7 @@ namespace Slang
return doesSignatureMatchRequirement(
memberFuncDecl,
requiredFuncDeclRef,
- requirementDictionary);
+ witnessTable);
}
}
else if (auto memberInitDecl = memberDeclRef.As<ConstructorDecl>())
@@ -2026,19 +2051,35 @@ namespace Slang
return doesSignatureMatchRequirement(
memberInitDecl,
requiredInitDecl,
- requirementDictionary);
+ witnessTable);
}
}
else if (auto genDecl = memberDeclRef.As<GenericDecl>())
{
+ // For a generic member, we will check if it can satisfy
+ // a generic requirement in the interface.
+ //
+ // TODO: we could also conceivably check that the generic
+ // could be *specialized* to satisfy the requirement,
+ // and then install a specialization of the generic into
+ // the witness table. Actually doing this would seem
+ // to require performing something akin to overload
+ // resolution as part of requirement satisfaction.
+ //
if (auto requiredGenDeclRef = requiredMemberDeclRef.As<GenericDecl>())
{
- return doesGenericSignatureMatchRequirement(genDecl, requiredGenDeclRef, requirementDictionary);
+ return doesGenericSignatureMatchRequirement(genDecl, requiredGenDeclRef, witnessTable);
}
}
- else if (auto subStructTypeDeclRef = memberDeclRef.As<AggTypeDecl>())
+ else if (auto subAggTypeDeclRef = memberDeclRef.As<AggTypeDecl>())
{
- return checkSubTypeMember(subStructTypeDeclRef);
+ if(auto requiredTypeDeclRef = requiredMemberDeclRef.As<AssocTypeDecl>())
+ {
+ checkDecl(subAggTypeDeclRef.getDecl());
+
+ auto satisfyingType = DeclRefType::Create(getSession(), subAggTypeDeclRef);
+ return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
+ }
}
else if (auto typedefDeclRef = memberDeclRef.As<TypeDefDecl>())
{
@@ -2046,28 +2087,25 @@ namespace Slang
// check if the specified type satisfies the constraints defined by the associated type
if (auto requiredTypeDeclRef = requiredMemberDeclRef.As<AssocTypeDecl>())
{
- auto declRefType = GetType(typedefDeclRef)->GetCanonicalType()->As<DeclRefType>();
- if (!declRefType)
- return false;
-
- if (auto genTypeParamDeclRef = declRefType->declRef.As<GenericTypeParamDecl>())
- {
- // TODO: check generic type parameter satisfies constraints
- return true;
- }
-
-
- auto containerDeclRef = declRefType->declRef.As<ContainerDecl>();
- if (!containerDeclRef)
- return false;
+ checkDecl(typedefDeclRef.getDecl());
- return checkSubTypeMember(containerDeclRef);
+ auto satisfyingType = getNamedType(getSession(), typedefDeclRef);
+ return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
}
}
// Default: just assume that thing aren't being satisfied.
return false;
}
+ // State used while checking if a declaration (either a type declaration
+ // or an extension of that type) conforms to the interfaces it claims
+ // via its inheritance clauses.
+ //
+ struct ConformanceCheckingContext
+ {
+ Dictionary<DeclRef<InterfaceDecl>, RefPtr<WitnessTable>> mapInterfaceToWitnessTable;
+ };
+
// Find the appropriate member of a declared type to
// satisfy a requirement of an interface the type
// claims to conform to.
@@ -2076,13 +2114,56 @@ namespace Slang
// conforms to the interface `interfaceDeclRef`, and
// `requiredMemberDeclRef` is a required member of
// the interface.
- RefPtr<Decl> findWitnessForInterfaceRequirement(
+ //
+ // If a satisfying value is found, registers it in
+ // `witnessTable` and returns `true`, otherwise
+ // returns `false`.
+ //
+ bool findWitnessForInterfaceRequirement(
+ ConformanceCheckingContext* context,
DeclRef<AggTypeDeclBase> typeDeclRef,
- InheritanceDecl* inheritanceDecl,
- DeclRef<InterfaceDecl> interfaceDeclRef,
- DeclRef<Decl> requiredMemberDeclRef,
- Dictionary<DeclRef<Decl>, DeclRef<Decl>> & requirementWitness)
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
{
+ // The goal of this function is to find a suitable
+ // value to satisfy the requirement.
+ //
+ // The 99% case is that the requirement is a named member
+ // of the interface, and we need to search for a member
+ // with the same name in the type declaration and
+ // its (known) extensions.
+
+ // An important exception to the above is that an
+ // inheritance declaration in the interface is not going
+ // to be satisfied by an inheritance declaration in the
+ // conforming type, but rather by a full "witness table"
+ // full of the satisfying values for each requirement
+ // in the inherited-from interface.
+ //
+ if( auto requiredInheritanceDeclRef = requiredMemberDeclRef.As<InheritanceDecl>() )
+ {
+ // Recursively check that the type conforms
+ // to the inherited interface.
+ //
+ // TODO: we *really* need a linearization step here!!!!
+
+ RefPtr<WitnessTable> satisfyingWitnessTable = checkConformanceToType(
+ context,
+ typeDeclRef,
+ requiredInheritanceDeclRef.getDecl(),
+ getBaseType(requiredInheritanceDeclRef));
+
+ if(!satisfyingWitnessTable)
+ return false;
+
+ witnessTable->requirementDictionary.Add(
+ requiredInheritanceDeclRef.getDecl(),
+ RequirementWitness(satisfyingWitnessTable));
+ return true;
+ }
+
// We will look up members with the same name,
// since only same-name members will be able to
// satisfy the requirement.
@@ -2117,21 +2198,21 @@ namespace Slang
// Make sure that by-name lookup is possible.
buildMemberDictionary(typeDeclRef.getDecl());
auto lookupResult = lookUpLocal(getSession(), this, name, typeDeclRef);
-
+
if (!lookupResult.isValid())
{
getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
- return nullptr;
+ return false;
}
// Iterate over the members and look for one that matches
// the expected signature for the requirement.
for (auto member : lookupResult)
{
- if (doesMemberSatisfyRequirement(member.declRef, requiredMemberDeclRef, requirementWitness))
- return member.declRef.getDecl();
+ if (doesMemberSatisfyRequirement(member.declRef, requiredMemberDeclRef, witnessTable))
+ return true;
}
-
+
// No suitable member found, although there were candidates.
//
// TODO: Eventually we might want something akin to the current
@@ -2140,83 +2221,125 @@ namespace Slang
// and if nothing is found we print the candidates
getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
- return nullptr;
+ return false;
}
// Check that the type declaration `typeDecl`, which
// declares conformance to the interface `interfaceDeclRef`,
// (via the given `inheritanceDecl`) actually provides
// members to satisfy all the requirements in the interface.
- bool checkInterfaceConformance(
- HashSet<DeclRef<InterfaceDecl>> & checkedInterfaceDeclRef,
- DeclRef<AggTypeDeclBase> typeDeclRef,
- InheritanceDecl* inheritanceDecl,
- DeclRef<InterfaceDecl> interfaceDeclRef)
- {
- if (!checkedInterfaceDeclRef.Contains(interfaceDeclRef))
- checkedInterfaceDeclRef.Add(interfaceDeclRef);
- else
- return true;
-
- bool result = true;
+ RefPtr<WitnessTable> checkInterfaceConformance(
+ ConformanceCheckingContext* context,
+ DeclRef<AggTypeDeclBase> typeDeclRef,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ // Has somebody already checked this conformance,
+ // and/or is in the middle of checking it?
+ RefPtr<WitnessTable> witnessTable;
+ if(context->mapInterfaceToWitnessTable.TryGetValue(interfaceDeclRef, witnessTable))
+ return witnessTable;
// We need to check the declaration of the interface
// before we can check that we conform to it.
checkDecl(interfaceDeclRef.getDecl());
+ // We will construct the witness table, and register it
+ // *before* we go about checking fine-grained requirements,
+ // in order to short-circuit any potential for infinite recursion.
+
+ witnessTable = new WitnessTable();
+ context->mapInterfaceToWitnessTable.Add(interfaceDeclRef, witnessTable);
+
+ bool result = true;
+
// TODO: If we ever allow for implementation inheritance,
// then we will need to consider the case where a type
// declares that it conforms to an interface, but one of
// its (non-interface) base types already conforms to
// that interface, so that all of the requirements are
// already satisfied with inherited implementations...
- auto allMembers = getMembersWithExt(interfaceDeclRef);
- for (auto requiredMemberDeclRef : allMembers)
- {
- // Some members of the interface don't actually represent
- // things that we required of the implementing type.
- // For example, when the interface declares that
- // it inherits from another interface, we don't look for
- // a matching inheritance clause on the type, but
- // instead require that it also conforms to that
- // interface.
- if (auto requiredInheritanceDeclRef = requiredMemberDeclRef.As<InheritanceDecl>())
- {
- // Recursively check that the type conforms
- // to the inherited interface.
- //
- // TODO: we *really* need a linearization step here!!!!
- result = result && checkConformanceToType(
- checkedInterfaceDeclRef,
- typeDeclRef,
- inheritanceDecl,
- getBaseType(requiredInheritanceDeclRef));
- continue;
- }
-
- // Look for a member in the type that can satisfy the
- // interface requirement.
- auto isConformanceSatisfied = findWitnessForInterfaceRequirement(
+ for(auto requiredMemberDeclRef : getMembers(interfaceDeclRef))
+ {
+ auto requirementSatisfied = findWitnessForInterfaceRequirement(
+ context,
typeDeclRef,
inheritanceDecl,
interfaceDeclRef,
requiredMemberDeclRef,
- inheritanceDecl->requirementWitnesses);
+ witnessTable);
- if (!isConformanceSatisfied)
- {
- result = false;
+ result = result && requirementSatisfied;
+ }
+
+ // Extensions that apply to the interface type can create new conformances
+ // for the concrete types that inherit from the interface.
+ //
+ // These new conformances should not be able to introduce new *requirements*
+ // for an implementing interface (although they currently can), but we
+ // still need to go through this logic to find the appropriate value
+ // that will satisfy the requirement in these cases, and also to put
+ // the required entry into the witness table for the interface itself.
+ //
+ // TODO: This logic is a bit slippery, and we need to figure out what
+ // it means in the context of separate compilation. If module A defines
+ // an interface IA, module B defines a type C that conforms to IA, and then
+ // module C defines an extension that makes IA conform to IC, then it is
+ // unreasonable to expect the {B:IA} witness table to contain an entry
+ // corresponding to {IA:IC}.
+ //
+ // The simple answer then would be that the {IA:IC} conformance should be
+ // fixed, with a single witness table for {IA:IC}, but then what should
+ // happen in B explicitly conformed to IC already?
+ //
+ // For now we will just walk through the extensions that are known at
+ // the time we are compiling and handle those, and punt on the larger issue
+ // for abit longer.
+ for(auto candidateExt = interfaceDeclRef.getDecl()->candidateExtensions; candidateExt; candidateExt = candidateExt->nextCandidateExtension)
+ {
+ // We need to apply the extension to the interface type that our
+ // concrete type is inheriting from.
+ //
+ // TODO: need to decide if a this-type substitution is needed here.
+ // It probably it.
+ RefPtr<Type> targetType = DeclRefType::Create(
+ getSession(),
+ interfaceDeclRef);
+ auto extDeclRef = ApplyExtensionToType(candidateExt, targetType);
+ if(!extDeclRef)
continue;
+
+ // Only inheritance clauses from the extension matter right now.
+ for(auto requiredInheritanceDeclRef : getMembersOfType<InheritanceDecl>(extDeclRef))
+ {
+ auto requirementSatisfied = findWitnessForInterfaceRequirement(
+ context,
+ typeDeclRef,
+ inheritanceDecl,
+ interfaceDeclRef,
+ requiredInheritanceDeclRef,
+ witnessTable);
+
+ result = result && requirementSatisfied;
}
}
- return result;
+
+ // If we failed to satisfy any requirements along the way,
+ // then we don't actually want to keep the witness table
+ // we've been constructing, because the whole thing was a failure.
+ if(!result)
+ {
+ return nullptr;
+ }
+
+ return witnessTable;
}
- bool checkConformanceToType(
- HashSet<DeclRef<InterfaceDecl>>& checkedInterfaceDeclRefs,
- DeclRef<AggTypeDeclBase> typeDeclRef,
- InheritanceDecl* inheritanceDecl,
- Type* baseType)
+ RefPtr<WitnessTable> checkConformanceToType(
+ ConformanceCheckingContext* context,
+ DeclRef<AggTypeDeclBase> typeDeclRef,
+ InheritanceDecl* inheritanceDecl,
+ Type* baseType)
{
if (auto baseDeclRefType = baseType->As<DeclRefType>())
{
@@ -2227,7 +2350,7 @@ namespace Slang
// We need to check that it provides all of the members
// required by that interface.
return checkInterfaceConformance(
- checkedInterfaceDeclRefs,
+ context,
typeDeclRef,
inheritanceDecl,
baseInterfaceDeclRef);
@@ -2235,41 +2358,65 @@ namespace Slang
}
getSink()->diagnose(inheritanceDecl, Diagnostics::unimplemented, "type not supported for inheritance");
- return false;
+ return nullptr;
}
- // Check that the type declaration `typeDecl`, which
- // declares that it inherits from another type via
+ // Check that the type (or extension) declaration `declRef`,
+ // which declares that it inherits from another type via
// `inheritanceDecl` actually does what it needs to
// for that inheritance to be valid.
bool checkConformance(
- DeclRef<AggTypeDeclBase> typeDecl,
+ DeclRef<AggTypeDeclBase> declRef,
InheritanceDecl* inheritanceDecl)
{
+ declRef = createDefaultSubstitutionsIfNeeded(getSession(), declRef).As<AggTypeDeclBase>();
+
+ // Don't check conformances for abstract types that
+ // are being used to express *required* conformances.
+ if (auto assocTypeDeclRef = declRef.As<AssocTypeDecl>())
+ {
+ // An associated type declaration represents a requirement
+ // in an outer interface declaration, and its members
+ // (type constraints) represent additional requirements.
+ return true;
+ }
+ else if (auto interfaceDeclRef = declRef.As<InterfaceDecl>())
+ {
+ // HACK: Our semantics as they stand today are that an
+ // `extension` of an interface that adds a new inheritance
+ // clause acts *as if* that inheritnace clause had been
+ // attached to the original `interface` decl: that is,
+ // it adds additional requirements.
+ //
+ // This is *not* a reasonable semantic to keep long-term,
+ // but it is required for some of our current example
+ // code to work.
+ return true;
+ }
+
+
// Look at the type being inherited from, and validate
// appropriately.
auto baseType = inheritanceDecl->base.type;
- HashSet<DeclRef<InterfaceDecl>> checkdInterfaceDeclRefs;
- return checkConformanceToType(checkdInterfaceDeclRefs, typeDecl, inheritanceDecl, baseType.As<Type>());
- }
- bool checkConformance(
- AggTypeDeclBase* typeDecl,
- InheritanceDecl* inheritanceDecl)
- {
- return checkConformance(DeclRef<AggTypeDeclBase>(typeDecl, SubstitutionSet()), inheritanceDecl);
+ ConformanceCheckingContext context;
+ RefPtr<WitnessTable> witnessTable = checkConformanceToType(&context, declRef, inheritanceDecl, baseType);
+ if(!witnessTable)
+ return false;
+
+ inheritanceDecl->witnessTable = witnessTable;
+ return true;
}
void checkExtensionConformance(ExtensionDecl* decl)
{
- DeclRef<AggTypeDecl> aggTypeDeclRef;
if (auto targetDeclRefType = decl->targetType->As<DeclRefType>())
{
- if (aggTypeDeclRef = targetDeclRefType->declRef.As<AggTypeDecl>())
+ if (auto aggTypeDeclRef = targetDeclRefType->declRef.As<AggTypeDecl>())
{
for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
{
- checkConformance(aggTypeDeclRef.getDecl(), inheritanceDecl);
+ checkConformance(aggTypeDeclRef, inheritanceDecl);
}
}
}
@@ -2303,7 +2450,7 @@ namespace Slang
// (That's what C# does).
for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
{
- checkConformance(decl, inheritanceDecl);
+ checkConformance(makeDeclRef(decl), inheritanceDecl);
}
}
}
@@ -2708,7 +2855,7 @@ namespace Slang
// generic.
//
subst->genericDecl = prevGenericDecl;
- prevFuncDeclRef.substitutions.genericSubstitutions = subst;
+ prevFuncDeclRef.substitutions.substitutions = subst;
//
// One way to think about it is that if we have these
// declarations (ignore the name differences...):
@@ -3481,6 +3628,7 @@ namespace Slang
switch(getSourceLanguage())
{
+ default:
case SourceLanguage::Slang:
case SourceLanguage::HLSL:
// HLSL: `static const` is used to mark compile-time constant expressions
@@ -3626,7 +3774,7 @@ namespace Slang
auto vectorGenericDecl = findMagicDecl(
session, "Vector").As<GenericDecl>();
auto vectorTypeDecl = vectorGenericDecl->inner;
-
+
auto substitutions = new GenericSubstitution();
substitutions->genericDecl = vectorGenericDecl.Ptr();
substitutions->args.Add(elementType);
@@ -3815,11 +3963,10 @@ namespace Slang
// TODO: need to check that the target type names a declaration...
- DeclRef<AggTypeDecl> aggTypeDeclRef;
if (auto targetDeclRefType = decl->targetType->As<DeclRefType>())
{
// Attach our extension to that type as a candidate...
- if (aggTypeDeclRef = targetDeclRefType->declRef.As<AggTypeDecl>())
+ if (auto aggTypeDeclRef = targetDeclRefType->declRef.As<AggTypeDecl>())
{
auto aggTypeDecl = aggTypeDeclRef.getDecl();
decl->nextCandidateExtension = aggTypeDecl->candidateExtensions;
@@ -4034,7 +4181,7 @@ namespace Slang
// Crete a subtype witness based on the declared relationship
// found in a single breadcrumb
- RefPtr<SubtypeWitness> createSimplSubtypeWitness(
+ RefPtr<DeclaredSubtypeWitness> createSimpleSubtypeWitness(
TypeWitnessBreadcrumb* breadcrumb)
{
RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
@@ -4052,7 +4199,7 @@ namespace Slang
if(!inBreadcrumbs)
{
// We need to construct a witness to the fact
- // that `type` has been proven to be equal
+ // that `type` has been proven to be *equal*
// to `interfaceDeclRef`.
//
SLANG_UNEXPECTED("reflexive type witness");
@@ -4061,44 +4208,74 @@ namespace Slang
// We might have one or more steps in the breadcrumb trail, e.g.:
//
- // (A : B) (B : C) (C : D)
+ // {A : B} {B : C} {C : D}
//
// The chain is stored as a reversed linked list, so that
// the first entry would be the `(C : D)` relationship
// above.
//
- // We are going to walk the list and build up a suitable
- // subtype witness.
+ // We need to walk the list and build up a suitable witness,
+ // which in the above case would look like:
+ //
+ // Transitive(
+ // Transitive(
+ // Declared({A : B}),
+ // {B : C}),
+ // {C : D})
+ //
+ // Because of the ordering of the breadcrumb trail, along
+ // with the way the `Transitive` case nests, we will be
+ // building these objects outside-in, and keeping
+ // track of the "hole" where the next step goes.
+ //
auto bb = inBreadcrumbs;
- // Create a witness for the last step in the chain
- RefPtr<SubtypeWitness> witness = createSimplSubtypeWitness(bb);
- bb = bb->prev;
+ // `witness` here will hold the first (outer-most) object
+ // we create, which is the overall result.
+ RefPtr<SubtypeWitness> witness;
- // Now, as long as we have more entries to deal with,
- // we'll be in a situation like:
- //
- // ... (B : C) <witness>
- //
- // and we want to wrap up one more link in our chain.
+ // `link` will point at the remaining "hole" in the
+ // data structure, to be filled in.
+ RefPtr<SubtypeWitness>* link = &witness;
- while (bb)
+ // As long as there is more than one breadcrumb, we
+ // need to be creating transitie witnesses.
+ while(bb->prev)
{
- // Create simple witness for the step in the chain
- RefPtr<SubtypeWitness> link = createSimplSubtypeWitness(bb);
-
- // Now join the link onto the existing chain represented
- // by `witness`.
+ // On the first iteration when processing the list
+ // above, the breadcrumb would be for `{ C : D }`,
+ // and so we'd create:
+ //
+ // Transitive(
+ // [...],
+ // { C : D})
+ //
+ // where `[...]` represents the "hole" we leave
+ // open to fill in next.
+ //
RefPtr<TransitiveSubtypeWitness> transitiveWitness = new TransitiveSubtypeWitness();
- transitiveWitness->sub = link->sub;
- transitiveWitness->sup = witness->sup;
- transitiveWitness->subToMid = link;
- transitiveWitness->midToSup = witness;
+ transitiveWitness->sub = bb->sub;
+ transitiveWitness->sup = bb->sup;
+ transitiveWitness->midToSup = bb->declRef;
+
+ // Fill in the current hole, and then set the
+ // hole to point into the node we just created.
+ *link = transitiveWitness;
+ link = &transitiveWitness->subToMid;
- witness = transitiveWitness;
+ // Move on with the list.
bb = bb->prev;
}
+ // If we exit the loop, then there is only one breadcrumb left.
+ // In our running example this would be `{ A : B }`. We create
+ // a simple (declared) subtype witness for it, and plug the
+ // final hole, after which there shouldn't be a hole to deal with.
+ RefPtr<DeclaredSubtypeWitness> declaredWitness = createSimpleSubtypeWitness(bb);
+ *link = declaredWitness;
+
+ // We now know that our original `witness` variable has been
+ // filled in, and there are no other holes.
return witness;
}
@@ -4325,7 +4502,7 @@ namespace Slang
{
if( auto leftInterfaceRef = leftDeclRefType->declRef.As<InterfaceDecl>() )
{
- //
+ //
return TryJoinTypeWithInterface(right, leftInterfaceRef);
}
}
@@ -4333,7 +4510,7 @@ namespace Slang
{
if( auto rightInterfaceRef = rightDeclRefType->declRef.As<InterfaceDecl>() )
{
- //
+ //
return TryJoinTypeWithInterface(left, rightInterfaceRef);
}
}
@@ -4481,9 +4658,9 @@ namespace Slang
RefPtr<GenericSubstitution> solvedSubst = new GenericSubstitution();
solvedSubst->genericDecl = genericDeclRef.getDecl();
- solvedSubst->outer = genericDeclRef.substitutions.genericSubstitutions;
+ solvedSubst->outer = genericDeclRef.substitutions.substitutions;
solvedSubst->args = args;
- resultSubst.genericSubstitutions = solvedSubst;
+ resultSubst.substitutions = solvedSubst;
for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
{
@@ -4959,12 +5136,12 @@ namespace Slang
assert(subst);
subst->genericDecl = genericDeclRef.getDecl();
- subst->outer = genericDeclRef.substitutions.genericSubstitutions;
+ subst->outer = genericDeclRef.substitutions.substitutions;
for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
{
auto subset = genericDeclRef.substitutions;
- subset.genericSubstitutions = subst;
+ subset.substitutions = subst;
DeclRef<GenericTypeConstraintDecl> constraintDeclRef(
constraintDecl, subset);
@@ -5039,7 +5216,7 @@ namespace Slang
}
subst->genericDecl = baseGenericRef.getDecl();
- subst->outer = baseGenericRef.substitutions.genericSubstitutions;
+ subst->outer = baseGenericRef.substitutions.substitutions;
DeclRef<Decl> innerDeclRef(GetInner(baseGenericRef), subst);
@@ -5305,7 +5482,6 @@ namespace Slang
}
}
-
OverloadCandidate candidate;
candidate.flavor = OverloadCandidate::Flavor::Func;
candidate.item = item;
@@ -5429,7 +5605,7 @@ namespace Slang
auto constraintDecl2 = sndWit->declRef.As<TypeConstraintDecl>();
assert(constraintDecl1);
assert(constraintDecl2);
- return TryUnifyTypes(constraints,
+ return TryUnifyTypes(constraints,
constraintDecl1.getDecl()->getSup().type,
constraintDecl2.getDecl()->getSup().type);
}
@@ -5440,15 +5616,40 @@ namespace Slang
// default: fail
return false;
}
-
- bool TryUnifySubstitutions(
- ConstraintSystem& constraints,
- RefPtr<GenericSubstitution> fst,
- RefPtr<GenericSubstitution> snd)
+
+ bool tryUnifySubstitutions(
+ ConstraintSystem& constraints,
+ RefPtr<Substitutions> fst,
+ RefPtr<Substitutions> snd)
{
// They must both be NULL or non-NULL
if (!fst || !snd)
- return fst == snd;
+ return !fst && !snd;
+
+ if(auto fstGeneric = fst.As<GenericSubstitution>())
+ {
+ if(auto sndGeneric = snd.As<GenericSubstitution>())
+ {
+ return tryUnifyGenericSubstitutions(
+ constraints,
+ fstGeneric,
+ sndGeneric);
+ }
+ }
+
+ // TODO: need to handle other cases here
+
+ return false;
+ }
+
+ bool tryUnifyGenericSubstitutions(
+ ConstraintSystem& constraints,
+ RefPtr<GenericSubstitution> fst,
+ RefPtr<GenericSubstitution> snd)
+ {
+ SLANG_ASSERT(fst);
+ SLANG_ASSERT(snd);
+
auto fstGen = fst;
auto sndGen = snd;
// They must be specializing the same generic
@@ -5468,7 +5669,7 @@ namespace Slang
}
// Their "base" specializations must unify
- if (!TryUnifySubstitutions(constraints, fstGen->outer, sndGen->outer))
+ if (!tryUnifySubstitutions(constraints, fstGen->outer, sndGen->outer))
{
okay = false;
}
@@ -5554,10 +5755,10 @@ namespace Slang
// next we need to unify the substitutions applied
// to each decalration reference.
- if (!TryUnifySubstitutions(
+ if (!tryUnifySubstitutions(
constraints,
- fstDeclRef.substitutions.genericSubstitutions,
- sndDeclRef.substitutions.genericSubstitutions))
+ fstDeclRef.substitutions.substitutions,
+ sndDeclRef.substitutions.substitutions))
{
return false;
}
@@ -5648,41 +5849,117 @@ namespace Slang
// Is the candidate extension declaration actually applicable to the given type
DeclRef<ExtensionDecl> ApplyExtensionToType(
- ExtensionDecl* extDecl,
- RefPtr<Type> type)
+ ExtensionDecl* extDecl,
+ RefPtr<Type> type)
{
+ DeclRef<ExtensionDecl> extDeclRef = makeDeclRef(extDecl);
+
+ // If the extension is a generic extension, then we
+ // need to infer type argumenst that will give
+ // us a target type that matches `type`.
+ //
if (auto extGenericDecl = GetOuterGeneric(extDecl))
{
ConstraintSystem constraints;
constraints.genericDecl = extGenericDecl;
if (!TryUnifyTypes(constraints, extDecl->targetType.Ptr(), type))
- return DeclRef<Decl>().As<ExtensionDecl>();
+ return DeclRef<ExtensionDecl>();
auto constraintSubst = TrySolveConstraintSystem(&constraints, DeclRef<Decl>(extGenericDecl, nullptr).As<GenericDecl>());
if (!constraintSubst)
{
- return DeclRef<Decl>().As<ExtensionDecl>();
+ return DeclRef<ExtensionDecl>();
}
// Consruct a reference to the extension with our constraint variables
// set as they were found by solving the constraint system.
- DeclRef<ExtensionDecl> extDeclRef = DeclRef<Decl>(extDecl, constraintSubst).As<ExtensionDecl>();
+ extDeclRef = DeclRef<Decl>(extDecl, constraintSubst).As<ExtensionDecl>();
+ }
- // We expect/require that the result of unification is such that
- // the target types are now equal
- SLANG_ASSERT(GetTargetType(extDeclRef)->Equals(type));
+ // Now extract the target type from our (possibly specialized) extension decl-ref.
+ RefPtr<Type> targetType = GetTargetType(extDeclRef);
- return extDeclRef;
- }
- else
+ // As a bit of a kludge here, if the target type of the extension is
+ // an interface, and the `type` we are trying to match up has a this-type
+ // substitution for that interface, then we want to attach a matching
+ // substitution to the extension decl-ref.
+ if(auto targetDeclRefType = targetType->As<DeclRefType>())
{
- // The easy case is when the extension isn't generic:
- // either it applies to the type or not.
- if (!type->Equals(extDecl->targetType))
- return DeclRef<Decl>().As<ExtensionDecl>();
- return DeclRef<Decl>(extDecl, nullptr).As<ExtensionDecl>();
+ if(auto targetInterfaceDeclRef = targetDeclRefType->declRef.As<InterfaceDecl>())
+ {
+ // Okay, the target type is an interface.
+ //
+ // Is the type we want to apply to also an interface?
+ if(auto appDeclRefType = type->As<DeclRefType>())
+ {
+ if(auto appInterfaceDeclRef = appDeclRefType->declRef.As<InterfaceDecl>())
+ {
+ if(appInterfaceDeclRef.getDecl() == targetInterfaceDeclRef.getDecl())
+ {
+ // Looks like we have a match in the types,
+ // now let's see if we have a this-type substitution.
+ if(auto appThisTypeSubst = appInterfaceDeclRef.substitutions.substitutions.As<ThisTypeSubstitution>())
+ {
+ if(appThisTypeSubst->interfaceDecl == appInterfaceDeclRef.getDecl())
+ {
+ // The type we want to apply to has a this-type substitution,
+ // and (by construction) the target type currently does not.
+ //
+ SLANG_ASSERT(!targetInterfaceDeclRef.substitutions.substitutions.As<ThisTypeSubstitution>());
+
+ // We will create a new substitution to apply to the target type.
+ RefPtr<ThisTypeSubstitution> newTargetSubst = new ThisTypeSubstitution();
+ newTargetSubst->interfaceDecl = appThisTypeSubst->interfaceDecl;
+ newTargetSubst->witness = appThisTypeSubst->witness;
+ newTargetSubst->outer = targetInterfaceDeclRef.substitutions.substitutions;
+
+ targetType = DeclRefType::Create(getSession(),
+ DeclRef<InterfaceDecl>(targetInterfaceDeclRef.getDecl(), newTargetSubst));
+
+ // Note: we are constructing a this-type substitution that
+ // we will apply to the extension declaration as well.
+ // This is not strictly allowed by our current representation
+ // choices, but we need it in order to make sure that
+ // references to the target type of the extension
+ // declaration have a chance to resolve the way we want them to.
+
+ RefPtr<ThisTypeSubstitution> newExtSubst = new ThisTypeSubstitution();
+ newExtSubst->interfaceDecl = appThisTypeSubst->interfaceDecl;
+ newExtSubst->witness = appThisTypeSubst->witness;
+ newExtSubst->outer = extDeclRef.substitutions.substitutions;
+
+ extDeclRef = DeclRef<ExtensionDecl>(
+ extDeclRef.getDecl(),
+ newExtSubst);
+
+ // TODO: Ideally we should also apply the chosen specialization to
+ // the decl-ref for the extension, so that subsequent lookup through
+ // the members of this extension will retain that substitution and
+ // be able to apply it.
+ //
+ // E.g., if an extension method returns a value of an associated
+ // type, then we'd want that to become specialized to a concrete
+ // type when using the extension method on a value of concrete type.
+ //
+ // The challenge here that makes me reluctant to just staple on
+ // such a substitution is that it wouldn't follow our implicit
+ // rules about where `ThisTypeSubstitution`s can appear.
+ }
+ }
+ }
+ }
+ }
+ }
}
+
+ // In order for this extension to apply to the given type, we
+ // need to have a match on the target types.
+ if (!type->Equals(targetType))
+ return DeclRef<ExtensionDecl>();
+
+
+ return extDeclRef;
}
#if 0
@@ -6033,8 +6310,8 @@ namespace Slang
// signature
if( parentGenericDeclRef )
{
- SLANG_RELEASE_ASSERT(declRef.substitutions);
- auto genSubst = declRef.substitutions.genericSubstitutions;
+ auto genSubst = declRef.substitutions.substitutions.As<GenericSubstitution>();
+ SLANG_RELEASE_ASSERT(genSubst);
SLANG_RELEASE_ASSERT(genSubst->genericDecl == parentGenericDeclRef.getDecl());
sb << "<";
@@ -7166,8 +7443,10 @@ namespace Slang
scopesToTry.Add(entryPoint->getTranslationUnit()->SyntaxNode->scope);
for (auto & module : entryPoint->compileRequest->loadedModulesList)
scopesToTry.Add(module->moduleDecl->scope);
+
+ List<RefPtr<Type>> globalGenericArgs;
for (auto name : entryPoint->genericParameterTypeNames)
- {
+ {
// parse type name
RefPtr<Type> type;
for (auto & s : scopesToTry)
@@ -7185,9 +7464,10 @@ namespace Slang
sink->diagnose(firstDeclWithName, Diagnostics::entryPointTypeSymbolNotAType, name);
return;
}
- entryPoint->genericParameterTypes.Add(type);
+
+ globalGenericArgs.Add(type);
}
-
+
// validate global type arguments only when we are generating code
if ((entryPoint->compileRequest->compileFlags & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0)
{
@@ -7210,38 +7490,102 @@ namespace Slang
for (auto p : globalGenParams)
globalGenericParams.Add(p);
}
- if (globalGenericParams.Count() != entryPoint->genericParameterTypes.Count())
+
+ if (globalGenericParams.Count() != globalGenericArgs.Count())
{
- sink->diagnose(entryPoint->decl, Diagnostics::mismatchEntryPointTypeArgument, globalGenericParams.Count(),
- entryPoint->genericParameterTypes.Count());
+ sink->diagnose(entryPoint->decl, Diagnostics::mismatchEntryPointTypeArgument,
+ globalGenericParams.Count(),
+ globalGenericArgs.Count());
return;
}
- // if entry-point type arguments matches parameters, try find
- // SubtypeWitness for each argument
- int index = 0;
- for (auto & gParam : globalGenericParams)
+
+ // We have an appropriate number of arguments for the global generic parameters,
+ // and now we need to check that the arguments conform to the declared constraints.
+ //
+ // Along the way, we will build up an appropriate set of substitutions to represent
+ // the generic arguments and their conformances.
+ //
+ RefPtr<Substitutions> globalGenericSubsts;
+ auto globalGenericSubstLink = &globalGenericSubsts;
+ //
+ // TODO: There is a serious flaw to this checking logic if we ever have cases where
+ // the constraints on one `type_param` can depend on another `type_param`, e.g.:
+ //
+ // type_param A;
+ // type_param B : ISidekick<A>;
+ //
+ // In that case, if a user tries to set `B` to `Robin` and `Robin` conforms to
+ // `ISidekick<Batman>`, then the compiler needs to know whether `A` is being
+ // set to `Batman` to know whether the setting for `B` is valid. In this limit
+ // the constraints can be mutually recursive (so `A : IMentor<B>`).
+ //
+ // The only way to check things corectly is to validate each conformance under
+ // a set of assumptions (substitutions) that includes all the type substitutions,
+ // and possibly also all the other constraints *except* the one to be validated.
+ //
+ // We will punt on this for now, and just check each constraint in isolation.
+ //
+ UInt argCounter = 0;
+ for(auto& globalGenericParam : globalGenericParams)
{
- for (auto constraint : gParam->getMembersOfType<GenericTypeConstraintDecl>())
+ // Get the argument that matches this parameter.
+ UInt argIndex = argCounter++;
+ SLANG_ASSERT(argIndex < globalGenericArgs.Count());
+ auto globalGenericArg = globalGenericArgs[argIndex];
+
+ // Create a substitution for this parameter/argument.
+ RefPtr<GlobalGenericParamSubstitution> subst = new GlobalGenericParamSubstitution();
+ subst->paramDecl = globalGenericParam;
+ subst->actualType = globalGenericArg;
+
+ // Walk through the declared constraints for the parameter,
+ // and check that the argument actually satisfies them.
+ for(auto constraint : globalGenericParam->getMembersOfType<GenericTypeConstraintDecl>())
{
+ // Get the type that the constraint is enforcing conformance to
auto interfaceType = GetSup(DeclRef<GenericTypeConstraintDecl>(constraint, nullptr));
+
+ // Use our semantic-checking logic to search for a witness to the required conformance
SemanticsVisitor visitor(sink, entryPoint->compileRequest, translationUnit);
- auto witness = visitor.tryGetSubtypeWitness(entryPoint->genericParameterTypes[index], interfaceType);
+ auto witness = visitor.tryGetSubtypeWitness(globalGenericArg, interfaceType);
if (!witness)
{
- sink->diagnose(gParam,
- Diagnostics::typeArgumentDoesNotConformToInterface, gParam->nameAndLoc.name, entryPoint->genericParameterTypes[index],
+ // If no witness was found, then we will be unable to satisfy
+ // the conformances required.
+ sink->diagnose(globalGenericParam,
+ Diagnostics::typeArgumentDoesNotConformToInterface,
+ globalGenericParam->nameAndLoc.name,
+ globalGenericArg,
interfaceType);
}
- entryPoint->genericParameterWitnesses.Add(witness);
+
+ // Attach the concrete witness for this conformance to the
+ // substutiton
+ GlobalGenericParamSubstitution::ConstraintArg constraintArg;
+ constraintArg.decl = constraint;
+ constraintArg.val = witness;
+ subst->constraintArgs.Add(constraintArg);
}
- index++;
+
+ // Add the substitution for this parameter to the global substitution
+ // set that we are building.
+
+ *globalGenericSubstLink = subst;
+ globalGenericSubstLink = &subst->outer;
}
+
+ entryPoint->globalGenericSubst = globalGenericSubsts;
}
if (sink->errorCount != 0)
return;
// Now that we've *found* the entry point, it is time to validate
// that it actually meets the constraints for the chosen stage/profile.
+ //
+ // TODO: This validation should be performed "under" any global generic
+ // parameter substitution we might have created, so that we can validate
+ // based on knowledge of actual types.
+ //
validateEntryPoint(entryPoint);
}
@@ -7453,6 +7797,43 @@ namespace Slang
return semantics->ApplyExtensionToType(extDecl, type);
}
+ RefPtr<GenericSubstitution> createDefaultSubsitutionsForGeneric(
+ Session* session,
+ GenericDecl* genericDecl,
+ RefPtr<Substitutions> outerSubst)
+ {
+ RefPtr<GenericSubstitution> genericSubst = new GenericSubstitution();
+ genericSubst->genericDecl = genericDecl;
+ genericSubst->outer = outerSubst;
+
+ for( auto mm : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() )
+ {
+ genericSubst->args.Add(DeclRefType::Create(session, DeclRef<Decl>(genericTypeParamDecl.Ptr(), outerSubst)));
+ }
+ else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
+ {
+ genericSubst->args.Add(new GenericParamIntVal(DeclRef<GenericValueParamDecl>(genericValueParamDecl.Ptr(), outerSubst)));
+ }
+ }
+
+ // create default substitution arguments for constraints
+ for (auto mm : genericDecl->Members)
+ {
+ if (auto genericTypeConstraintDecl = mm.As<GenericTypeConstraintDecl>())
+ {
+ RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
+ witness->declRef = DeclRef<Decl>(genericTypeConstraintDecl.Ptr(), outerSubst);
+ witness->sub = genericTypeConstraintDecl->sub.type;
+ witness->sup = genericTypeConstraintDecl->sup.type;
+ genericSubst->args.Add(witness);
+ }
+ }
+
+ return genericSubst;
+ }
+
// Sometimes we need to refer to a declaration the way that it would be specialized
// inside the context where it is declared (e.g., with generic parameters filled in
// using their archetypes).
@@ -7460,53 +7841,25 @@ namespace Slang
SubstitutionSet createDefaultSubstitutions(
Session* session,
Decl* decl,
- SubstitutionSet parentSubst)
+ SubstitutionSet outerSubstSet)
{
- 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 resultSubst;
+ return outerSubstSet;
- 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, DeclRef<Decl>(genericTypeParamDecl.Ptr(), outerSubst)));
- }
- else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
- {
- subst->args.Add(new GenericParamIntVal(DeclRef<GenericValueParamDecl>(genericValueParamDecl.Ptr(), outerSubst)));
- }
- }
+ RefPtr<GenericSubstitution> genericSubst = createDefaultSubsitutionsForGeneric(
+ session,
+ genericDecl,
+ outerSubstSet.substitutions);
- // create default substitution arguments for constraints
- for (auto mm : genericDecl->Members)
- {
- if (auto genericTypeConstraintDecl = mm.As<GenericTypeConstraintDecl>())
- {
- RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
- witness->declRef = DeclRef<Decl>(genericTypeConstraintDecl.Ptr(), outerSubst);
- witness->sub = genericTypeConstraintDecl->sub.type;
- witness->sup = genericTypeConstraintDecl->sup.type;
- subst->args.Add(witness);
- }
- }
+ return SubstitutionSet(genericSubst);
}
- return resultSubst;
+
+ return outerSubstSet;
}
SubstitutionSet createDefaultSubstitutions(