diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 304 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 17 |
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`. |
