diff options
| author | Yong He <yonghe@outlook.com> | 2025-06-13 22:13:00 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-13 22:13:00 -0700 |
| commit | 6a23949f07f4eba38086b656e7073ce3bf8cd2fe (patch) | |
| tree | 132bbe330b6027d323c74175686d006605e4da6d /source/slang/slang-check-decl.cpp | |
| parent | e72b3325663ab6d4bb791742574b031f0df6328a (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.cpp | 131 |
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) |
