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.cpp809
1 files changed, 671 insertions, 138 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 5ebb22999..a42edc331 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -212,9 +212,22 @@ namespace Slang
case LookupResultItem::Breadcrumb::Kind::Member:
bb = ConstructDeclRefExpr(breadcrumb->declRef, bb, loc);
break;
+
case LookupResultItem::Breadcrumb::Kind::Deref:
bb = ConstructDerefExpr(bb, loc);
break;
+
+ case LookupResultItem::Breadcrumb::Kind::Constraint:
+ {
+ // TODO: do we need to make something more
+ // explicit here?
+ bb = ConstructDeclRefExpr(
+ breadcrumb->declRef,
+ bb,
+ loc);
+ }
+ break;
+
default:
SLANG_UNREACHABLE("all cases handle");
}
@@ -425,9 +438,20 @@ namespace Slang
// the name of a non-proper type, and then have the compiler fill
// in the default values for its type arguments (e.g., a variable
// given type `Texture2D` will actually have type `Texture2D<float4>`).
- bool CoerceToProperTypeImpl(TypeExp const& typeExp, RefPtr<Type>* outProperType)
+ bool CoerceToProperTypeImpl(
+ TypeExp const& typeExp,
+ RefPtr<Type>* outProperType,
+ DiagnosticSink* sink)
{
Type* type = typeExp.type.Ptr();
+ if(!type && typeExp.exp)
+ {
+ if(auto typeType = typeExp.exp->type.type.As<TypeType>())
+ {
+ type = typeType->type;
+ }
+ }
+
if (auto genericDeclRefType = type->As<GenericDeclRefType>())
{
// We are using a reference to a generic declaration as a concrete
@@ -447,11 +471,11 @@ namespace Slang
{
if (!typeParam->initType.exp)
{
- if (outProperType)
+ if (sink)
{
if (!isRewriteMode())
{
- getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
+ sink->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
}
*outProperType = getSession()->getErrorType();
}
@@ -466,11 +490,11 @@ namespace Slang
{
if (!valParam->initExpr)
{
- if (outProperType)
+ if (sink)
{
if (!isRewriteMode())
{
- getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
+ sink->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter");
}
*outProperType = getSession()->getErrorType();
}
@@ -509,13 +533,16 @@ namespace Slang
TypeExp CoerceToProperType(TypeExp const& typeExp)
{
TypeExp result = typeExp;
- CoerceToProperTypeImpl(typeExp, &result.type);
+ CoerceToProperTypeImpl(typeExp, &result.type, getSink());
return result;
}
- bool CanCoerceToProperType(TypeExp const& typeExp)
+ TypeExp tryCoerceToProperType(TypeExp const& typeExp)
{
- return CoerceToProperTypeImpl(typeExp, nullptr);
+ TypeExp result = typeExp;
+ if(!CoerceToProperTypeImpl(typeExp, &result.type, nullptr))
+ return TypeExp();
+ return result;
}
// Check a type, and coerce it to be proper
@@ -1156,16 +1183,13 @@ namespace Slang
}
}
+ genericDecl->SetCheckState(DeclCheckState::CheckedHeader);
+
// check the nested declaration
// TODO: this needs to be done in an appropriate environment...
checkDecl(genericDecl->inner);
}
- void visitInterfaceDecl(InterfaceDecl* /*decl*/)
- {
- // TODO: do some actual checking of members here
- }
-
void visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
// check the type being inherited from
@@ -1417,19 +1441,6 @@ namespace Slang
}
}
- void visitClassDecl(ClassDecl * classNode)
- {
- if (classNode->IsChecked(DeclCheckState::Checked))
- return;
- classNode->SetCheckState(DeclCheckState::Checked);
-
- for (auto field : classNode->GetFields())
- {
- field->type = CheckUsableType(field->type);
- field->SetCheckState(DeclCheckState::Checked);
- }
- }
-
void visitStructField(StructField* field)
{
// TODO: bottleneck through general-case variable checking
@@ -1438,16 +1449,298 @@ namespace Slang
field->SetCheckState(DeclCheckState::Checked);
}
- void visitStructDecl(StructDecl * structNode)
+ bool doesSignatureMatchRequirement(
+ CallableDecl* memberDecl,
+ DeclRef<CallableDecl> requiredMemberDeclRef)
+ {
+ // TODO: actually implement matching here. For now we'll
+ // just pretend that things are satisfied in order to make progress.
+ return true;
+ }
+
+ // Does the given `memberDecl` work as an implementation
+ // to satisfy the requirement `requiredMemberDeclRef`
+ // from an interface?
+ bool doesMemberSatisfyRequirement(
+ Decl* memberDecl,
+ DeclRef<Decl> requiredMemberDeclRef)
+ {
+ // At a high level, we want to chack that the
+ // `memberDecl` and the `requiredMemberDeclRef`
+ // have the same AST node class, and then also
+ // check that their signatures match.
+ //
+ // There are a bunch of detailed decisions that
+ // have to be made, though, because we might, e.g.,
+ // allow a function with more general parameter
+ // types to satisfy a requirement with more
+ // specific parameter types.
+ //
+ // If we ever allow for "property" declarations,
+ // then we would probably need to allow an
+ // ordinary field to satisfy a property requirement.
+ //
+ // An associated type requirement should be allowed
+ // to be satisfied by any type declaration:
+ // a typedef, a `struct`, etc.
+
+ if (auto memberFuncDecl = dynamic_cast<FuncDecl*>(memberDecl))
+ {
+ if (auto requiredFuncDeclRef = requiredMemberDeclRef.As<FuncDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberFuncDecl,
+ requiredFuncDeclRef);
+ }
+ }
+ else if (auto memberInitDecl = dynamic_cast<ConstructorDecl*>(memberDecl))
+ {
+ if (auto requiredInitDecl = requiredMemberDeclRef.As<ConstructorDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberInitDecl,
+ requiredInitDecl);
+ }
+ }
+
+ // Default: just assume that thing aren't being satisfied.
+ return false;
+ }
+
+ // Find the appropriate member of a declared type to
+ // satisfy a requirement of an interface the type
+ // claims to conform to.
+ //
+ // The type declaration `typeDecl` has declared that it
+ // conforms to the interface `interfaceDeclRef`, and
+ // `requiredMemberDeclRef` is a required member of
+ // the interface.
+ RefPtr<Decl> findWitnessForInterfaceRequirement(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef)
+ {
+ // We will look up members with the same name,
+ // since only same-name members will be able to
+ // satisfy the requirement.
+ //
+ // TODO: this won't work right now for members that
+ // don't have names, which right now includes
+ // initializers/constructors.
+ Name* name = requiredMemberDeclRef.GetName();
+
+ // We are basically looking up members of the
+ // given type, but we need to be a bit careful.
+ // We *cannot* perfom lookup "through" inheritance
+ // declarations for this or other interfaces,
+ // since that would let us satisfy a requirement
+ // with itself.
+ //
+ // There's also an interesting question of whether
+ // we can/should support innterface requirements
+ // being satisfied via `__transparent` members.
+ // This seems like a "clever" idea rather than
+ // a useful one, and IR generation would
+ // need to construct real IR to trampoline over
+ // to the implementation.
+ //
+ // The final case that can't be reduced to just
+ // "a directly declared member with the same name"
+ // is the case where the type inherits a member
+ // that can satisfy the requirement from a base type.
+ // We are ignoring implementation inheritance for
+ // now, so we won't worry about this.
+
+ // Make sure that by-name lookup is possible.
+ buildMemberDictionary(typeDecl);
+
+ Decl* firstMemberOfName = nullptr;
+ typeDecl->memberDictionary.TryGetValue(name, firstMemberOfName);
+
+ if (!firstMemberOfName)
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDecl, requiredMemberDeclRef);
+ return nullptr;
+ }
+
+ // Iterate over the members and look for one that matches
+ // the expected signature for the requirement.
+ for (auto memberDecl = firstMemberOfName; memberDecl; memberDecl = memberDecl->nextInContainerWithSameName)
+ {
+ if (doesMemberSatisfyRequirement(memberDecl, requiredMemberDeclRef))
+ return memberDecl;
+ }
+
+ // No suitable member found, although there were candidates.
+ //
+ // TODO: Eventually we might want something akin to the current
+ // overload resolution logic, where we keep track of a list
+ // of "candidates" for satisfaction of the requirement,
+ // and if nothing is found we print the candidates
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDecl, requiredMemberDeclRef);
+ return nullptr;
+ }
+
+ // 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.
+ void checkInterfaceConformance(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ // We need to check the declaration of the interface
+ // before we can check that we conform to it.
+ EnsureDecl(interfaceDeclRef.getDecl());
+
+ // 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...
+
+ for (auto requiredMemberDeclRef : getMembers(interfaceDeclRef))
+ {
+ // 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!!!!
+ checkConformanceToType(
+ typeDecl,
+ inheritanceDecl,
+ getBaseType(requiredInheritanceDeclRef));
+ continue;
+ }
+
+ // Look for a member in the type that can satisfy the
+ // interface requirement.
+ auto conformanceWitness = findWitnessForInterfaceRequirement(
+ typeDecl,
+ inheritanceDecl,
+ interfaceDeclRef,
+ requiredMemberDeclRef);
+
+ if (!conformanceWitness)
+ continue;
+
+ // Store that witness into a table stored on the `inheritnaceDecl`
+ // so that it can be used for downstream code generation.
+
+ inheritanceDecl->requirementWitnesses.Add(requiredMemberDeclRef, conformanceWitness);
+ }
+ }
+
+ void checkConformanceToType(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl,
+ Type* baseType)
+ {
+ if (auto baseDeclRefType = baseType->As<DeclRefType>())
+ {
+ auto baseTypeDeclRef = baseDeclRefType->declRef;
+ if (auto baseInterfaceDeclRef = baseTypeDeclRef.As<InterfaceDecl>())
+ {
+ // The type is stating that it conforms to an interface.
+ // We need to check that it provides all of the members
+ // required by that interface.
+ checkInterfaceConformance(
+ typeDecl,
+ inheritanceDecl,
+ baseInterfaceDeclRef);
+ return;
+ }
+ }
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::unimplemented, "type not supported for inheritance");
+ }
+
+ // Check that the type declaration `typeDecl`, which
+ // declares that it inherits from another type via
+ // `inheritanceDecl` actually does what it needs to
+ // for that inheritance to be valid.
+ void checkConformance(
+ AggTypeDecl* typeDecl,
+ InheritanceDecl* inheritanceDecl)
+ {
+ // Look at the type being inherited from, and validate
+ // appropriately.
+ auto baseType = inheritanceDecl->base.type;
+ checkConformanceToType(typeDecl, inheritanceDecl, baseType);
+ }
+
+ void visitAggTypeDecl(AggTypeDecl* decl)
{
- if (structNode->IsChecked(DeclCheckState::Checked))
+ if (decl->IsChecked(DeclCheckState::Checked))
return;
- structNode->SetCheckState(DeclCheckState::Checked);
- for (auto field : structNode->GetFields())
+ // TODO: we should check inheritance declarations
+ // first, since they need to be validated before
+ // we can make use of the type (e.g., you need
+ // to know that `A` inherits from `B` in order
+ // to check an expression like `aValue.bMethod()`
+ // where `aValue` is of type `A` but `bMethod`
+ // is defined in type `B`.
+ //
+ // TODO: We should also add a pass that takes
+ // all the stated inheritance relationships,
+ // expands them to include implicitic inheritance,
+ // and then linearizes them. This would allow
+ // later passes that need to know everything
+ // a type inherits from to proceed linearly
+ // through the list, rather than having to
+ // recurse (and potentially see the same interface
+ // more than once).
+
+ decl->SetCheckState(DeclCheckState::CheckedHeader);
+
+ // Now check all of the member declarations.
+ for (auto member : decl->Members)
{
- checkDecl(field);
+ checkDecl(member);
}
+
+ // After we've checked members, we need to go through
+ // any inheritance clauses on the type itself, and
+ // confirm that the type actually provides whatever
+ // those clauses require.
+
+ if (auto interfaceDecl = dynamic_cast<InterfaceDecl*>(decl))
+ {
+ // Don't check that an interface conforms to the
+ // things it inherits from.
+ }
+ else
+ {
+ // For non-interface types we need to check conformance.
+ //
+ // TODO: Need to figure out what this should do for
+ // `abstract` types if we ever add them. Should they
+ // be required to implement all interface requirements,
+ // just with `abstract` methods that replicate things?
+ // (That's what C# does).
+
+ for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
+ {
+ checkConformance(decl, inheritanceDecl);
+ }
+ }
+
+ decl->SetCheckState(DeclCheckState::Checked);
}
void visitDeclGroup(DeclGroup* declGroup)
@@ -2751,38 +3044,34 @@ namespace Slang
// Default behavior is to look at all available `__subscript`
// declarations on the type and try to call one of them.
- if (auto declRefType = baseType->AsDeclRefType())
{
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ getName("operator[]"),
+ baseType);
+ if (!lookupResult.isValid())
{
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- // Note(tfoley): The name used for lookup here is a bit magical, since
- // it must match what the parser installed in subscript declarations.
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, getName("operator[]"), aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- goto fail;
- }
-
- RefPtr<Expr> subscriptFuncExpr = createLookupResultExpr(
- lookupResult, subscriptExpr->BaseExpression, subscriptExpr->loc);
+ goto fail;
+ }
- // Now that we know there is at least one subscript member,
- // we will construct a reference to it and try to call it
+ // Now that we know there is at least one subscript member,
+ // we will construct a reference to it and try to call it.
+ //
+ // Note: the expression may be an `OverloadedExpr`, in which
+ // case the attempt to call it will trigger overload
+ // resolution.
+ RefPtr<Expr> subscriptFuncExpr = createLookupResultExpr(
+ lookupResult, subscriptExpr->BaseExpression, subscriptExpr->loc);
- RefPtr<InvokeExpr> subscriptCallExpr = new InvokeExpr();
- subscriptCallExpr->loc = subscriptExpr->loc;
- subscriptCallExpr->FunctionExpr = subscriptFuncExpr;
+ RefPtr<InvokeExpr> subscriptCallExpr = new InvokeExpr();
+ subscriptCallExpr->loc = subscriptExpr->loc;
+ subscriptCallExpr->FunctionExpr = subscriptFuncExpr;
- // TODO(tfoley): This path can support multiple arguments easily
- subscriptCallExpr->Arguments.Add(subscriptExpr->IndexExpression);
+ // TODO(tfoley): This path can support multiple arguments easily
+ subscriptCallExpr->Arguments.Add(subscriptExpr->IndexExpression);
- return CheckInvokeExprWithCheckedOperands(subscriptCallExpr.Ptr());
- }
+ return CheckInvokeExprWithCheckedOperands(subscriptCallExpr.Ptr());
}
fail:
@@ -2987,9 +3276,59 @@ namespace Slang
vectorType->elementCount);
}
- bool DoesTypeConformToInterface(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef)
+ struct TypeWitnessBreadcrumb
+ {
+ TypeWitnessBreadcrumb* prev;
+ DeclRef<Decl> declRef;
+ };
+
+ RefPtr<Val> createTypeWitness(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ TypeWitnessBreadcrumb* inBreadcrumbs)
+ {
+ if(!inBreadcrumbs)
+ {
+ // We need to construct a witness to the fact
+ // that `type` has been proven to be equal
+ // to `interfaceDeclRef`.
+ //
+ SLANG_UNEXPECTED("reflexive type witness");
+ return nullptr;
+ }
+
+ auto breadcrumbs = inBreadcrumbs;
+
+ auto bb = breadcrumbs;
+ breadcrumbs = breadcrumbs->prev;
+
+ if(breadcrumbs)
+ {
+ // There are multiple steps in the proof, so
+ // we need a transitive witness to show that
+ // because `A : B` and `B : C` then `A : C`
+ //
+ SLANG_UNEXPECTED("transitive type witness");
+ return nullptr;
+ }
+
+ // Simple case: we have a single declaration
+ // that shows that `type` conforms to `interfaceDeclRef`.
+ //
+
+ RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
+ witness->sub = type;
+ witness->sup = DeclRefType::Create(getSession(), interfaceDeclRef);
+ witness->declRef = bb->declRef;
+ return witness;
+ }
+
+ bool doesTypeConformToInterfaceImpl(
+ RefPtr<Type> originalType,
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ RefPtr<Val>* outWitness,
+ TypeWitnessBreadcrumb* inBreadcrumbs)
{
// for now look up a conformance member...
if(auto declRefType = type->As<DeclRefType>())
@@ -3002,7 +3341,13 @@ namespace Slang
// the interface needs to be "object-safe" for us to
// really make this determination...
if(declRef == interfaceDeclRef)
+ {
+ if(outWitness)
+ {
+ *outWitness = createTypeWitness(originalType, interfaceDeclRef, inBreadcrumbs);
+ }
return true;
+ }
if( auto aggTypeDeclRef = declRef.As<AggTypeDecl>() )
{
@@ -3022,8 +3367,18 @@ namespace Slang
// conformances multiple times.
auto inheritedType = getBaseType(inheritanceDeclRef);
- if(DoesTypeConformToInterface(inheritedType, interfaceDeclRef))
+
+ // We need to ensure that the witness that gets created
+ // is a composite one, reflecting lookup through
+ // the inheritance declaration.
+ TypeWitnessBreadcrumb breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.declRef = inheritanceDeclRef;
+
+ if(doesTypeConformToInterfaceImpl(originalType, inheritedType, interfaceDeclRef, outWitness, &breadcrumb))
+ {
return true;
+ }
}
}
else if( auto genericTypeParamDeclRef = declRef.As<GenericTypeParamDecl>() )
@@ -3045,8 +3400,18 @@ namespace Slang
if(subDeclRef->declRef != genericTypeParamDeclRef)
continue;
- if(DoesTypeConformToInterface(sup, interfaceDeclRef))
+ // The witness that we create needs to reflect that
+ // it found the needed conformance by lookup through
+ // a generic type constraint.
+
+ TypeWitnessBreadcrumb breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.declRef = constraintDeclRef;
+
+ if(doesTypeConformToInterfaceImpl(originalType, sup, interfaceDeclRef, outWitness, &breadcrumb))
+ {
return true;
+ }
}
}
}
@@ -3055,6 +3420,22 @@ namespace Slang
return false;
}
+ bool DoesTypeConformToInterface(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ return doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, nullptr, nullptr);
+ }
+
+ RefPtr<Val> tryGetInterfaceConformanceWitness(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ RefPtr<Val> result;
+ doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, &result, nullptr);
+ return result;
+ }
+
RefPtr<Type> TryJoinTypeWithInterface(
RefPtr<Type> type,
DeclRef<InterfaceDecl> interfaceDeclRef)
@@ -3308,6 +3689,7 @@ namespace Slang
ArityChecked,
FixityChecked,
TypeChecked,
+ DirectionChecked,
Appicable,
};
Status status = Status::Unchecked;
@@ -3324,6 +3706,11 @@ namespace Slang
// How much conversion cost should be considered for this overload,
// when ranking candidates.
ConversionCost conversionCostSum = kConversionCost_None;
+
+ // When required, a candidate can store a pre-checked list of
+ // arguments so that we don't have to repeat work across checking
+ // phases. Currently this is only needed for generics.
+ RefPtr<Substitutions> subst;
};
@@ -3541,6 +3928,12 @@ namespace Slang
{
auto genericDeclRef = candidate.item.declRef.As<GenericDecl>();
+ // We will go ahead and hang onto the arguments that we've
+ // already checked, since downstream validation might need
+ // them.
+ candidate.subst = new Substitutions();
+ auto& checkedArgs = candidate.subst->args;
+
int aa = 0;
for (auto memberRef : getMembers(genericDeclRef))
{
@@ -3548,17 +3941,20 @@ namespace Slang
{
auto arg = context.getArg(aa++);
+ TypeExp typeExp;
if (context.mode == OverloadResolveContext::Mode::JustTrying)
{
- if (!CanCoerceToProperType(TypeExp(arg)))
+ typeExp = tryCoerceToProperType(TypeExp(arg));
+ if(!typeExp.type)
{
return false;
}
}
else
{
- TypeExp typeExp = CoerceToProperType(TypeExp(arg));
+ typeExp = CoerceToProperType(TypeExp(arg));
}
+ checkedArgs.Add(typeExp.type);
}
else if (auto valParamRef = memberRef.As<GenericValueParamDecl>())
{
@@ -3573,11 +3969,10 @@ namespace Slang
}
candidate.conversionCostSum += cost;
}
- else
- {
- arg = Coerce(GetType(valParamRef), arg);
- auto val = ExtractGenericArgInteger(arg);
- }
+
+ arg = Coerce(GetType(valParamRef), arg);
+ auto val = ExtractGenericArgInteger(arg);
+ checkedArgs.Add(val);
}
else
{
@@ -3585,6 +3980,7 @@ namespace Slang
}
}
+ // Okay, we've made it!
return true;
}
@@ -3650,6 +4046,100 @@ namespace Slang
return true;
}
+ // Create a witness that attests to the fact that `type`
+ // is equal to itself.
+ RefPtr<Val> createTypeEqualityWitness(
+ Type* type)
+ {
+ SLANG_UNEXPECTED("unimplemented");
+ }
+
+ // If `sub` is a subtype of `sup`, then return a value that
+ // can serve as a "witness" for that fact.
+ RefPtr<Val> tryGetSubtypeWitness(
+ RefPtr<Type> sub,
+ RefPtr<Type> sup)
+ {
+ if(sub->Equals(sup))
+ {
+ // They are the same type, so we just need a witness
+ // for type equality.
+ return createTypeEqualityWitness(sub);
+ }
+
+ if(auto supDeclRefType = sup->As<DeclRefType>())
+ {
+ auto supDeclRef = supDeclRefType->declRef;
+ if(auto supInterfaceDeclRef = supDeclRef.As<InterfaceDecl>())
+ {
+ if(auto witness = tryGetInterfaceConformanceWitness(sub, supInterfaceDeclRef))
+ {
+ return witness;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ // In the case where we are explicitly applying a generic
+ // to arguments (e.g., `G<A,B>`) check that the constraints
+ // on those parameters are satisfied.
+ //
+ // Note: the constraints actually work as additional parameters/arguments
+ // of the generic, and so we need to reify them into the final
+ // argument list.
+ //
+ bool TryCheckOverloadCandidateConstraints(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+ {
+ // We only need this step for generics, so always succeed on
+ // everything else.
+ if(candidate.flavor != OverloadCandidate::Flavor::Generic)
+ return true;
+
+ auto genericDeclRef = candidate.item.declRef.As<GenericDecl>();
+ assert(genericDeclRef);
+
+ // We should have the existing arguments to the generic
+ // handy, so that we can construct a substitution list.
+
+ RefPtr<Substitutions> subst = candidate.subst;
+ assert(subst);
+
+ subst->genericDecl = genericDeclRef.getDecl();
+ subst->outer = genericDeclRef.substitutions;
+
+ for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
+ {
+ DeclRef<GenericTypeConstraintDecl> constraintDeclRef(
+ constraintDecl,
+ subst);
+
+ auto sub = GetSub(constraintDeclRef);
+ auto sup = GetSup(constraintDeclRef);
+
+ auto subTypeWitness = tryGetSubtypeWitness(sub, sup);
+ if(subTypeWitness)
+ {
+ subst->args.Add(subTypeWitness);
+ }
+ else
+ {
+ if(context.mode != OverloadResolveContext::Mode::JustTrying)
+ {
+ // TODO: diagnose a problem here
+ getSink()->diagnose(context.loc, Diagnostics::unimplemented, "generic constraint not satisfied");
+ }
+ return false;
+ }
+ }
+
+ // Done checking all the constraints, hooray.
+ return true;
+ }
+
// Try to check an overload candidate, but bail out
// if any step fails
void TryCheckOverloadCandidate(
@@ -3671,15 +4161,18 @@ namespace Slang
if (!TryCheckOverloadCandidateDirections(context, candidate))
return;
+ candidate.status = OverloadCandidate::Status::DirectionChecked;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ return;
+
candidate.status = OverloadCandidate::Status::Appicable;
}
// Create the representation of a given generic applied to some arguments
- RefPtr<Expr> CreateGenericDeclRef(
- RefPtr<Expr> baseExpr,
- RefPtr<Expr> originalExpr,
- UInt argCount,
- RefPtr<Expr> const* args)
+ RefPtr<Expr> createGenericDeclRef(
+ RefPtr<Expr> baseExpr,
+ RefPtr<Expr> originalExpr,
+ RefPtr<Substitutions> subst)
{
auto baseDeclRefExpr = baseExpr.As<DeclRefExpr>();
if (!baseDeclRefExpr)
@@ -3694,16 +4187,9 @@ namespace Slang
return CreateErrorExpr(originalExpr);
}
- RefPtr<Substitutions> subst = new Substitutions();
subst->genericDecl = baseGenericRef.getDecl();
subst->outer = baseGenericRef.substitutions;
- for(UInt aa = 0; aa < argCount; ++aa)
- {
- auto arg = args[aa];
- subst->args.Add(ExtractGenericArgVal(arg));
- }
-
DeclRef<Decl> innerDeclRef(GetInner(baseGenericRef), subst);
return ConstructDeclRefExpr(
@@ -3753,6 +4239,9 @@ namespace Slang
if (!TryCheckOverloadCandidateDirections(context, candidate))
goto error;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ goto error;
+
{
auto baseExpr = ConstructLookupResultExpr(
candidate.item, context.baseExpr, context.funcLoc);
@@ -3792,9 +4281,10 @@ namespace Slang
break;
case OverloadCandidate::Flavor::Generic:
- return CreateGenericDeclRef(baseExpr, context.originalExpr,
- context.argCount,
- context.args);
+ return createGenericDeclRef(
+ baseExpr,
+ context.originalExpr,
+ candidate.subst);
break;
default:
@@ -5144,7 +5634,7 @@ namespace Slang
expr->type = QualType(getSession()->getErrorType());
- auto lookupResult = LookUp(
+ auto lookupResult = lookUp(
getSession(),
this, expr->name, expr->scope);
if (lookupResult.isValid())
@@ -5449,6 +5939,12 @@ namespace Slang
// Note: Checking for vector types before declaration-reference types,
// because vectors are also declaration reference types...
+ //
+ // Also note: the way this is done right now means that the ability
+ // to swizzle vectors interferes with any chance of looking up
+ // members via extension, for vector or scalar types.
+ //
+ // TODO: Matrix swizzles probably need to be handled at some point.
if (auto baseVecType = baseType->AsVectorType())
{
return CheckSwizzleExpr(
@@ -5473,69 +5969,45 @@ namespace Slang
// TODO: this duplicates a *lot* of logic with the case below.
// We need to fix that.
auto type = typeType->type;
- if(auto declRefType = type->AsDeclRefType())
- {
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
- {
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, expr->name, aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- return lookupResultFailure(expr, baseType);
- }
- // TODO: need to filter for declarations that are valid to refer
- // to in this context...
-
- return createLookupResultExpr(
- lookupResult,
- expr->BaseExpression,
- expr->loc);
- }
- }
- }
- else if (auto declRefType = baseType->AsDeclRefType())
- {
- if (auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ expr->name,
+ type);
+ if (!lookupResult.isValid())
{
- // Checking of the type must be complete before we can reference its members safely
- EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked);
-
- LookupResult lookupResult = LookUpLocal(
- getSession(),
- this, expr->name, aggTypeDeclRef);
- if (!lookupResult.isValid())
- {
- return lookupResultFailure(expr, baseType);
- }
-
- return createLookupResultExpr(
- lookupResult,
- expr->BaseExpression,
- expr->loc);
+ return lookupResultFailure(expr, baseType);
}
- // catch-all
- return lookupResultFailure(expr, baseType);
+ // TODO: need to filter for declarations that are valid to refer
+ // to in this context...
+
+ return createLookupResultExpr(
+ lookupResult,
+ expr->BaseExpression,
+ expr->loc);
}
- // All remaining cases assume we have a `BasicType`
- else if (!baseType->AsBasicType())
- expr->type = QualType(getSession()->getErrorType());
else
- expr->type = QualType(getSession()->getErrorType());
- if (!baseType->Equals(getSession()->getErrorType()) &&
- expr->type->Equals(getSession()->getErrorType()))
{
- if (!isRewriteMode())
+ LookupResult lookupResult = lookUpMember(
+ getSession(),
+ this,
+ expr->name,
+ baseType.Ptr());
+ if (!lookupResult.isValid())
{
- getSink()->diagnose(expr, Diagnostics::typeHasNoPublicMemberOfName, baseType, expr->name);
+ return lookupResultFailure(expr, baseType);
}
+
+ // TODO: need to filter for declarations that are valid to refer
+ // to in this context...
+
+ return createLookupResultExpr(
+ lookupResult,
+ expr->BaseExpression,
+ expr->loc);
}
- return expr;
}
SemanticsVisitor & operator = (const SemanticsVisitor &) = delete;
@@ -5810,6 +6282,14 @@ namespace Slang
*outTypeResult = type;
return QualType(getTypeType(type));
}
+ else if (auto constraintDeclRef = declRef.As<GenericTypeConstraintDecl>())
+ {
+ // When we access a constraint or an inheritance decl (as a member),
+ // we are conceptually performing a "cast" to the given super-type,
+ // with the declaration showing that such a cast is legal.
+ auto type = GetSup(constraintDeclRef);
+ return QualType(type);
+ }
else if (auto funcDeclRef = declRef.As<CallableDecl>())
{
auto type = getFuncType(session, funcDeclRef);
@@ -5842,4 +6322,57 @@ namespace Slang
return semantics->ApplyExtensionToType(extDecl, type);
}
+ // 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).
+ //
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl,
+ Substitutions* parentSubst)
+ {
+ auto dd = decl->ParentDecl;
+ if( auto genericDecl = dynamic_cast<GenericDecl*>(dd) )
+ {
+ // We don't want to specialize references to anything
+ // other than the "inner" declaration itself.
+ if(decl != genericDecl->inner)
+ return parentSubst;
+
+ RefPtr<Substitutions> subst = new Substitutions();
+ subst->genericDecl = genericDecl;
+ subst->outer = parentSubst;
+
+ for( auto mm : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = mm.As<GenericTypeParamDecl>() )
+ {
+ subst->args.Add(DeclRefType::Create(session, makeDeclRef(genericTypeParamDecl.Ptr())));
+ }
+ else if( auto genericValueParamDecl = mm.As<GenericValueParamDecl>() )
+ {
+ subst->args.Add(new GenericParamIntVal(makeDeclRef(genericValueParamDecl.Ptr())));
+ }
+ }
+
+ // TODO: need to fill in constraints here...
+
+ return subst;
+ }
+ return parentSubst;
+ }
+
+ RefPtr<Substitutions> createDefaultSubstitutions(
+ Session* session,
+ Decl* decl)
+ {
+ RefPtr<Substitutions> subst;
+ if( auto parentDecl = decl->ParentDecl )
+ {
+ subst = createDefaultSubstitutions(session, parentDecl);
+ }
+ subst = createDefaultSubstitutions(session, decl, subst);
+ return subst;
+ }
+
}