summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-check-decl.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2025-06-13 22:13:00 -0700
committerGitHub <noreply@github.com>2025-06-13 22:13:00 -0700
commit6a23949f07f4eba38086b656e7073ce3bf8cd2fe (patch)
tree132bbe330b6027d323c74175686d006605e4da6d /source/slang/slang-check-decl.cpp
parente72b3325663ab6d4bb791742574b031f0df6328a (diff)
Allow interface methods to have default implementations. (#7439)
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
-rw-r--r--source/slang/slang-check-decl.cpp131
1 files changed, 123 insertions, 8 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index ddc4ec4d5..8471acb0a 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -738,6 +738,7 @@ struct SemanticsDeclReferenceVisitor : public SemanticsDeclVisitorBase,
void visitThisExpr(ThisExpr*) { return; }
void visitThisTypeExpr(ThisTypeExpr*) { return; }
+ void visitThisInterfaceExpr(ThisInterfaceExpr*) { return; }
void visitAndTypeExpr(AndTypeExpr* expr)
{
dispatchIfNotNull(expr->left.type);
@@ -5043,6 +5044,17 @@ static void removeNonStaticLookupItems(LookupResult& lookupResult)
}
}
+// Returns true if declRef points to an interface method requirement that has a default
+// implementation.
+HasInterfaceDefaultImplModifier* hasDefaultImpl(DeclRef<Decl> declRef)
+{
+ if (auto modifier = declRef.getDecl()->findModifier<HasInterfaceDefaultImplModifier>())
+ return modifier;
+ if (auto genericParent = as<GenericDecl>(declRef.getDecl()->parentDecl))
+ return genericParent->findModifier<HasInterfaceDefaultImplModifier>();
+ return nullptr;
+}
+
bool SemanticsVisitor::trySynthesizeMethodRequirementWitness(
ConformanceCheckingContext* context,
LookupResult const& lookupResult,
@@ -5091,17 +5103,19 @@ bool SemanticsVisitor::trySynthesizeMethodRequirementWitness(
// With the big picture spelled out, we can settle into
// the work of constructing our synthesized method.
//
-
bool isInWrapperType = isWrapperTypeDecl(context->parentDecl);
// First, we check that the differentiabliity of the method matches the requirement,
// and we don't attempt to synthesize a method if they don't match.
- if (!isInWrapperType && getShared()->getFuncDifferentiableLevel(
- as<FunctionDeclBase>(lookupResult.item.declRef.getDecl())) <
- getShared()->getFuncDifferentiableLevel(
- as<FunctionDeclBase>(requiredMemberDeclRef.getDecl())))
+ if (lookupResult.isValid())
{
- return false;
+ if (!isInWrapperType && getShared()->getFuncDifferentiableLevel(
+ as<FunctionDeclBase>(lookupResult.item.declRef.getDecl())) <
+ getShared()->getFuncDifferentiableLevel(
+ as<FunctionDeclBase>(requiredMemberDeclRef.getDecl())))
+ {
+ return false;
+ }
}
ThisExpr* synThis = nullptr;
@@ -6899,6 +6913,95 @@ bool SemanticsVisitor::trySynthesizeDifferentialMethodRequirementWitness(
return true;
}
+bool SemanticsVisitor::findDefaultInterfaceImpl(
+ ConformanceCheckingContext* context,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+{
+ // Only functions can have default implemnetation at the moment.
+ DeclRef<FuncDecl> requiredFuncDeclRef = requiredMemberDeclRef.as<FuncDecl>();
+ if (!requiredFuncDeclRef)
+ {
+ // If requiredMember is a generic func, form a direct declref to the inner func.
+ if (auto requiredGenericDeclRef = requiredMemberDeclRef.as<GenericDecl>())
+ {
+ auto inner = getInner(requiredGenericDeclRef);
+ if (auto func = as<FuncDecl>(inner))
+ {
+ requiredFuncDeclRef = m_astBuilder->getMemberDeclRef(requiredGenericDeclRef, func);
+ }
+ }
+ }
+ if (!requiredFuncDeclRef)
+ return false;
+
+ // If the interface requirement comes with a default impl, it should have a
+ // HasInterfaceDefaultImplModifier.
+ auto defaultModifier = hasDefaultImpl(requiredMemberDeclRef);
+ if (!defaultModifier)
+ return false;
+
+ // If we have a default implementation, return a declref to the default stub decl directly.
+ // We can grab a declref to the stub decl standing for the generic default impl from
+ // the InterfaceDefaultImplModifier on the requiredMemberDeclRef, that should have been
+ // created during checking of the required method decl.
+ //
+ // For example, given:
+ // ```
+ // interface IFoo { int bar() { return 1; } }
+ // struct Foo : IFoo { /*needing witness synthesis*/ }
+ // ```
+ // After parsing `IFoo`, we will have a default impl stub decl for `bar`:
+ // ```
+ // interface IFoo {
+ // [HasInterfaceDefaultImpl(bar_defaultImpl)] // <-- `defaultModifier`
+ // int bar(); // this is the requirement.
+ //
+ // // This is the default impl stub decl.
+ // __interface_default_impl_generic<This:IFoo>
+ // int bar_defaultImpl() { return 1; }
+ // }
+ // ```
+ // Given this, we can simply form a GenericAppDeclRef to `IFoo::bar_defaultImpl`
+ // with `This` being `context->conformingType` and `This:IFoo` being
+ // `context->conformingWitness`.
+ //
+ // The following logic will create this declref, and register it to the witness table.
+ //
+ auto interfaceDeclRef = getParentDeclRef(requiredMemberDeclRef);
+ if (!as<InterfaceDecl>(interfaceDeclRef.getDecl()))
+ return false;
+
+ auto genericDeclOfDefaultImplStub =
+ as<InterfaceDefaultImplDecl>(defaultModifier->defaultImplDecl);
+ if (!genericDeclOfDefaultImplStub)
+ return false;
+
+ // Form a declref to unspecialized `IFoo::bar_defaultImpl`.
+ DeclRef<Decl> resultDeclRef =
+ m_astBuilder->getMemberDeclRef(interfaceDeclRef, genericDeclOfDefaultImplStub);
+ List<Val*> specArgs;
+ specArgs.add(context->conformingType);
+ specArgs.add(context->conformingWitness);
+
+ // Form a declref to `IFoo::bar_defaultImpl<conformingType>`.
+ resultDeclRef = m_astBuilder->getGenericAppDeclRef(
+ resultDeclRef.as<GenericDecl>(),
+ specArgs.getArrayView());
+
+ // If `bar_defaultImpl` is a generic method, we need to form an explicit
+ // declref to the inner method decl to stay consistent the existing format of
+ // witness table entries.
+ if (auto genDeclRef = as<GenericDecl>(resultDeclRef))
+ resultDeclRef = m_astBuilder->getMemberDeclRef(genDeclRef, genDeclRef.getDecl()->inner);
+
+ // Register the declref to the witness table.
+ auto callableDeclRef = as<CallableDecl>(resultDeclRef);
+ SLANG_ASSERT(callableDeclRef);
+ _addMethodWitness(witnessTable, requiredFuncDeclRef, callableDeclRef);
+ return true;
+}
+
bool SemanticsVisitor::findWitnessForInterfaceRequirement(
ConformanceCheckingContext* context,
Type* subType,
@@ -7043,7 +7146,8 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement(
{
// If we failed to look up a member with the name of the
// requirement, it may be possible that we can still synthesis the
- // implementation if this is one of the known builtin requirements.
+ // implementation if this is one of the known builtin requirements,
+ // or if the interface method contains a default impl.
// Otherwise, report diagnostic now.
if (requiredMemberDeclRef.getDecl()->hasModifier<BuiltinRequirementModifier>() ||
@@ -7059,6 +7163,9 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement(
as<MatrixExpressionType>(context->conformingType)))
{
}
+ else if (hasDefaultImpl(requiredMemberDeclRef))
+ {
+ }
else
{
getSink()->diagnose(
@@ -7147,6 +7254,12 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement(
return true;
}
+ // Finally, if there is a default implementation for the required member,
+ // we can use that as a witness.
+ //
+ if (findDefaultInterfaceImpl(context, requiredMemberDeclRef, witnessTable))
+ return true;
+
// We failed to find a member of the type that can be used
// to satisfy the requirement (even via synthesis), so we
// need to report the failure to the user.
@@ -7312,6 +7425,8 @@ bool SemanticsVisitor::checkInterfaceConformance(
continue;
if (requiredMemberDecl.as<DerivativeRequirementDecl>())
continue;
+ if (as<InterfaceDefaultImplDecl>(requiredMemberDecl.getDecl()))
+ continue;
ensureDecl(requiredMemberDecl, DeclCheckState::ReadyForReference);
auto requiredMemberDeclRef = m_astBuilder->getLookupDeclRef(
subTypeConformsToSuperInterfaceWitness,
@@ -7512,7 +7627,7 @@ bool SemanticsVisitor::checkConformance(
ConformanceCheckingContext context;
context.conformingType = subType;
context.parentDecl = parentDecl;
-
+ context.conformingWitness = subIsSuperWitness;
RefPtr<WitnessTable> witnessTable = inheritanceDecl->witnessTable;
if (!witnessTable)