summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-decl.cpp304
-rw-r--r--source/slang/slang-check-impl.h17
2 files changed, 221 insertions, 100 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 39f7de89a..3a11bd8ad 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -3431,100 +3431,12 @@ namespace Slang
return synGenericDecl;
}
- FuncDecl* SemanticsVisitor::synthesizeMethodSignatureForRequirementWitness(
+ void SemanticsVisitor::addModifiersToSynthesizedDecl(
ConformanceCheckingContext* context,
- DeclRef<FuncDecl> requiredMemberDeclRef,
- List<Expr*>& synArgs,
+ DeclRef<Decl> requiredMemberDeclRef,
+ FunctionDeclBase* synthesized,
ThisExpr*& synThis)
{
- auto synFuncDecl = m_astBuilder->create<FuncDecl>();
- synFuncDecl->ownedScope = m_astBuilder->create<Scope>();
- synFuncDecl->ownedScope->containerDecl = synFuncDecl;
- synFuncDecl->ownedScope->parent = getScope(context->parentDecl);
-
- // For now our synthesized method will use the name and source
- // location of the requirement we are trying to satisfy.
- //
- // TODO: as it stands right now our syntesized method will
- // get a mangled name, which we don't actually want. Leaving
- // out the name here doesn't help matters, because then *all*
- // snthesized methods on a given type would share the same
- // mangled name!
- //
- synFuncDecl->nameAndLoc = requiredMemberDeclRef.getDecl()->nameAndLoc;
- if (synFuncDecl->nameAndLoc.name)
- {
- synFuncDecl->nameAndLoc.name = getSession()->getNameObj("$__syn_" + synFuncDecl->nameAndLoc.name->text);
- }
-
- // The result type of our synthesized method will be the expected
- // result type from the interface requirement.
- //
- // TODO: This logic can/will run into problems if the return type
- // is an associated type.
- //
- // The ideal solution is that we should be solving for interface
- // conformance in two phases: a first phase to solve for how
- // associated types are satisfied, and then a second phase to solve
- // for how other requirements are satisfied (where we can substitute
- // in the associated type witnesses for the abstract associated
- // types as part of `requiredMemberDeclRef`).
- //
- // TODO: We should also double-check that this logic will work
- // with a method that returns `This`.
- //
- auto resultType = getResultType(m_astBuilder, requiredMemberDeclRef);
- synFuncDecl->returnType.type = resultType;
-
- // Our synthesized method will have parameters matching the names
- // and types of those on the requirement, and it will use expressions
- // that reference those parametesr as arguments for the call expresison
- // that makes up the body.
- //
- for (auto paramDeclRef : getParameters(m_astBuilder, requiredMemberDeclRef))
- {
- auto paramType = getType(m_astBuilder, paramDeclRef);
-
- // For each parameter of the requirement, we create a matching
- // parameter (same name and type) for the synthesized method.
- //
- auto synParamDecl = m_astBuilder->create<ParamDecl>();
- synParamDecl->nameAndLoc = paramDeclRef.getDecl()->nameAndLoc;
- synParamDecl->type.type = paramType;
-
- // We need to add the parameter as a child declaration of
- // the method we are building.
- //
- synParamDecl->parentDecl = synFuncDecl;
- synFuncDecl->members.add(synParamDecl);
-
- // For each paramter, we will create an argument expression
- // for the call in the function body.
- //
- auto synArg = m_astBuilder->create<VarExpr>();
- synArg->declRef = makeDeclRef(synParamDecl);
- synArg->type = paramType;
- synArgs.add(synArg);
-
- // Add modifiers
- for (auto modifier : paramDeclRef.getDecl()->modifiers)
- {
- if (as<NoDiffModifier>(modifier))
- {
- auto noDiffModifier = m_astBuilder->create<NoDiffModifier>();
- noDiffModifier->keywordName = getSession()->getNameObj("no_diff");
- addModifier(synParamDecl, noDiffModifier);
- }
- else if (as<InOutModifier>(modifier) || as<OutModifier>(modifier) || as<ConstRefModifier>(modifier) || as<RefModifier>(modifier))
- {
- auto clonedModifier = (Modifier*)m_astBuilder->createByNodeType(modifier->astNodeType);
- clonedModifier->keywordName = modifier->keywordName;
- addModifier(synParamDecl, clonedModifier);
- }
- }
- }
-
-
// Required interface methods can be `static` or non-`static`,
// and non-`static` methods can be `[mutating]` or non-`[mutating]`.
// All of these details affect how we introduce our `this` parameter,
@@ -3533,14 +3445,14 @@ namespace Slang
if (requiredMemberDeclRef.getDecl()->hasModifier<HLSLStaticModifier>())
{
auto synStaticModifier = m_astBuilder->create<HLSLStaticModifier>();
- synFuncDecl->modifiers.first = synStaticModifier;
+ synthesized->modifiers.first = synStaticModifier;
}
else
{
// For a non-`static` requirement, we need a `this` parameter.
//
synThis = m_astBuilder->create<ThisExpr>();
- synThis->scope = synFuncDecl->ownedScope;
+ synThis->scope = synthesized->ownedScope;
// The type of `this` in our method will be the type for
// which we are synthesizing a conformance.
@@ -3556,7 +3468,7 @@ namespace Slang
synThis->type.isLeftValue = true;
auto synMutatingAttr = m_astBuilder->create<MutatingAttribute>();
- addModifier(synFuncDecl, synMutatingAttr);
+ addModifier(synthesized, synMutatingAttr);
}
if (requiredMemberDeclRef.getDecl()->hasModifier<ConstRefAttribute>())
{
@@ -3564,23 +3476,23 @@ namespace Slang
// synthesized method should be too.
//
auto synConstRefAttr = m_astBuilder->create<ConstRefAttribute>();
- addModifier(synFuncDecl, synConstRefAttr);
+ addModifier(synthesized, synConstRefAttr);
}
if (requiredMemberDeclRef.getDecl()->hasModifier<NoDiffThisAttribute>())
{
auto noDiffThisAttr = m_astBuilder->create<NoDiffThisAttribute>();
- addModifier(synFuncDecl, noDiffThisAttr);
+ addModifier(synthesized, noDiffThisAttr);
}
}
if (requiredMemberDeclRef.getDecl()->hasModifier<ForwardDifferentiableAttribute>())
{
auto attr = m_astBuilder->create<ForwardDifferentiableAttribute>();
- addModifier(synFuncDecl, attr);
+ addModifier(synthesized, attr);
}
if (requiredMemberDeclRef.getDecl()->hasModifier<BackwardDifferentiableAttribute>())
{
auto attr = m_astBuilder->create<BackwardDifferentiableAttribute>();
- addModifier(synFuncDecl, attr);
+ addModifier(synthesized, attr);
}
// The visibility of synthesized decl should be the min of the parent decl and the requirement.
if (requiredMemberDeclRef.getDecl()->findModifier<VisibilityModifier>())
@@ -3588,9 +3500,112 @@ namespace Slang
auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, synFuncDecl, visibility);
+ addVisibilityModifier(m_astBuilder, synthesized, visibility);
}
+ }
+ void SemanticsVisitor::addRequiredParamsToSynthesizedDecl(
+ DeclRef<CallableDecl> requirement,
+ CallableDecl* synthesized,
+ List<Expr*>& synArgs)
+ {
+ // Our synthesized method will have parameters matching the names
+ // and types of those on the requirement, and it will use expressions
+ // that reference those parametesr as arguments for the call expresison
+ // that makes up the body.
+ //
+ for (auto paramDeclRef : getParameters(m_astBuilder, requirement))
+ {
+ auto paramType = getType(m_astBuilder, paramDeclRef);
+
+ // For each parameter of the requirement, we create a matching
+ // parameter (same name and type) for the synthesized method.
+ //
+ auto synParamDecl = m_astBuilder->create<ParamDecl>();
+ synParamDecl->nameAndLoc = paramDeclRef.getDecl()->nameAndLoc;
+ synParamDecl->type.type = paramType;
+
+ // We need to add the parameter as a child declaration of
+ // the method we are building.
+ //
+ synParamDecl->parentDecl = synthesized;
+ synthesized->members.add(synParamDecl);
+
+ // For each paramter, we will create an argument expression
+ // for the call in the function body.
+ //
+ auto synArg = m_astBuilder->create<VarExpr>();
+ synArg->declRef = makeDeclRef(synParamDecl);
+ synArg->type = paramType;
+ synArgs.add(synArg);
+
+ // Add modifiers
+ for (auto modifier : paramDeclRef.getDecl()->modifiers)
+ {
+ if (as<NoDiffModifier>(modifier))
+ {
+ auto noDiffModifier = m_astBuilder->create<NoDiffModifier>();
+ noDiffModifier->keywordName = getSession()->getNameObj("no_diff");
+ addModifier(synParamDecl, noDiffModifier);
+ }
+ else if (as<InOutModifier>(modifier) || as<OutModifier>(modifier) || as<ConstRefModifier>(modifier) || as<RefModifier>(modifier))
+ {
+ auto clonedModifier = (Modifier*)m_astBuilder->createByNodeType(modifier->astNodeType);
+ clonedModifier->keywordName = modifier->keywordName;
+ addModifier(synParamDecl, clonedModifier);
+ }
+ }
+ }
+ }
+
+ FuncDecl* SemanticsVisitor::synthesizeMethodSignatureForRequirementWitness(
+ ConformanceCheckingContext* context,
+ DeclRef<FuncDecl> requiredMemberDeclRef,
+ List<Expr*>& synArgs,
+ ThisExpr*& synThis)
+ {
+ auto synFuncDecl = m_astBuilder->create<FuncDecl>();
+ synFuncDecl->ownedScope = m_astBuilder->create<Scope>();
+ synFuncDecl->ownedScope->containerDecl = synFuncDecl;
+ synFuncDecl->ownedScope->parent = getScope(context->parentDecl);
+
+ // For now our synthesized method will use the name and source
+ // location of the requirement we are trying to satisfy.
+ //
+ // TODO: as it stands right now our syntesized method will
+ // get a mangled name, which we don't actually want. Leaving
+ // out the name here doesn't help matters, because then *all*
+ // snthesized methods on a given type would share the same
+ // mangled name!
+ //
+ synFuncDecl->nameAndLoc = requiredMemberDeclRef.getDecl()->nameAndLoc;
+ if (synFuncDecl->nameAndLoc.name)
+ {
+ synFuncDecl->nameAndLoc.name = getSession()->getNameObj("$__syn_" + synFuncDecl->nameAndLoc.name->text);
+ }
+
+ // The result type of our synthesized method will be the expected
+ // result type from the interface requirement.
+ //
+ // TODO: This logic can/will run into problems if the return type
+ // is an associated type.
+ //
+ // The ideal solution is that we should be solving for interface
+ // conformance in two phases: a first phase to solve for how
+ // associated types are satisfied, and then a second phase to solve
+ // for how other requirements are satisfied (where we can substitute
+ // in the associated type witnesses for the abstract associated
+ // types as part of `requiredMemberDeclRef`).
+ //
+ // TODO: We should also double-check that this logic will work
+ // with a method that returns `This`.
+ //
+ auto resultType = getResultType(m_astBuilder, requiredMemberDeclRef);
+ synFuncDecl->returnType.type = resultType;
+
+ addRequiredParamsToSynthesizedDecl(requiredMemberDeclRef, synFuncDecl, synArgs);
+ addModifiersToSynthesizedDecl(context, requiredMemberDeclRef, synFuncDecl, synThis);
+
return synFuncDecl;
}
@@ -3638,7 +3653,7 @@ namespace Slang
// interface ICounter { [mutating] int increment(); }
// struct MyCounter : ICounter
// {
- // [murtating] int increment(int val = 1) { ... }
+ // [mutating] int increment(int val = 1) { ... }
// }
//
// It is clear in this case that the `MyCounter` type *can*
@@ -3857,6 +3872,86 @@ namespace Slang
return true;
}
+ bool SemanticsVisitor::trySynthesizeConstructorRequirementWitness(
+ ConformanceCheckingContext* context,
+ LookupResult const& satisfyingMemberLookupResult,
+ DeclRef<ConstructorDecl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ SLANG_UNUSED(satisfyingMemberLookupResult);
+
+ bool isInWrapperType = isWrapperTypeDecl(context->parentDecl);
+ if (!isInWrapperType)
+ {
+ return false;
+ }
+
+ auto ctorDecl = m_astBuilder->create<ConstructorDecl>();
+ ctorDecl->ownedScope = m_astBuilder->create<Scope>();
+ ctorDecl->ownedScope->containerDecl = ctorDecl;
+ ctorDecl->ownedScope->parent = getScope(context->parentDecl);
+ ctorDecl->loc = context->parentDecl->loc;
+ ctorDecl->closingSourceLoc = ctorDecl->loc;
+ ctorDecl->parentDecl = context->parentDecl;
+ auto ctorName = getName("$init");
+ ctorDecl->nameAndLoc.name = ctorName;
+ ctorDecl->nameAndLoc.loc = ctorDecl->loc;
+
+ List<Expr*> synArgs;
+ addRequiredParamsToSynthesizedDecl(requiredMemberDeclRef, ctorDecl, synArgs);
+
+ ThisExpr* synThis = nullptr;
+ addModifiersToSynthesizedDecl(context, requiredMemberDeclRef, ctorDecl, synThis);
+
+ auto seqStmt = m_astBuilder->create<SeqStmt>();
+ ctorDecl->body = seqStmt;
+ ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl));
+ SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl));
+ bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType);
+
+ for (auto member : context->parentDecl->members)
+ {
+ if (auto varDecl = as<VarDeclBase>(member))
+ {
+ auto varExpr = m_astBuilder->create<VarExpr>();
+ varExpr->scope = ctorDecl->ownedScope;
+ varExpr->name = varDecl->getName();
+ auto checkedVarExpr = CheckTerm(varExpr);
+ if (!checkedVarExpr)
+ return false;
+ if (as<ErrorType>(checkedVarExpr->type.type))
+ return false;
+ auto assign = m_astBuilder->create<AssignExpr>();
+ assign->left = checkedVarExpr;
+ auto temp = m_astBuilder->create<InvokeExpr>();
+ auto lookupResult = lookUpMember(
+ m_astBuilder,
+ this,
+ ctorName,
+ varDecl->type.type,
+ ctorDecl->ownedScope,
+ LookupMask::Function,
+ LookupOptions::IgnoreBaseInterfaces);
+ temp->functionExpr = createLookupResultExpr(ctorName, lookupResult, nullptr, context->parentDecl->loc, nullptr);
+ temp->arguments.addRange(synArgs);
+ auto resolvedVar = ResolveInvoke(temp);
+ if (!resolvedVar)
+ return false;
+ assign->right = resolvedVar;
+ assign->type = m_astBuilder->getVoidType();
+ bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type);
+
+ auto stmt = m_astBuilder->create<ExpressionStmt>();
+ stmt->expression = assign;
+ seqStmt->stmts.add(stmt);
+ break;
+ }
+ }
+
+ _addMethodWitness(witnessTable, requiredMemberDeclRef, makeDeclRef(ctorDecl));
+ return true;
+ }
+
bool SemanticsVisitor::trySynthesizePropertyRequirementWitness(
ConformanceCheckingContext* context,
LookupResult const& lookupResult,
@@ -4529,6 +4624,15 @@ namespace Slang
witnessTable);
}
+ if (auto requiredCtor = requiredMemberDeclRef.as<ConstructorDecl>())
+ {
+ return trySynthesizeConstructorRequirementWitness(
+ context,
+ lookupResult,
+ requiredCtor,
+ witnessTable);
+ }
+
// TODO: There are other kinds of requirements for which synthesis should
// be possible:
//
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 702fe5619..b1618dfb6 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1586,6 +1586,17 @@ namespace Slang
Dictionary<DeclRef<InterfaceDecl>, RefPtr<WitnessTable>> mapInterfaceToWitnessTable;
};
+ void addModifiersToSynthesizedDecl(
+ ConformanceCheckingContext* context,
+ DeclRef<Decl> requirement,
+ FunctionDeclBase* synthesized,
+ ThisExpr* &synThis);
+
+ void addRequiredParamsToSynthesizedDecl(
+ DeclRef<CallableDecl> requirement,
+ CallableDecl* synthesized,
+ List<Expr*>& synArgs);
+
FuncDecl* synthesizeMethodSignatureForRequirementWitness(
ConformanceCheckingContext* context,
DeclRef<FuncDecl> requiredMemberDeclRef,
@@ -1614,6 +1625,12 @@ namespace Slang
DeclRef<FuncDecl> requiredMemberDeclRef,
RefPtr<WitnessTable> witnessTable);
+ bool trySynthesizeConstructorRequirementWitness(
+ ConformanceCheckingContext* context,
+ LookupResult const& lookupResult,
+ DeclRef<ConstructorDecl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable);
+
/// Attempt to synthesize a property that can satisfy `requiredMemberDeclRef` using `lookupResult`.
///
/// On success, installs the syntethesized method in `witnessTable` and returns `true`.