summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/unix/slang-unix-process-util.cpp15
-rw-r--r--source/slang/core.meta.slang42
-rw-r--r--source/slang/core.meta.slang.h50
-rw-r--r--source/slang/slang-check-conversion.cpp2
-rw-r--r--source/slang/slang-check-decl.cpp117
-rw-r--r--source/slang/slang-check-expr.cpp110
-rw-r--r--source/slang/slang-check-impl.h52
-rw-r--r--source/slang/slang-check-overload.cpp248
-rw-r--r--source/slang/slang-check-type.cpp10
-rw-r--r--source/slang/slang-diagnostic-defs.h2
-rw-r--r--source/slang/slang-lookup.cpp8
-rw-r--r--source/slang/slang-lookup.h4
-rw-r--r--source/slang/slang-lower-to-ir.cpp21
-rw-r--r--source/slang/slang-syntax.cpp64
-rw-r--r--source/slang/slang-type-defs.h14
15 files changed, 574 insertions, 185 deletions
diff --git a/source/core/unix/slang-unix-process-util.cpp b/source/core/unix/slang-unix-process-util.cpp
index ab4366b06..ec5d4865a 100644
--- a/source/core/unix/slang-unix-process-util.cpp
+++ b/source/core/unix/slang-unix-process-util.cpp
@@ -106,14 +106,23 @@ namespace Slang {
int stderrPipe[2];
if (pipe(stdoutPipe) == -1)
+ {
+ fprintf(stderr, "error: `pipe` failed\n");
return SLANG_FAIL;
+ }
if (pipe(stderrPipe) == -1)
+ {
+ fprintf(stderr, "error: `pipe` failed\n");
return SLANG_FAIL;
+ }
pid_t childProcessID = fork();
if (childProcessID == -1)
+ {
+ fprintf(stderr, "error: `fork` failed\n");
return SLANG_FAIL;
+ }
if (childProcessID == 0)
{
@@ -166,9 +175,9 @@ namespace Slang {
return SLANG_FAIL;
}
- // Set a timeout of ten seconds;
+ // Set a timeout of twenty seconds;
// we really shouldn't wait too long...
- int pollTimeout = 10000;
+ int pollTimeout = 20000;
int pollResult = poll(pollInfos, pollInfoCount, pollTimeout);
if (pollResult <= 0)
{
@@ -178,6 +187,7 @@ namespace Slang {
continue;
// timeout or error...
+ fprintf(stderr, "error: `poll` failed or timed out\n");
return SLANG_FAIL;
}
@@ -228,6 +238,7 @@ namespace Slang {
pid_t terminatedProcessID = waitpid(childProcessID, &childStatus, 0);
if (terminatedProcessID == -1)
{
+ fprintf(stderr, "error: `waitpid` failed\n");
return SLANG_FAIL;
}
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index f0ec44080..80ff09ea8 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -52,6 +52,48 @@ interface __EnumType
associatedtype __Tag : __BuiltinIntegerType;
};
+// Use an extension to declare that every `enum` type
+// inherits an initializer based on the tag type.
+//
+// Note: there is an important and subtle point here.
+// If we declared these initializers inside the `interface`
+// declaration above, then they would implicitly be
+// *requirements* of the `__EnumType` interface, and any
+// type that declares conformance to it would need to
+// provide implementations. That would put the onus on
+// the semantic checker to synthesize such initializers
+// when conforming an `enum` type to `__EnumType` (just
+// as it currently synthesizes the `__Tag` requirement.
+// Putting the declaration in an `extension` makes them
+// concrete declerations rather than interface requirements.
+// (Admittedly, they are "concrete" declarations with
+// no bodies, because currently all initializers are
+// assumed to be intrinsics).
+//
+// TODO: It might be more accurate to express this as:
+//
+// __generic<T:__EnumType> extension T { ... }
+//
+// That alternative would express an extension of every
+// type that conforms to `__EnumType`, rather than an
+// extension of `__EnumType` itself. The distinction
+// is subtle, and unfortunately not one the Slang type
+// checker is equiped to handle right now. For now we
+// will stick with the syntax that actually works, even
+// if it might be the less technically correct one.
+//
+//
+extension __EnumType
+{
+ // TODO: this should be a single initializer using
+ // the `__Tag` associated type from the `__EnumType`
+ // interface, but right now the scoping for looking
+ // up that type isn't working right.
+ //
+ __init(int value);
+ __init(uint value);
+}
+
// A type resulting from an `enum` declaration
// with the `[flags]` attribute.
interface __FlagsEnumType : __EnumType
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 6f21f991d..27811b588 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -52,6 +52,48 @@ SLANG_RAW(" // conflict if a user had an `enum` case called `Tag`\n")
SLANG_RAW(" associatedtype __Tag : __BuiltinIntegerType;\n")
SLANG_RAW("};\n")
SLANG_RAW("\n")
+SLANG_RAW("// Use an extension to declare that every `enum` type\n")
+SLANG_RAW("// inherits an initializer based on the tag type.\n")
+SLANG_RAW("//\n")
+SLANG_RAW("// Note: there is an important and subtle point here.\n")
+SLANG_RAW("// If we declared these initializers inside the `interface`\n")
+SLANG_RAW("// declaration above, then they would implicitly be\n")
+SLANG_RAW("// *requirements* of the `__EnumType` interface, and any\n")
+SLANG_RAW("// type that declares conformance to it would need to\n")
+SLANG_RAW("// provide implementations. That would put the onus on\n")
+SLANG_RAW("// the semantic checker to synthesize such initializers\n")
+SLANG_RAW("// when conforming an `enum` type to `__EnumType` (just\n")
+SLANG_RAW("// as it currently synthesizes the `__Tag` requirement.\n")
+SLANG_RAW("// Putting the declaration in an `extension` makes them\n")
+SLANG_RAW("// concrete declerations rather than interface requirements.\n")
+SLANG_RAW("// (Admittedly, they are \"concrete\" declarations with\n")
+SLANG_RAW("// no bodies, because currently all initializers are\n")
+SLANG_RAW("// assumed to be intrinsics).\n")
+SLANG_RAW("//\n")
+SLANG_RAW("// TODO: It might be more accurate to express this as:\n")
+SLANG_RAW("//\n")
+SLANG_RAW("// __generic<T:__EnumType> extension T { ... }\n")
+SLANG_RAW("//\n")
+SLANG_RAW("// That alternative would express an extension of every\n")
+SLANG_RAW("// type that conforms to `__EnumType`, rather than an\n")
+SLANG_RAW("// extension of `__EnumType` itself. The distinction\n")
+SLANG_RAW("// is subtle, and unfortunately not one the Slang type\n")
+SLANG_RAW("// checker is equiped to handle right now. For now we\n")
+SLANG_RAW("// will stick with the syntax that actually works, even\n")
+SLANG_RAW("// if it might be the less technically correct one.\n")
+SLANG_RAW("//\n")
+SLANG_RAW("//\n")
+SLANG_RAW("extension __EnumType\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" // TODO: this should be a single initializer using\n")
+SLANG_RAW(" // the `__Tag` associated type from the `__EnumType`\n")
+SLANG_RAW(" // interface, but right now the scoping for looking\n")
+SLANG_RAW(" // up that type isn't working right.\n")
+SLANG_RAW(" //\n")
+SLANG_RAW(" __init(int value);\n")
+SLANG_RAW(" __init(uint value);\n")
+SLANG_RAW("}\n")
+SLANG_RAW("\n")
SLANG_RAW("// A type resulting from an `enum` declaration\n")
SLANG_RAW("// with the `[flags]` attribute.\n")
SLANG_RAW("interface __FlagsEnumType : __EnumType\n")
@@ -153,7 +195,7 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt)
// TODO: should this cover the full gamut of integer types?
case BaseType::Int:
case BaseType::UInt:
-SLANG_RAW("#line 153 \"core.meta.slang\"")
+SLANG_RAW("#line 195 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW(" __generic<T:__EnumType>\n")
SLANG_RAW(" __init(T value);\n")
@@ -169,7 +211,7 @@ SLANG_RAW(" __init(T value);\n")
// Declare built-in pointer type
// (eventually we can have the traditional syntax sugar for this)
-SLANG_RAW("#line 168 \"core.meta.slang\"")
+SLANG_RAW("#line 210 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T>\n")
@@ -231,7 +273,7 @@ sb << " __init(T value);\n";
sb << " __init(vector<T,N> value);\n";
sb << "};\n";
-SLANG_RAW("#line 214 \"core.meta.slang\"")
+SLANG_RAW("#line 256 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T = float, let R : int = 4, let C : int = 4>\n")
@@ -1216,7 +1258,7 @@ for (auto op : binaryOps)
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
}
}
-SLANG_RAW("#line 1198 \"core.meta.slang\"")
+SLANG_RAW("#line 1240 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("// Specialized function\n")
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index f313c7913..85c508d41 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -607,7 +607,7 @@ namespace Slang
overloadContext.baseExpr = nullptr;
overloadContext.mode = OverloadResolveContext::Mode::JustTrying;
- AddTypeOverloadCandidates(toType, overloadContext, toType);
+ AddTypeOverloadCandidates(toType, overloadContext);
// After all of the overload candidates have been added
// to the context and processed, we need to see whether
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 3e0a1c618..027ddc70f 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -161,6 +161,14 @@ namespace Slang
if(as<SimpleTypeDecl>(decl))
return true;
+ // Initializer/constructor declarations are effectively `static`
+ // in Slang. They behave like functions that return an instance
+ // of the enclosing type, rather than as functions that are
+ // called on a pre-existing value.
+ //
+ if(as<ConstructorDecl>(decl))
+ return true;
+
// Things nested inside functions may have dependencies
// on values from the enclosing scope, but this needs to
// be dealt with via "capture" so they are also effectively
@@ -1197,6 +1205,18 @@ namespace Slang
DeclRef<Decl> requiredMemberDeclRef,
RefPtr<WitnessTable> witnessTable)
{
+ // Sanity check: if are checking whether a type `T`
+ // implements, say, `IFoo::bar` and lookup of `bar`
+ // in type `T` yielded `IFoo::bar`, then that shouldn't
+ // be treated as a valid satisfaction of the requirement.
+ //
+ // TODO: Ideally this check should be comparing the `DeclRef`s
+ // and not just the `Decl`s, but we currently don't get exactly
+ // the same substitutions when we see the inherited `IFoo::bar`.
+ //
+ if(memberDeclRef.getDecl() == requiredMemberDeclRef.getDecl())
+ return false;
+
// At a high level, we want to check that the
// `memberDecl` and the `requiredMemberDeclRef`
// have the same AST node class, and then also
@@ -2524,6 +2544,78 @@ namespace Slang
getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here");
}
+ RefPtr<Type> SemanticsVisitor::calcThisType(DeclRef<Decl> declRef)
+ {
+ if( auto interfaceDeclRef = declRef.as<InterfaceDecl>() )
+ {
+ // In the body of an `interface`, a `This` type
+ // refers to the concrete type that will eventually
+ // conform to the interface and fill in its
+ // requirements.
+ //
+ RefPtr<ThisType> thisType = new ThisType();
+ thisType->setSession(getSession());
+ thisType->interfaceDeclRef = interfaceDeclRef;
+ return thisType;
+ }
+ else if (auto aggTypeDeclRef = declRef.as<AggTypeDecl>())
+ {
+ // In the body of an ordinary aggregate type,
+ // such as a `struct`, the `This` type just
+ // refers to the type itself.
+ //
+ // TODO: If/when we support `class` types
+ // with inheritance, then `This` inside a class
+ // would need to refer to the eventual concrete
+ // type, much like the `interface` case above.
+ //
+ return DeclRefType::Create(
+ getSession(),
+ aggTypeDeclRef);
+ }
+ else if (auto extDeclRef = declRef.as<ExtensionDecl>())
+ {
+ // In the body of an `extension`, the `This`
+ // type refers to the type being extended.
+ //
+ // Note: we currently have this loop back
+ // around through `calcThisType` for the
+ // type being extended, rather than just
+ // using it directly. This makes a difference
+ // for polymorphic types like `interface`s,
+ // and there are reasonable arguments for
+ // the validity of either option.
+ //
+ // Does `extension IFoo` mean extending
+ // exactly the type `IFoo` (an existential,
+ // which could at runtime be a value of
+ // any type conforming to `IFoo`), or does
+ // it implicitly extend every type that
+ // conforms to `IFoo`? The difference is
+ // significant, and we need to make a choice
+ // sooner or later.
+ //
+ auto targetType = GetTargetType(extDeclRef);
+ return calcThisType(targetType);
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ RefPtr<Type> SemanticsVisitor::calcThisType(Type* type)
+ {
+ if( auto declRefType = as<DeclRefType>(type) )
+ {
+ return calcThisType(declRefType->declRef);
+ }
+ else
+ {
+ return type;
+ }
+ }
+
RefPtr<Type> SemanticsVisitor::findResultTypeForConstructorDecl(ConstructorDecl* decl)
{
// We want to look at the parent of the declaration,
@@ -2538,27 +2630,16 @@ namespace Slang
parent = genericParent->ParentDecl;
}
- // Now look at the type of the parent (or grandparent).
- if (auto aggTypeDecl = as<AggTypeDecl>(parent))
- {
- // We are nested in an aggregate type declaration,
- // so the result type of the initializer will just
- // be the surrounding type.
- return DeclRefType::Create(
- getSession(),
- makeDeclRef(aggTypeDecl));
- }
- else if (auto extDecl = as<ExtensionDecl>(parent))
- {
- // We are nested inside an extension, so the result
- // type needs to be the type being extended.
- return extDecl->targetType.type;
- }
- else
+ // The result type for a constructor is whatever `This` would
+ // refer to in the body of the outer declaration.
+ //
+ auto thisType = calcThisType(makeDeclRef(parent));
+ if( !thisType )
{
getSink()->diagnose(decl, Diagnostics::initializerNotInsideType);
- return nullptr;
+ thisType = getSession()->getErrorType();
}
+ return thisType;
}
void SemanticsDeclHeaderVisitor::visitConstructorDecl(ConstructorDecl* decl)
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 9aa081e1b..f1c1b9a43 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -379,14 +379,77 @@ namespace Slang
}
}
- RefPtr<Expr> SemanticsVisitor::ResolveOverloadedExpr(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask)
+ LookupResult SemanticsVisitor::resolveOverloadedLookup(LookupResult const& inResult)
+ {
+ // If the result isn't actually overloaded, it is fine as-is
+ if (!inResult.isValid()) return inResult;
+ if (!inResult.isOverloaded()) return inResult;
+
+ // We are going to build up a list of items to return.
+ List<LookupResultItem> items;
+ for( auto item : inResult.items )
+ {
+ // For each item we consider adding, we will compare it
+ // to those items we've already added.
+ //
+ // If any of the existing items is "better" than `item`,
+ // then we will skip adding `item`.
+ //
+ // If `item` is "better" than any of the existing items,
+ // we will remove those from `items`.
+ //
+ bool shouldAdd = true;
+ for( Index ii = 0; ii < items.getCount(); ++ii )
+ {
+ int cmp = CompareLookupResultItems(item, items[ii]);
+ if( cmp < 0 )
+ {
+ // The new `item` is strictly better
+ items.fastRemoveAt(ii);
+ --ii;
+ }
+ else if( cmp > 0 )
+ {
+ // The existing item is strictly better
+ shouldAdd = false;
+ }
+ }
+ if( shouldAdd )
+ {
+ items.add(item);
+ }
+ }
+
+ // The resulting `items` list should be all those items
+ // that were neither better nor worse than one another.
+ //
+ // There should always be at least one such item.
+ //
+ SLANG_ASSERT(items.getCount() != 0);
+
+ LookupResult result;
+ for( auto item : items )
+ {
+ AddToLookupResult(result, item);
+ }
+ return result;
+ }
+
+ RefPtr<Expr> SemanticsVisitor::_resolveOverloadedExprImpl(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask, DiagnosticSink* diagSink)
{
auto lookupResult = overloadedExpr->lookupResult2;
SLANG_RELEASE_ASSERT(lookupResult.isValid() && lookupResult.isOverloaded());
// Take the lookup result we had, and refine it based on what is expected in context.
+ //
+ // E.g., if there is both a type and a variable named `Foo`, but in context we know
+ // that a type is expected, then we can disambiguate by assuming the type is intended.
+ //
lookupResult = refineLookup(lookupResult, mask);
+ // Try to filter out overload candidates based on which ones are "better" than one another.
+ lookupResult = resolveOverloadedLookup(lookupResult);
+
if (!lookupResult.isValid())
{
// If we didn't find any symbols after filtering, then just
@@ -394,9 +457,23 @@ namespace Slang
return overloadedExpr;
}
- if (lookupResult.isOverloaded())
+ if(!lookupResult.isOverloaded())
+ {
+ // If there is only a single item left in the lookup result,
+ // then we can proceed to use that item alone as the resolved
+ // expression.
+ //
+ return ConstructLookupResultExpr(lookupResult.item, overloadedExpr->base, overloadedExpr->loc);
+ }
+
+ // Otherwise, we weren't able to resolve the overloading given
+ // the information available in context.
+ //
+ // If the client is asking for us to emit diagnostics about
+ // this fact, we should do so here:
+ //
+ if( diagSink )
{
- // We had an ambiguity anyway, so report it.
getSink()->diagnose(overloadedExpr, Diagnostics::ambiguousReference, lookupResult.items[0].declRef.GetName());
for(auto item : lookupResult.items)
@@ -408,9 +485,32 @@ namespace Slang
// TODO(tfoley): should we construct a new ErrorExpr here?
return CreateErrorExpr(overloadedExpr);
}
+ else
+ {
+ // If the client isn't trying to *force* overload resolution
+ // to complete just yet (e.g., they are just trying out one
+ // candidate for an overloaded call site), then we return
+ // the input expression as-is.
+ //
+ return overloadedExpr;
+ }
+ }
+
+ RefPtr<Expr> SemanticsVisitor::maybeResolveOverloadedExpr(RefPtr<Expr> expr, LookupMask mask, DiagnosticSink* diagSink)
+ {
+ if( auto overloadedExpr = as<OverloadedExpr>(expr) )
+ {
+ return _resolveOverloadedExprImpl(overloadedExpr, mask, diagSink);
+ }
+ else
+ {
+ return expr;
+ }
+ }
- // otherwise, we had a single decl and it was valid, hooray!
- return ConstructLookupResultExpr(lookupResult.item, overloadedExpr->base, overloadedExpr->loc);
+ RefPtr<Expr> SemanticsVisitor::resolveOverloadedExpr(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask)
+ {
+ return _resolveOverloadedExprImpl(overloadedExpr, mask, getSink());
}
RefPtr<Expr> SemanticsVisitor::CheckTerm(RefPtr<Expr> term)
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index f026f0ed1..8cb691f4a 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -360,7 +360,27 @@ namespace Slang
RefPtr<Expr> baseExpr,
SourceLoc loc);
- RefPtr<Expr> ResolveOverloadedExpr(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask);
+ /// Attempt to "resolve" an overloaded `LookupResult` to only include the "best" results
+ LookupResult resolveOverloadedLookup(LookupResult const& lookupResult);
+
+ /// Attempt to resolve `expr` into an expression that refers to a single declaration/value.
+ /// If `expr` isn't overloaded, then it will be returned as-is.
+ ///
+ /// The provided `mask` is used to filter items down to those that are applicable in a given context (e.g., just types).
+ ///
+ /// If the expression cannot be resolved to a single value then *if* `diagSink` is non-null an
+ /// appropriate "ambiguous reference" error will be reported, and an error expression will be returned.
+ /// Otherwise, the original expression is returned if resolution fails.
+ ///
+ RefPtr<Expr> maybeResolveOverloadedExpr(RefPtr<Expr> expr, LookupMask mask, DiagnosticSink* diagSink);
+
+ /// Attempt to resolve `overloadedExpr` into an expression that refers to a single declaration/value.
+ ///
+ /// Equivalent to `maybeResolveOverloadedExpr` with `diagSink` bound to the sink for the `SemanticsVisitor`.
+ RefPtr<Expr> resolveOverloadedExpr(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask);
+
+ /// Worker reoutine for `maybeResolveOverloadedExpr` and `resolveOverloadedExpr`.
+ RefPtr<Expr> _resolveOverloadedExprImpl(RefPtr<OverloadedExpr> overloadedExpr, LookupMask mask, DiagnosticSink* diagSink);
RefPtr<Expr> ExpectATypeRepr(RefPtr<Expr> expr);
@@ -842,6 +862,12 @@ namespace Slang
// declaration that its declaration is nested inside.
RefPtr<Type> findResultTypeForConstructorDecl(ConstructorDecl* decl);
+ /// Determine what type `This` should refer to in the context of the given parent `decl`.
+ RefPtr<Type> calcThisType(DeclRef<Decl> decl);
+
+ /// Determine what type `This` should refer to in an extension of `type`.
+ RefPtr<Type> calcThisType(Type* type);
+
//
@@ -1090,6 +1116,11 @@ namespace Slang
OverloadCandidate* left,
OverloadCandidate* right);
+ /// Compare items `left` and `right` produced by lookup, to see if one should be favored for overloading.
+ int CompareLookupResultItems(
+ LookupResultItem const& left,
+ LookupResultItem const& right);
+
void AddOverloadCandidateInner(
OverloadResolveContext& context,
OverloadCandidate& candidate);
@@ -1174,28 +1205,19 @@ namespace Slang
DeclRef<GenericDecl> genericDeclRef,
OverloadResolveContext& context);
- void AddAggTypeOverloadCandidates(
- LookupResultItem typeItem,
- RefPtr<Type> type,
- DeclRef<AggTypeDecl> aggTypeDeclRef,
- OverloadResolveContext& context,
- RefPtr<Type> resultType);
-
- void addGenericTypeParamOverloadCandidates(
- DeclRef<GenericTypeParamDecl> typeDeclRef,
- OverloadResolveContext& context,
- RefPtr<Type> resultType);
-
void AddTypeOverloadCandidates(
RefPtr<Type> type,
- OverloadResolveContext& context,
- RefPtr<Type> resultType);
+ OverloadResolveContext& context);
void AddDeclRefOverloadCandidates(
LookupResultItem item,
OverloadResolveContext& context);
void AddOverloadCandidates(
+ LookupResult const& result,
+ OverloadResolveContext& context);
+
+ void AddOverloadCandidates(
RefPtr<Expr> funcExpr,
OverloadResolveContext& context);
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index a7ae187a7..dc013b781 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -1,6 +1,8 @@
// slang-check-overload.cpp
#include "slang-check-impl.h"
+#include "slang-lookup.h"
+
// This file implements semantic checking logic related
// to resolving overloading call operations, by checking
// the applicability and relative priority of various candidates.
@@ -482,6 +484,59 @@ namespace Slang
}
}
+ /// Does the given `declRef` represent an interface requirement?
+ bool isInterfaceRequirement(DeclRef<Decl> const& declRef)
+ {
+ if(!declRef)
+ return false;
+
+ auto parent = declRef.GetParent();
+ if(parent.as<GenericDecl>())
+ parent = parent.GetParent();
+
+ if(parent.as<InterfaceDecl>())
+ return true;
+
+ return false;
+ }
+
+ int SemanticsVisitor::CompareLookupResultItems(
+ LookupResultItem const& left,
+ LookupResultItem const& right)
+ {
+ // It is possible for lookup to return both an interface requirement
+ // and the concrete function that satisfies that requirement.
+ // We always want to favor a concrete method over an interface
+ // requirement it might override.
+ //
+ // TODO: This should turn into a more detailed check such that
+ // a candidate for declaration A is always better than a candidate
+ // for declaration B if A is an override of B. We can't
+ // easily make that check right now because we aren't tracking
+ // this kind of "is an override of ..." information on declarations
+ // directly (it is only visible through the requirement witness
+ // information for inheritance declarations).
+ //
+ bool leftIsInterfaceRequirement = isInterfaceRequirement(left.declRef);
+ bool rightIsInterfaceRequirement = isInterfaceRequirement(right.declRef);
+ if(leftIsInterfaceRequirement != rightIsInterfaceRequirement)
+ return int(leftIsInterfaceRequirement) - int(rightIsInterfaceRequirement);
+
+ // TODO: We should always have rules such that in a tie a declaration
+ // A::m is better than B::m when all other factors are equal and
+ // A inherits from B.
+
+ // TODO: There are other cases like this we need to add in terms
+ // of ranking/prioritizing overloads, around things like
+ // "transparent" members, or when lookup proceeds from an "inner"
+ // to an "outer" scope. In many cases the right way to proceed
+ // could involve attaching a distance/cost/rank to things directly
+ // as part of lookup, and in other cases it might be best handled
+ // as a semantic check based on the actual declarations found.
+
+ return 0;
+ }
+
int SemanticsVisitor::CompareOverloadCandidates(
OverloadCandidate* left,
OverloadCandidate* right)
@@ -496,6 +551,15 @@ namespace Slang
{
if (left->conversionCostSum != right->conversionCostSum)
return left->conversionCostSum - right->conversionCostSum;
+
+ // If all conversion costs match, then we should consider
+ // whether one of the two items/declarations should be
+ // preferred based on grounds that have nothing to do
+ // with applicability or conversion costs.
+ //
+ auto itemDiff = CompareLookupResultItems(left->item, right->item);
+ if(itemDiff)
+ return itemDiff;
}
return 0;
@@ -758,135 +822,39 @@ namespace Slang
return DeclRef<Decl>(innerDecl, constraintSubst);
}
- void SemanticsVisitor::AddAggTypeOverloadCandidates(
- LookupResultItem typeItem,
+ void SemanticsVisitor::AddTypeOverloadCandidates(
RefPtr<Type> type,
- DeclRef<AggTypeDecl> aggTypeDeclRef,
- OverloadResolveContext& context,
- RefPtr<Type> resultType)
+ OverloadResolveContext& context)
{
- for (auto ctorDeclRef : getMembersOfType<ConstructorDecl>(aggTypeDeclRef))
- {
- // now work through this candidate...
- AddCtorOverloadCandidate(typeItem, type, ctorDeclRef, context, resultType);
- }
-
- // Also check for generic constructors.
+ // The code being checked is trying to apply `type` like a function.
+ // Semantically, the operations `T(args...)` is equivalent to
+ // `T.__init(args...)` if we had a surface syntax that supported
+ // looking up `__init` declarations by that name.
//
- // TODO: There is way too much duplication between this case and the extension
- // handling below, and all of this is *also* duplicative with the ordinary
- // overload resolution logic for function.
+ // Internally, all `__init` declarations are stored with the name
+ // `$init`, to avoid potential conflicts if a user decided to name
+ // a field/method `__init`.
//
- // The right solution is to handle a "constructor" call expression by
- // first doing member lookup in the type (for initializer members, which
- // should all share a common name), and then to do overload resolution using
- // the (possibly overloaded) result of that lookup.
+ // We will look up all the initializers on `type` by looking up
+ // its members named `$init`, and then proceed to perform overload
+ // resolution with what we find.
//
- for (auto genericDeclRef : getMembersOfType<GenericDecl>(aggTypeDeclRef))
- {
- if (auto ctorDecl = as<ConstructorDecl>(genericDeclRef.getDecl()->inner))
- {
- DeclRef<Decl> innerRef = SpecializeGenericForOverload(genericDeclRef, context);
- if (!innerRef)
- continue;
-
- DeclRef<ConstructorDecl> innerCtorRef = innerRef.as<ConstructorDecl>();
- AddCtorOverloadCandidate(typeItem, type, innerCtorRef, context, resultType);
- }
- }
-
- // Now walk through any extensions we can find for this types
- for (auto ext = GetCandidateExtensions(aggTypeDeclRef); ext; ext = ext->nextCandidateExtension)
- {
- auto extDeclRef = ApplyExtensionToType(ext, type);
- if (!extDeclRef)
- continue;
-
- for (auto ctorDeclRef : getMembersOfType<ConstructorDecl>(extDeclRef))
- {
- // TODO(tfoley): `typeItem` here should really reference the extension...
-
- // now work through this candidate...
- AddCtorOverloadCandidate(typeItem, type, ctorDeclRef, context, resultType);
- }
-
- // Also check for generic constructors
- for (auto genericDeclRef : getMembersOfType<GenericDecl>(extDeclRef))
- {
- if (auto ctorDecl = genericDeclRef.getDecl()->inner.as<ConstructorDecl>())
- {
- DeclRef<Decl> innerRef = SpecializeGenericForOverload(genericDeclRef, context);
- if (!innerRef)
- continue;
-
- DeclRef<ConstructorDecl> innerCtorRef = innerRef.as<ConstructorDecl>();
-
- AddCtorOverloadCandidate(typeItem, type, innerCtorRef, context, resultType);
-
- // TODO(tfoley): need a way to do the solving step for the constraint system
- }
- }
- }
- }
-
- void SemanticsVisitor::addGenericTypeParamOverloadCandidates(
- DeclRef<GenericTypeParamDecl> typeDeclRef,
- OverloadResolveContext& context,
- RefPtr<Type> resultType)
- {
- // We need to look for any constraints placed on the generic
- // type parameter, since they will give us information on
- // interfaces that the type must conform to.
-
- // We expect the parent of the generic type parameter to be a generic...
- auto genericDeclRef = typeDeclRef.GetParent().as<GenericDecl>();
- SLANG_ASSERT(genericDeclRef);
-
- for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
- {
- // Does this constraint pertain to the type we are working on?
- //
- // We want constraints of the form `T : Foo` where `T` is the
- // generic parameter in question, and `Foo` is whatever we are
- // constraining it to.
- auto subType = GetSub(constraintDeclRef);
- auto subDeclRefType = as<DeclRefType>(subType);
- if(!subDeclRefType)
- continue;
- if(!subDeclRefType->declRef.Equals(typeDeclRef))
- continue;
-
- // The super-type in the constraint (e.g., `Foo` in `T : Foo`)
- // will tell us a type we should use for lookup.
- auto bound = GetSup(constraintDeclRef);
-
- // Go ahead and use the target type:
- //
- // TODO: Need to consider case where this might recurse infinitely.
- AddTypeOverloadCandidates(bound, context, resultType);
- }
- }
-
- void SemanticsVisitor::AddTypeOverloadCandidates(
- RefPtr<Type> type,
- OverloadResolveContext& context,
- RefPtr<Type> resultType)
- {
- if (auto declRefType = as<DeclRefType>(type))
- {
- auto declRef = declRefType->declRef;
- if (auto aggTypeDeclRef = declRef.as<AggTypeDecl>())
- {
- AddAggTypeOverloadCandidates(LookupResultItem(aggTypeDeclRef), type, aggTypeDeclRef, context, resultType);
- }
- else if(auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
- {
- addGenericTypeParamOverloadCandidates(
- genericTypeParamDeclRef,
- context,
- resultType);
- }
- }
+ // TODO: One wrinkle here is single-argument constructor syntax.
+ // An operation like `(T) oneArg` or `T(oneArg)` is currently
+ // treated as a call expression, but we might want such cases
+ // to go through the type coercion logic first/instead, because
+ // by doing so we could weed out cases where a type is "constructed"
+ // from a value of the same type. There is no need in Slang for
+ // "copy constructors" but the stdlib currently has to define
+ // some just to make code that does, e.g., `float(1.0f)` work.
+
+ LookupResult initializers = lookUpMember(
+ getSession(),
+ this,
+ getName("$init"),
+ type);
+
+ AddOverloadCandidates(initializers, context);
}
void SemanticsVisitor::AddDeclRefOverloadCandidates(
@@ -904,7 +872,7 @@ namespace Slang
auto type = DeclRefType::Create(
getSession(),
aggTypeDeclRef);
- AddAggTypeOverloadCandidates(item, type, aggTypeDeclRef, context, type);
+ AddTypeOverloadCandidates(type, context);
}
else if (auto genericDeclRef = item.declRef.as<GenericDecl>())
{
@@ -938,14 +906,14 @@ namespace Slang
else if( auto typeDefDeclRef = item.declRef.as<TypeDefDecl>() )
{
auto type = getNamedType(getSession(), typeDefDeclRef);
- AddTypeOverloadCandidates(GetType(typeDefDeclRef), context, type);
+ AddTypeOverloadCandidates(type, context);
}
else if( auto genericTypeParamDeclRef = item.declRef.as<GenericTypeParamDecl>() )
{
auto type = DeclRefType::Create(
getSession(),
genericTypeParamDeclRef);
- addGenericTypeParamOverloadCandidates(genericTypeParamDeclRef, context, type);
+ AddTypeOverloadCandidates(type, context);
}
else
{
@@ -954,6 +922,23 @@ namespace Slang
}
void SemanticsVisitor::AddOverloadCandidates(
+ LookupResult const& result,
+ OverloadResolveContext& context)
+ {
+ if(result.isOverloaded())
+ {
+ for(auto item : result.items)
+ {
+ AddDeclRefOverloadCandidates(item, context);
+ }
+ }
+ else
+ {
+ AddDeclRefOverloadCandidates(result.item, context);
+ }
+ }
+
+ void SemanticsVisitor::AddOverloadCandidates(
RefPtr<Expr> funcExpr,
OverloadResolveContext& context)
{
@@ -973,12 +958,7 @@ namespace Slang
}
else if (auto overloadedExpr = as<OverloadedExpr>(funcExpr))
{
- auto lookupResult = overloadedExpr->lookupResult2;
- SLANG_RELEASE_ASSERT(lookupResult.isOverloaded());
- for(auto item : lookupResult.items)
- {
- AddDeclRefOverloadCandidates(item, context);
- }
+ AddOverloadCandidates(overloadedExpr->lookupResult2, context);
}
else if (auto overloadedExpr2 = as<OverloadedExpr2>(funcExpr))
{
@@ -996,7 +976,7 @@ namespace Slang
// TODO(tfoley): are there any meaningful types left
// that aren't declaration references?
auto type = typeType->type;
- AddTypeOverloadCandidates(type, context, type);
+ AddTypeOverloadCandidates(type, context);
return;
}
}
diff --git a/source/slang/slang-check-type.cpp b/source/slang/slang-check-type.cpp
index ea808e456..b92f9a8f7 100644
--- a/source/slang/slang-check-type.cpp
+++ b/source/slang/slang-check-type.cpp
@@ -74,7 +74,7 @@ namespace Slang
{
if (auto overloadedExpr = as<OverloadedExpr>(expr))
{
- expr = ResolveOverloadedExpr(overloadedExpr, LookupMask::type);
+ expr = resolveOverloadedExpr(overloadedExpr, LookupMask::type);
}
if (auto typeType = as<TypeType>(expr->type))
@@ -115,7 +115,7 @@ namespace Slang
if (auto overloadedExpr = as<OverloadedExpr>(exp))
{
// assume that if it is overloaded, we want a type
- exp = ResolveOverloadedExpr(overloadedExpr, LookupMask::type);
+ exp = resolveOverloadedExpr(overloadedExpr, LookupMask::type);
}
if (auto typeType = as<TypeType>(exp->type))
@@ -162,7 +162,11 @@ namespace Slang
Type* type = typeExp.type.Ptr();
if(!type && typeExp.exp)
{
- if(auto typeType = as<TypeType>(typeExp.exp->type))
+ auto expr = typeExp.exp;
+
+ expr = maybeResolveOverloadedExpr(expr, LookupMask::type, diagSink);
+
+ if(auto typeType = as<TypeType>(expr->type))
{
type = typeType->type;
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index ff9b14972..d7a45335e 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -340,7 +340,7 @@ DIAGNOSTIC(39999, Note, genericSignatureTried, "see declaration of $0")
DIAGNOSTIC(39999, Error, expectedAnInterfaceGot, "expected an interface, got '$0'")
-DIAGNOSTIC(39999, Error, ambiguousReference, "amiguous reference to '$0'");
+DIAGNOSTIC(39999, Error, ambiguousReference, "ambiguous reference to '$0'");
DIAGNOSTIC(39999, Error, declarationDidntDeclareAnything, "declaration does not declare anything");
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index f46a15d02..2a99d8e53 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -415,8 +415,12 @@ void DoLocalLookupImpl(
targetDeclRef = targetDeclRefType->declRef.as<ContainerDecl>().SubstituteImpl(containerDeclRef.substitutions, &diff);
}
- // if we are looking inside an interface decl, try find in the interfaces it inherits from
- if (targetDeclRef.is<InterfaceDecl>())
+ // When looking up inside a type, we want to also perform lookup via
+ // the types it inherits from, in case of them defines the member we are looking for.
+ //
+ // TODO: Need to be careful that this doesn't allow a type to satisfy an interface
+ // requirement using the original declaration of that requirement...
+
{
if(!targetDeclRefType)
{
diff --git a/source/slang/slang-lookup.h b/source/slang/slang-lookup.h
index 76a097d8c..d1675015d 100644
--- a/source/slang/slang-lookup.h
+++ b/source/slang/slang-lookup.h
@@ -54,6 +54,10 @@ QualType getTypeForDeclRef(
Session* session,
DeclRef<Decl> declRef);
+ /// Add a found item to a lookup result
+void AddToLookupResult(
+ LookupResult& result,
+ LookupResultItem item);
}
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index b3c45b704..19062f1df 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -1690,6 +1690,27 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
return LoweredValInfo::simple(irType);
}
+ LoweredValInfo visitThisType(ThisType* type)
+ {
+ // TODO: In theory, we should only run into a `ThisType` when lowering a concrete
+ // declaration defined on an interface type (e.g., via an `extension`).
+ //
+ // There is an open question of how we should emit a concrete method (say) defined
+ // on an `interface` type. We could emit the code in "object-oriented" style,
+ // passing in a `this` parameter of type `IFoo`, or we could emit it in a "generic"
+ // type where the whole member is wrapped in a generic on `<This : IFoo>`.
+ //
+ // The generic option has the benefit of having a clear solution in the case of
+ // static members that don't have a `this` parameter, but might still need `This`,
+ // but we have so far favored the "object-oriented" lowering for code involving
+ // bare interface types.
+ //
+ // For now we punt and emit the `ThisType` of an interface `IFoo` as `IFoo`.
+ //
+ return emitDeclRef(context, type->interfaceDeclRef, getBuilder()->getTypeKind());
+ }
+
+
// We do not expect to encounter the following types in ASTs that have
// passed front-end semantic checking.
#define UNEXPECTED_CASE(NAME) IRType* visit##NAME(NAME*) { SLANG_UNEXPECTED(#NAME); UNREACHABLE_RETURN(nullptr); }
diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp
index d74e3a993..19e4256fe 100644
--- a/source/slang/slang-syntax.cpp
+++ b/source/slang/slang-syntax.cpp
@@ -2894,4 +2894,68 @@ RefPtr<Val> ExistentialSpecializedType::SubstituteImpl(SubstitutionSet subst, in
return substType;
}
+//
+// ThisType
+//
+
+String ThisType::ToString()
+{
+ String result;
+ result.append(interfaceDeclRef.toString());
+ result.append(".This");
+ return result;
+}
+
+bool ThisType::EqualsImpl(Type * type)
+{
+ auto other = as<ThisType>(type);
+ if(!other)
+ return false;
+
+ if(!interfaceDeclRef.Equals(other->interfaceDeclRef))
+ return false;
+
+ return true;
+}
+
+int ThisType::GetHashCode()
+{
+ return combineHash(
+ HashCode(typeid(*this).hash_code()),
+ interfaceDeclRef.GetHashCode());
+}
+
+RefPtr<Type> ThisType::CreateCanonicalType()
+{
+ RefPtr<ThisType> canType = new ThisType();
+ canType->setSession(getSession());
+
+ // TODO: need to canonicalize the decl-ref
+ canType->interfaceDeclRef = interfaceDeclRef;
+ return canType;
+}
+
+RefPtr<Val> ThisType::SubstituteImpl(SubstitutionSet subst, int* ioDiff)
+{
+ int diff = 0;
+
+ auto substInterfaceDeclRef = interfaceDeclRef.SubstituteImpl(subst, &diff);
+
+ auto thisTypeSubst = findThisTypeSubstitution(subst.substitutions, substInterfaceDeclRef.getDecl());
+ if( thisTypeSubst )
+ {
+ return thisTypeSubst->witness->sub;
+ }
+
+ if(!diff)
+ return this;
+
+ (*ioDiff)++;
+
+ RefPtr<ThisType> substType = new ThisType();
+ substType->setSession(getSession());
+ substType->interfaceDeclRef = substInterfaceDeclRef;
+ return substType;
+}
+
} // namespace Slang
diff --git a/source/slang/slang-type-defs.h b/source/slang/slang-type-defs.h
index 7afc23411..9b28681d5 100644
--- a/source/slang/slang-type-defs.h
+++ b/source/slang/slang-type-defs.h
@@ -488,3 +488,17 @@ RAW(
virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
)
END_SYNTAX_CLASS()
+
+ /// The type of `this` within a polymorphic declaration
+SYNTAX_CLASS(ThisType, Type)
+RAW(
+ DeclRef<InterfaceDecl> interfaceDeclRef;
+
+ virtual String ToString() override;
+ virtual bool EqualsImpl(Type * type) override;
+ virtual int GetHashCode() override;
+ virtual RefPtr<Type> CreateCanonicalType() override;
+ virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
+)
+END_SYNTAX_CLASS()
+