summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-check-expr.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2022-10-26 08:32:24 -0700
committerGitHub <noreply@github.com>2022-10-26 08:32:24 -0700
commit939be44ca23476e622dfb24a592383fe2a1da61f (patch)
tree7f45645897fe5735d58a7687290552d479e4d6fc /source/slang/slang-check-expr.cpp
parent4fc34b18da2f83ee6b4f094067503a66cab3d0b5 (diff)
Auto synthesis of Differential type (#2466)
Diffstat (limited to 'source/slang/slang-check-expr.cpp')
-rw-r--r--source/slang/slang-check-expr.cpp137
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)