From 6a23949f07f4eba38086b656e7073ce3bf8cd2fe Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 13 Jun 2025 22:13:00 -0700 Subject: Allow interface methods to have default implementations. (#7439) --- source/slang/slang-check-decl.cpp | 131 +++++++++++++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 8 deletions(-) (limited to 'source/slang/slang-check-decl.cpp') 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 declRef) +{ + if (auto modifier = declRef.getDecl()->findModifier()) + return modifier; + if (auto genericParent = as(declRef.getDecl()->parentDecl)) + return genericParent->findModifier(); + 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(lookupResult.item.declRef.getDecl())) < - getShared()->getFuncDifferentiableLevel( - as(requiredMemberDeclRef.getDecl()))) + if (lookupResult.isValid()) { - return false; + if (!isInWrapperType && getShared()->getFuncDifferentiableLevel( + as(lookupResult.item.declRef.getDecl())) < + getShared()->getFuncDifferentiableLevel( + as(requiredMemberDeclRef.getDecl()))) + { + return false; + } } ThisExpr* synThis = nullptr; @@ -6899,6 +6913,95 @@ bool SemanticsVisitor::trySynthesizeDifferentialMethodRequirementWitness( return true; } +bool SemanticsVisitor::findDefaultInterfaceImpl( + ConformanceCheckingContext* context, + DeclRef requiredMemberDeclRef, + RefPtr witnessTable) +{ + // Only functions can have default implemnetation at the moment. + DeclRef requiredFuncDeclRef = requiredMemberDeclRef.as(); + if (!requiredFuncDeclRef) + { + // If requiredMember is a generic func, form a direct declref to the inner func. + if (auto requiredGenericDeclRef = requiredMemberDeclRef.as()) + { + auto inner = getInner(requiredGenericDeclRef); + if (auto func = as(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 + // 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(interfaceDeclRef.getDecl())) + return false; + + auto genericDeclOfDefaultImplStub = + as(defaultModifier->defaultImplDecl); + if (!genericDeclOfDefaultImplStub) + return false; + + // Form a declref to unspecialized `IFoo::bar_defaultImpl`. + DeclRef resultDeclRef = + m_astBuilder->getMemberDeclRef(interfaceDeclRef, genericDeclOfDefaultImplStub); + List specArgs; + specArgs.add(context->conformingType); + specArgs.add(context->conformingWitness); + + // Form a declref to `IFoo::bar_defaultImpl`. + resultDeclRef = m_astBuilder->getGenericAppDeclRef( + resultDeclRef.as(), + 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(resultDeclRef)) + resultDeclRef = m_astBuilder->getMemberDeclRef(genDeclRef, genDeclRef.getDecl()->inner); + + // Register the declref to the witness table. + auto callableDeclRef = as(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() || @@ -7059,6 +7163,9 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement( as(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()) continue; + if (as(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 = inheritanceDecl->witnessTable; if (!witnessTable) -- cgit v1.2.3