summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-ast-decl.h8
-rw-r--r--source/slang/slang-ast-expr.h11
-rw-r--r--source/slang/slang-ast-iterator.h4
-rw-r--r--source/slang/slang-ast-modifier.h8
-rw-r--r--source/slang/slang-check-decl.cpp131
-rw-r--r--source/slang/slang-check-expr.cpp29
-rw-r--r--source/slang/slang-check-impl.h10
-rw-r--r--source/slang/slang-language-server-ast-lookup.cpp2
-rw-r--r--source/slang/slang-lookup.cpp29
-rw-r--r--source/slang/slang-lower-to-ir.cpp76
-rw-r--r--source/slang/slang-parser.cpp148
-rw-r--r--source/slang/slang-syntax.h10
12 files changed, 417 insertions, 49 deletions
diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h
index 3410806dc..c46878945 100644
--- a/source/slang/slang-ast-decl.h
+++ b/source/slang/slang-ast-decl.h
@@ -823,6 +823,14 @@ class GenericDecl : public ContainerDecl
FIDDLE() Decl* inner = nullptr;
};
+FIDDLE()
+class InterfaceDefaultImplDecl : public GenericDecl
+{
+ FIDDLE(...)
+ FIDDLE() GenericTypeParamDecl* thisTypeDecl;
+ FIDDLE() GenericTypeConstraintDecl* thisTypeConstraintDecl;
+};
+
FIDDLE(abstract)
class GenericTypeParamDeclBase : public SimpleTypeDecl
{
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 950fd6645..177feff15 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -734,6 +734,17 @@ class ThisTypeExpr : public Expr
Scope* scope = nullptr;
};
+
+/// A type expression of the form `ThisInterface`
+///
+/// Refers to the interface type itself, not the conforming type from an interface decl.
+///
+FIDDLE()
+class ThisInterfaceExpr : public VarExpr
+{
+ FIDDLE(...)
+};
+
/// A type expression of the form `Left & Right`.
FIDDLE()
class AndTypeExpr : public Expr
diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h
index 4c866fc5a..094a9c1a2 100644
--- a/source/slang/slang-ast-iterator.h
+++ b/source/slang/slang-ast-iterator.h
@@ -245,6 +245,10 @@ struct ASTIterator
void visitThisExpr(ThisExpr* expr) { iterator->maybeDispatchCallback(expr); }
void visitThisTypeExpr(ThisTypeExpr* expr) { iterator->maybeDispatchCallback(expr); }
+ void visitThisInterfaceExpr(ThisInterfaceExpr* expr)
+ {
+ iterator->maybeDispatchCallback(expr);
+ }
void visitReturnValExpr(ReturnValExpr* expr) { iterator->maybeDispatchCallback(expr); }
void visitAndTypeExpr(AndTypeExpr* expr)
diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h
index 10ebc2841..4f6a08e4f 100644
--- a/source/slang/slang-ast-modifier.h
+++ b/source/slang/slang-ast-modifier.h
@@ -731,6 +731,14 @@ class HLSLVolatileModifier : public Modifier
FIDDLE(...)
};
+// Indicate that an interface method requirement has a default impl.
+FIDDLE()
+class HasInterfaceDefaultImplModifier : public Modifier
+{
+ FIDDLE(...)
+public:
+ FIDDLE() Decl* defaultImplDecl = nullptr;
+};
FIDDLE()
class AttributeTargetModifier : public Modifier
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)
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 9f5c1dc2e..4c6bf98d2 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -5346,6 +5346,12 @@ Expr* SemanticsExprVisitor::visitThisExpr(ThisExpr* expr)
}
return expr;
}
+ else if (auto defaultImplDecl = as<InterfaceDefaultImplDecl>(containerDecl))
+ {
+ expr->type.type =
+ DeclRefType::create(m_astBuilder, DeclRef<Decl>(defaultImplDecl->thisTypeDecl));
+ return expr;
+ }
#if 0
else if (auto aggTypeDecl = as<AggTypeDecl>(containerDecl))
{
@@ -5401,7 +5407,12 @@ Expr* SemanticsExprVisitor::visitThisTypeExpr(ThisTypeExpr* expr)
expr->type.type = thisTypeType;
return expr;
}
-
+ else if (auto defaultImplDecl = as<InterfaceDefaultImplDecl>(containerDecl))
+ {
+ expr->type.type =
+ DeclRefType::create(m_astBuilder, DeclRef<Decl>(defaultImplDecl->thisTypeDecl));
+ return expr;
+ }
scope = scope->parent;
}
@@ -5409,6 +5420,22 @@ Expr* SemanticsExprVisitor::visitThisTypeExpr(ThisTypeExpr* expr)
return CreateErrorExpr(expr);
}
+Expr* SemanticsExprVisitor::visitThisInterfaceExpr(ThisInterfaceExpr* expr)
+{
+ auto scope = expr->scope;
+
+ auto containerDecl = findParentInterfaceDecl(scope->containerDecl);
+
+ // ThisInterfaceExpr can only be synthesized by the compiler during parsing
+ // an interface decl with default implementation, so container must always
+ // be an interface decl.
+ SLANG_ASSERT(containerDecl);
+ expr->declRef =
+ createDefaultSubstitutionsIfNeeded(m_astBuilder, this, getDefaultDeclRef(containerDecl));
+ expr->type = m_astBuilder->getTypeType(DeclRefType::create(m_astBuilder, expr->declRef));
+ return expr;
+}
+
Expr* SemanticsExprVisitor::visitCastToSuperTypeExpr(CastToSuperTypeExpr* expr)
{
// CastToSuperType is effectively a struct field.
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 1cdebb115..be6b1c2fc 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1837,6 +1837,8 @@ public:
/// The type for which conformances are being checked
Type* conformingType;
+ Witness* conformingWitness;
+
/// The outer declaration for the conformances being checked (either a type or `extension`
/// declaration)
ContainerDecl* parentDecl;
@@ -2027,6 +2029,13 @@ public:
// Check and register a type if it is differentiable.
void maybeRegisterDifferentiableType(ASTBuilder* builder, Type* type);
+ // Find the default implementation of an interface requirement,
+ // and insert it to the witness table, if it exists.
+ bool findDefaultInterfaceImpl(
+ ConformanceCheckingContext* context,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable);
+
// Find the appropriate member of a declared type to
// satisfy a requirement of an interface the type
// claims to conform to.
@@ -2989,6 +2998,7 @@ public:
Expr* visitThisExpr(ThisExpr* expr);
Expr* visitThisTypeExpr(ThisTypeExpr* expr);
+ Expr* visitThisInterfaceExpr(ThisInterfaceExpr* expr);
Expr* visitCastToSuperTypeExpr(CastToSuperTypeExpr* expr);
Expr* visitReturnValExpr(ReturnValExpr* expr);
Expr* visitAndTypeExpr(AndTypeExpr* expr);
diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp
index 6c5935992..f394e7d7a 100644
--- a/source/slang/slang-language-server-ast-lookup.cpp
+++ b/source/slang/slang-language-server-ast-lookup.cpp
@@ -420,6 +420,8 @@ public:
}
bool visitThisTypeExpr(ThisTypeExpr*) { return false; }
+ bool visitThisInterfaceExpr(ThisInterfaceExpr*) { return false; }
+
bool visitAndTypeExpr(AndTypeExpr* expr)
{
if (dispatchIfNotNull(expr->left.exp))
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index d03d763b8..604cee2b7 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -977,6 +977,35 @@ static void _lookUpInScopes(
nullptr);
}
+ if (auto defaultImplDecl = as<InterfaceDefaultImplDecl>(containerDecl))
+ {
+ // If we are checking an interface default method implementation,
+ // we should look up members from implicit `this` whose type is the explicit `This`
+ // generic parameter, and skip looking up in the interface decl itself.
+
+ // Instead of looking up in the interface decl itself, we should
+ // look up in the `This` type instead.
+ if (getText(name) != "This")
+ {
+ BreadcrumbInfo breadcrumb;
+ breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::This;
+ breadcrumb.thisParameterMode = thisParameterMode;
+ breadcrumb.declRef = DeclRef<Decl>(defaultImplDecl->thisTypeDecl);
+ breadcrumb.prev = nullptr;
+ Type* type = DeclRefType::create(astBuilder, breadcrumb.declRef);
+ _lookUpMembersInType(astBuilder, name, type, request, result, &breadcrumb);
+ }
+
+ // We need to skip looking up in the interface decl itself, since we are
+ // looking up in the implicit `this` type.
+ for (; scope && !as<InterfaceDecl>(scope->containerDecl); scope = scope->parent)
+ {
+ // We need to skip looking up in the interface decl itself, since we are
+ // looking up in the implicit `this` type.
+ }
+ break;
+ }
+
// Before we proceed up to the next outer scope to perform lookup
// again, we need to consider what the current scope tells us
// about how to interpret uses of implicit `this` or `This`. For
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 596a091d4..a03c75d7d 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -1495,6 +1495,15 @@ bool shouldDeclBeTreatedAsInterfaceRequirement(Decl* requirementDecl)
else if (const auto varDecl = as<VarDeclBase>(requirementDecl))
{
}
+ else if (as<AccessorDecl>(requirementDecl))
+ {
+ }
+ else if (as<InterfaceDefaultImplDecl>(requirementDecl))
+ {
+ // A default impl stub function represents a concrete function, not
+ // a requirement.
+ return false;
+ }
else if (const auto genericDecl = as<GenericDecl>(requirementDecl))
{
return shouldDeclBeTreatedAsInterfaceRequirement(genericDecl->inner);
@@ -1511,6 +1520,11 @@ bool shouldDeclBeTreatedAsInterfaceRequirement(Decl* requirementDecl)
IRStructKey* getInterfaceRequirementKey(IRGenContext* context, Decl* requirementDecl)
{
+ // Only specific types of decls are treated as requirements, e.g. methods and asssociated types.
+ // Other types of decls are allowed but not regarded as a requirement.
+ if (!shouldDeclBeTreatedAsInterfaceRequirement(requirementDecl))
+ return nullptr;
+
// TODO: this special case logic can be removed if we also clean up
// `doesGenericSignatureMatchRequirement` Currently `doesGenericSignatureMatchRequirement` will
// use the inner func decl as the key in AST WitnessTable. Therefore we need to match this
@@ -1518,11 +1532,6 @@ IRStructKey* getInterfaceRequirementKey(IRGenContext* context, Decl* requirement
if (auto genericDecl = as<GenericDecl>(requirementDecl))
return getInterfaceRequirementKey(context, genericDecl->inner);
- // Only specific types of decls are treated as requirements, e.g. methods and asssociated types.
- // Other types of decls are allowed but not regarded as a requirement.
- if (!shouldDeclBeTreatedAsInterfaceRequirement(requirementDecl))
- return nullptr;
-
IRStructKey* requirementKey = nullptr;
if (context->shared->interfaceRequirementKeys.tryGetValue(requirementDecl, requirementKey))
{
@@ -3046,6 +3055,14 @@ static Type* _findReplacementThisParamType(IRGenContext* context, DeclRef<Decl>
return thisType;
}
+ if (auto defaultImplDeclRef = parentDeclRef.as<InterfaceDefaultImplDecl>())
+ {
+ auto thisType = DeclRefType::create(
+ context->astBuilder,
+ DeclRef<Decl>(defaultImplDeclRef.getDecl()->thisTypeDecl));
+ return thisType;
+ }
+
return nullptr;
}
@@ -3257,6 +3274,10 @@ void collectParameterLists(
ParameterListCollectMode mode,
ParameterDirection thisParamDirection)
{
+ // Don't collect any parameters beyond certain decls.
+ if (as<InterfaceDefaultImplDecl>(declRef) || as<AggTypeDeclBase>(declRef))
+ return;
+
// The parameters introduced by any "parent" declarations
// will need to come first, so we'll deal with that
// logic here.
@@ -9221,28 +9242,29 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
UInt operandCount = 0;
for (auto requirementDecl : decl->getDirectMemberDecls())
{
+ auto innerRequirementDecl = requirementDecl;
+ if (as<InterfaceDefaultImplDecl>(requirementDecl))
+ continue;
if (as<GenericDecl>(requirementDecl))
- requirementDecl = getInner(requirementDecl);
+ innerRequirementDecl = getInner(requirementDecl);
- if (as<SubscriptDecl>(requirementDecl) || as<PropertyDecl>(requirementDecl))
+ if (as<SubscriptDecl>(innerRequirementDecl) || as<PropertyDecl>(innerRequirementDecl))
{
- for (auto accessorDecl :
- as<ContainerDecl>(requirementDecl)->getDirectMemberDeclsOfType<AccessorDecl>())
+ for (auto accessorDecl : as<ContainerDecl>(innerRequirementDecl)
+ ->getDirectMemberDeclsOfType<AccessorDecl>())
{
SLANG_UNUSED(accessorDecl);
operandCount++;
}
}
if (!shouldDeclBeTreatedAsInterfaceRequirement(requirementDecl))
- {
continue;
- }
operandCount++;
// As a special case, any type constraints placed
// on an associated type will *also* need to be turned
// into requirement keys for this interface.
- if (auto associatedTypeDecl = as<AssocTypeDecl>(requirementDecl))
+ if (auto associatedTypeDecl = as<AssocTypeDecl>(innerRequirementDecl))
{
operandCount +=
associatedTypeDecl->getMembersOfType<TypeConstraintDecl>().getCount();
@@ -9374,6 +9396,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
auto requirementKey = getInterfaceRequirementKey(requirementDecl);
if (!requirementKey)
{
+ if (as<InterfaceDefaultImplDecl>(requirementDecl))
+ continue;
if (auto genericDecl = as<GenericDecl>(requirementDecl))
{
// We need to form a declref into the inner decls in case of a generic
@@ -11361,33 +11385,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
LoweredValInfo visitGenericDecl(GenericDecl* genDecl)
{
- // TODO: Should this just always visit/lower the inner decl?
-
- if (auto innerFuncDecl = as<FunctionDeclBase>(genDecl->inner))
- return ensureDecl(context, innerFuncDecl);
- else if (auto innerStructDecl = as<StructDecl>(genDecl->inner))
- {
- ensureDecl(context, innerStructDecl);
- return LoweredValInfo();
- }
- else if (auto extensionDecl = as<ExtensionDecl>(genDecl->inner))
- {
- return ensureDecl(context, extensionDecl);
- }
- else if (auto interfaceDecl = as<InterfaceDecl>(genDecl->inner))
- {
- return ensureDecl(context, interfaceDecl);
- }
- else if (auto typedefDecl = as<TypeDefDecl>(genDecl->inner))
- {
- return ensureDecl(context, typedefDecl);
- }
- else if (auto subscriptDecl = as<SubscriptDecl>(genDecl->inner))
- {
- return ensureDecl(context, subscriptDecl);
- }
- SLANG_RELEASE_ASSERT(false);
- UNREACHABLE_RETURN(LoweredValInfo());
+ return ensureDecl(context, genDecl->inner);
}
LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl)
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 49e0704bc..6d6341e4e 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -1,6 +1,7 @@
#include "slang-parser.h"
#include "../core/slang-semantic-version.h"
+#include "slang-ast-decl.h"
#include "slang-check-impl.h"
#include "slang-compiler.h"
#include "slang-lookup-spirv.h"
@@ -5123,9 +5124,103 @@ static bool parseGLSLGlobalDecl(Parser* parser, ContainerDecl* containerDecl)
return false;
}
+static void parseInterfaceDefaultMethodAsExplicitGeneric(
+ Parser* parser,
+ Decl* parsedDecl,
+ ContainerDecl* interfaceDecl)
+{
+ // If we parsed an interface method with a body,
+ // parse it again as an explicit generic decl on ThisType.
+ auto astBuilder = parser->astBuilder;
+ InterfaceDefaultImplDecl* genericDecl = astBuilder->create<InterfaceDefaultImplDecl>();
+ parser->PushScope(genericDecl);
+
+ // Create GenericTypeParamDecl for `This`.
+ auto thisTypeDecl = astBuilder->create<GenericTypeParamDecl>();
+ thisTypeDecl->nameAndLoc.name = getName(parser, "This");
+ thisTypeDecl->parameterIndex = 0;
+ genericDecl->thisTypeDecl = thisTypeDecl;
+ AddMember(genericDecl, thisTypeDecl);
+
+ // Create GenericTypeConstraintDecl for `This:IFoo`.
+ auto thisTypeConstraint = astBuilder->create<GenericTypeConstraintDecl>();
+ auto thisTypeVarExpr = astBuilder->create<VarExpr>();
+ thisTypeVarExpr->name = getName(parser, "This");
+ thisTypeVarExpr->scope = parser->currentScope;
+ thisTypeConstraint->sub.exp = thisTypeVarExpr;
+
+ // Since we can't form a DeclRef to the parent interface decl yet (because we are still parsing
+ // it), we will use a `ThisInterfaceExpr` here to represent the interface type.
+ auto thisInterfaceExpr = astBuilder->create<ThisInterfaceExpr>();
+ thisInterfaceExpr->scope = parser->currentScope;
+ thisTypeConstraint->sup.exp = thisInterfaceExpr;
+ genericDecl->thisTypeConstraintDecl = thisTypeConstraint;
+ AddMember(genericDecl, thisTypeConstraint);
+
+ parser->FillPosition(genericDecl);
+ parser->genericDepth++;
+ auto newInnerDecl = as<Decl>(ParseDecl(parser, genericDecl));
+ genericDecl->inner = newInnerDecl;
+ newInnerDecl->parentDecl = genericDecl;
+ parser->genericDepth--;
+ parser->PopScope();
+ AddMember(interfaceDecl, genericDecl);
+
+ // Mark the requirement decl with `HasInterfaceDefaultImplModifier`.
+ auto hasDefaultImplModifier = astBuilder->create<HasInterfaceDefaultImplModifier>();
+ hasDefaultImplModifier->defaultImplDecl = genericDecl;
+ addModifier(parsedDecl, hasDefaultImplModifier);
+
+ // Update the name of the generic and newly parsed func, to ensure
+ // the mangled name of the default impl func doesn't clash with the
+ // requirement.
+ StringBuilder sb;
+ sb << getText(newInnerDecl->getName()) << "$defaultImpl";
+ genericDecl->nameAndLoc.name = getName(parser, sb.produceString());
+
+ newInnerDecl->nameAndLoc.name = genericDecl->getName();
+ if (auto newInnerGenDecl = as<GenericDecl>(newInnerDecl))
+ {
+ // If the method itself is generic, continue to update the inner function's name.
+ if (newInnerGenDecl->inner)
+ {
+ newInnerGenDecl->inner->nameAndLoc.name = genericDecl->getName();
+ }
+ }
+
+ // Remove the body from the requirement decl.
+ auto requirementFunc = maybeGetInner(parsedDecl);
+ if (auto funcDecl = as<FuncDecl>(requirementFunc))
+ funcDecl->body = nullptr;
+}
+
+static void maybeReparseInterfaceFuncAsExplicitGeneric(
+ Parser* parser,
+ Decl* parsedDecl,
+ ContainerDecl* containerDecl,
+ TokenReader tokenReader)
+{
+ auto funcDecl = as<FuncDecl>(maybeGetInner(parsedDecl));
+ if (!funcDecl || !funcDecl->body)
+ return;
+
+ auto interfaceDecl = as<InterfaceDecl>(containerDecl);
+ if (!interfaceDecl)
+ return;
+
+ if (parser->sink->getErrorCount() != 0)
+ return;
+
+ Parser newParser(*parser);
+ newParser.tokenReader = tokenReader;
+ parseInterfaceDefaultMethodAsExplicitGeneric(&newParser, parsedDecl, interfaceDecl);
+}
+
static void parseDecls(Parser* parser, ContainerDecl* containerDecl, MatchedTokenType matchType)
{
+ TokenReader tokenReader;
Token closingBraceToken;
+ bool parentIsInterface = containerDecl->astNodeType == ASTNodeType::InterfaceDecl;
while (!AdvanceIfMatch(parser, matchType, &closingBraceToken))
{
if (parser->options.allowGLSLInput)
@@ -5133,7 +5228,58 @@ static void parseDecls(Parser* parser, ContainerDecl* containerDecl, MatchedToke
if (parseGLSLGlobalDecl(parser, containerDecl))
continue;
}
- ParseDecl(parser, containerDecl);
+ if (parentIsInterface)
+ tokenReader = parser->tokenReader;
+ auto decl = ParseDecl(parser, containerDecl);
+ if (parentIsInterface)
+ {
+ // If we parsed an interface method with a body (for the default implementation),
+ // we will create a duplicate decl where it is nested inside an explicit generic
+ // decl, such that `ThisType` is a generic type parameter.
+ // For an example, if the user writes:
+ // ```
+ // interface IFoo {
+ // int getVal();
+ // int getGreaterVal() { return getVal() + 1; }
+ // }
+ // ```
+ // We will represent `IFoo` as:
+ // ```
+ // interface IFoo {
+ // int getVal();
+ //
+ // [HasInterfaceDefaultImplModifier(getGreaterVal_defaultImpl)]
+ // int getGreaterVal();
+ //
+ // __interface_default_impl_generic<This:IFoo>
+ // int getGreaterVal_defaultImpl() {
+ // // `this` here will have type `This` (generic param).
+ // return this.getVal() + 1;
+ // }
+ // }
+ // ```
+ // Where `__interface_default_impl_generic` is a sub-class of `__generic`, and
+ // acts exactly like a generic. The sub-class is just to make it easy to
+ // identify a default impl. An interface default impl decl will not be treated
+ // as an interface requirement and lowered as an entry in the witness table,
+ // instead it is just an ordinary member decl that will be lowered into an
+ // `IRGeneric`.
+ //
+ // Ideally we would achieve the same result by parsing the function once, and
+ // then clone the AST nodes to represent `getGreaterVal_defaultImpl`, but we
+ // don't have the infrastructure to do that just yet. So we will play a dirty
+ // trick to achieve the same effect by re-parsing the method body as an
+ // explicit generic decl. Fortunately, this redundant parsing won't actually
+ // cause too much redundant work, because the function bodies are only parsed
+ // as an `UnparsedStmt` at this stage of parsing, which is a relatively simple
+ // process. Once we have the functionality to systematically clone AST nodes,
+ // we can eliminate this reparsing hack.
+ maybeReparseInterfaceFuncAsExplicitGeneric(
+ parser,
+ as<Decl>(decl),
+ containerDecl,
+ tokenReader);
+ }
}
containerDecl->closingSourceLoc = closingBraceToken.loc;
}
diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h
index 1b5fc005d..e7a82592c 100644
--- a/source/slang/slang-syntax.h
+++ b/source/slang/slang-syntax.h
@@ -284,6 +284,14 @@ inline Decl* getInner(DeclRef<GenericDecl> declRef)
return declRef.getDecl()->inner;
}
+inline Decl* maybeGetInner(Decl* decl)
+{
+ if (auto genericDeclRef = as<GenericDecl>(decl))
+ {
+ return genericDeclRef->inner;
+ }
+ return decl;
+}
//
inline Type* getType(ASTBuilder* astBuilder, SubstExpr<Expr> expr)
@@ -393,6 +401,8 @@ AggTypeDecl* getParentAggTypeDecl(Decl* decl);
AggTypeDeclBase* getParentAggTypeDeclBase(Decl* decl);
FunctionDeclBase* getParentFunc(Decl* decl);
+/// Get the parent declref, skipping any generic decls in between.
+DeclRef<Decl> getParentDeclRef(DeclRef<Decl> declRef);
} // namespace Slang
#endif