diff options
| author | Yong He <yonghe@outlook.com> | 2022-10-26 08:32:24 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-26 08:32:24 -0700 |
| commit | 939be44ca23476e622dfb24a592383fe2a1da61f (patch) | |
| tree | 7f45645897fe5735d58a7687290552d479e4d6fc /source/slang/slang-check-expr.cpp | |
| parent | 4fc34b18da2f83ee6b4f094067503a66cab3d0b5 (diff) | |
Auto synthesis of Differential type (#2466)
Diffstat (limited to 'source/slang/slang-check-expr.cpp')
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 137 |
1 files changed, 123 insertions, 14 deletions
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index 29b44e726..d69cd39ed 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -393,12 +393,107 @@ namespace Slang return derefExpr; } + Expr* SemanticsVisitor::maybeUseSynthesizedDeclForLookupResult(LookupResultItem const& item, Expr* originalExpr) + { + // If the only result from lookup is an entry in an interface decl, it could be that + // the user is leaving out an explicit definition for the requirement and depending on + // the compiler to synthesis the definition. + // In this case, if the lookup is triggered from a location such that the satisfying + // definition should be returned should it existed, we should create a placeholder decl for + // the definition and return a reference to to newly created decl instead of the requirement + // decl in the interface. + switch (item.declRef.getDecl()->astNodeType) + { + case ASTNodeType::AssocTypeDecl: + return maybeUseSynthesizedTypeDeclForLookupResult(item, originalExpr); + default: + return nullptr; + } + } + + Expr* SemanticsVisitor::maybeUseSynthesizedTypeDeclForLookupResult(LookupResultItem const& item, Expr* originalExpr) + { + // We need to check if the lookup should resolve to a definition in an implementation type + // if it existed. + // This will be the case when the lookup is initiated from the concrete implementation type instead of + // directly from the Interface decl. The breadcrumbs of the lookup should provide this information. + + // If no breadcrumbs existed, then the lookup should just resolve to the interface requirement. + + if (!item.breadcrumbs) + return nullptr; + + // We will only ever need to synthesis a type to satisfy an associatedtype requirement. + // In this case the lookup should have resolved to a known associatedtype decl. + auto builtinAssocTypeAttr = item.declRef.getDecl()->findModifier<BuiltinAssociatedTypeRequirementAttribute>(); + if (!builtinAssocTypeAttr) + return nullptr; + + DeclRefType* subType = nullptr; + + // Check if we are reaching the associated type decl through inheritance from a concrete type. + for (auto breadcrumb = item.breadcrumbs; breadcrumb; breadcrumb = breadcrumb->next) + { + switch (breadcrumb->kind) + { + case LookupResultItem::Breadcrumb::Kind::SuperType: + { + auto witness = as<SubtypeWitness>(breadcrumb->val); + if (auto subDeclRefType = as<DeclRefType>(witness->sub)) + { + if (!as<InterfaceDecl>(subDeclRefType->declRef.getDecl())) + { + // Store the inner most concrete super type. + subType = subDeclRefType; + } + } + } + break; + default: + break; + } + } + if (!subType) + return nullptr; + + subType = as<DeclRefType>(subType->getCanonicalType()); + if (!subType) + return nullptr; + + // Don't synthesize for generic parameters. + auto parent = as<AggTypeDecl>(subType->declRef.getDecl()); + if (!parent) + return nullptr; + + // If we reach here, we are expecting a synthesized associated type defined in `subType`. + // Instead of returning a DeclRefExpr to the requirement decl, we synthesize a placeholder type + // in `subType` and return a DeclRefExpr to the synthesized decl. + auto assocType = m_astBuilder->create<StructDecl>(); + assocType->parentDecl = parent; + assocType->nameAndLoc.name = item.declRef.getName(); + assocType->loc = parent->loc; + parent->members.add(assocType); + parent->invalidateMemberDictionary(); + + // Mark the newly synthesized decl as `ToBeSynthesized` so future checking can differentiate it + // from user-provided definitions, and proceed to fill in its definition. + auto toBeSynthesized = m_astBuilder->create<ToBeSynthesizedModifier>(); + addModifier(assocType, toBeSynthesized); + + return ConstructDeclRefExpr(makeDeclRef(assocType), nullptr, originalExpr->loc, originalExpr); + } + Expr* SemanticsVisitor::ConstructLookupResultExpr( LookupResultItem const& item, Expr* baseExpr, SourceLoc loc, Expr* originalExpr) { + // We could be referencing a decl that will be synthesized. If so create a placeholder + // and return a DeclRefExpr to it. + if (auto lookupResultExpr = maybeUseSynthesizedDeclForLookupResult(item, originalExpr)) + return lookupResultExpr; + // If we collected any breadcrumbs, then these represent // additional segments of the lookup path that we need // to expand here. @@ -719,21 +814,25 @@ namespace Slang return _resolveOverloadedExprImpl(overloadedExpr, mask, getSink()); } - Type* SemanticsVisitor::_getDifferential(ASTBuilder* builder, Type* type) + Type* SemanticsVisitor::tryGetDifferentialType(ASTBuilder* builder, Type* type) { if (auto ptrType = as<PtrTypeBase>(type)) { + auto baseDiffType = tryGetDifferentialType(builder, ptrType->getValueType()); + if (!baseDiffType) return nullptr; return builder->getPtrType( - _getDifferential(builder, ptrType->getValueType()), + baseDiffType, ptrType->getClassInfo().m_name); } else if (auto arrayType = as<ArrayExpressionType>(type)) { + auto baseDiffType = tryGetDifferentialType(builder, arrayType->baseType); + if (!baseDiffType) return nullptr; return builder->getArrayType( - _getDifferential(builder, arrayType->baseType), + baseDiffType, arrayType->arrayLength); } - + if (auto declRefType = as<DeclRefType>(type)) { if (auto witness = as<SubtypeWitness>(tryGetInterfaceConformanceWitness(type, builder->getDifferentiableInterface()))) @@ -745,17 +844,16 @@ namespace Slang type, Slang::LookupMask::type, Slang::LookupOptions::None); - + diffTypeLookupResult = resolveOverloadedLookup(diffTypeLookupResult); if (!diffTypeLookupResult.isValid()) { - // Diagnose no 'Differential' member. - getSink()->diagnose(declRefType->declRef, Diagnostics::typeDoesntImplementInterfaceRequirement, type, getName("Differential")); + return nullptr; } else if (diffTypeLookupResult.isOverloaded()) { - getSink()->diagnose(declRefType->declRef, Diagnostics::ambiguousReference, getName("Differential")); + return nullptr; } else { @@ -764,17 +862,28 @@ namespace Slang baseTypeExpr->type.type = m_astBuilder->getTypeType(type); auto diffTypeExpr = ConstructLookupResultExpr( - diffTypeLookupResult.item, - baseTypeExpr, - declRefType->declRef.getLoc(), - baseTypeExpr); - + diffTypeLookupResult.item, + baseTypeExpr, + declRefType->declRef.getLoc(), + baseTypeExpr); + return ExtractTypeFromTypeRepr(diffTypeExpr); } } } - return m_astBuilder->getErrorType(); + return nullptr; + } + + Type* SemanticsVisitor::getDifferentialType(ASTBuilder* builder, Type* type, SourceLoc loc) + { + auto result = tryGetDifferentialType(builder, type); + if (!result) + { + getSink()->diagnose(loc, Diagnostics::typeDoesntImplementInterfaceRequirement, type, getName("Differential")); + return m_astBuilder->getErrorType(); + } + return result; } void SemanticsVisitor::maybeRegisterDifferentiableType(ASTBuilder* builder, Type* type) |
