summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-12-06 15:50:32 -0800
committerGitHub <noreply@github.com>2019-12-06 15:50:32 -0800
commit2e52217cb870b4101c1639fed78224f89bf119b3 (patch)
tree3290c233716b809d7e453364c20fc448b2c821a5 /source
parent895fcff7df1a71af59fad6bb31939ff370920eb4 (diff)
Support conversion from int/uint to enum types (#1147)
* Support conversion from int/uint to enum types The basic feature here is tiny, and is summarized in the code added to the stdlib: ``` extension __EnumType { __init(int val); __init(uint val); } ``` The front-end already makes all `enum` types implicitly conform to `__EnumType` behind the scenes, and this `extension` makes it so that all such types inherit some initializers (`__init` declarations, aka. "constructors") that take `int` and `uint`. (Note: right now all `__init` declarations in Slang are assumed to be implemented as intrinsics using `kIROp_Construct`. This obviously needs to change some day, especially so that we can support user-defined initializers.) Actually making this *work* required a bit of fleshing out pieces of the compiler that had previously been a bit ad hoc to be a bit more "correct." Most of the rest of this description is focused on those details, since the main feature is not itself very exciting. When overload resolution sees an attempt to "call" a type (e.g., `MyType(3.0)`) it needs to add appropriate overload candidates for the initializers in that type, which may take different numbers and types of parameters. The existing code for handling this case was using an ad hoc approach to try to enumerate the initializer declarations to consider, which might be found via inheritance, `extension` declarations, etc. In practice, the ad hoc logic for looking up initializers was just doing a subset of the work that already goes into doing member lookup. Changing the code so that it effectively does lookup for `MyType.__init` allows us to look up initializers in a way that is consistent with any other case of member lookup. Generalizing this lookup step brings us one step closer to being able to go from an `enum` type `E` to an initializer defined on an `extension` of an `interface` that `E` conforms to. One casualty of using the ordinary lookup logic for initializers is that we used to pass the type being constructed down into the logic that enumerated the initializers, which made it easier to short-circuit the part of overload resolution that usually asks "what type does this candidate return." It might seem "obvious" that an initializer/constructor on type `Foo` should return a value of type `Foo`, but that isn't necessarily true. Consider the `__BuiltinFloatingPointType` interface, which requires all the built-in floating-point types (`float`, `double`, `half`) to have an initializer that can take a `float`. If we call that interface in a generic context for `T : __BuiltinFloatingPointType`, then we want to treat that initializer as returning `T` and not `__BuiltinFloatingPointType`. Without the ad hoc logic in initializer overload resolution, this is the exact problem that surfaced for the stdlib definition of `clamp`. The solution to the "what type does an initializer return" problem was to introduce a notion of a `ThisType`, which refers to the type of `this` in the body of an interface. More generally, we will eventually want to have the keyword `This` be the type-level equivalent of `this`, and be usable inside any type. The `calcThisType` function introduced here computes a reasonable `Type` to represent the value of `This` within a given declaration. Inside of concrete type it refers to the type itself, while in an `interface` it will always be a `ThisType`. The existing `ThisTypeSubstitution`s, previously only applied to associated types, now apply to `ThisType`s as well, in the same situations. The next roadblock for making the simple declarations for `__EnumType` work was that the lookup logic was only doing lookup through inheritance relationships when the type being looked up in was an `interface`. The logic in play was reasonable: if you are doing lookup in a type `T` that inherits from `IFoo`, then why bother looking for `IFoo::bar` when there must be a `T::bar` if `T` actually implements the interface? The catch in this case is that `IFoo::bar` might not be a requirement of `IFoo`, but rather a concrete method added via an `extension`, in which case `T` need not have its own concrete `bar`. The simple/obvious fix here was to make the lookup logic always include inherited members, even when looking up through a concrete type. Of course, if we allow lookup to see `IFoo::bar` when looking up on `T`, then we have the problem that both `T::bar` and `IFoo::bar` show up in the lookup results, and potentially lead to an "ambiguous overload" error. This problem arises for any interface rquirement (so both methods and associated types right now). In order to get around it, I added a somewhat grungy check for comparing overload candidates (during overload resolution) or `LookupResultItem`s (during resolution of simple overloaded identifiers) that considers a member of a concrete type as automatically "better" than a member of an interface. The Right Way to solve this problem in the long run requires some more subtlety, but for now this check should Just Work. One final wrinkle is that due to our IR lowering pass being a bit overzealous, we currently end up trying to emit IR for those new `__init` declarations, which ends up causing us to try and emit IR for a `ThisType`. That is a case that will require some subtlty to handle correctly down the line, for for now we do the expedient thing and emit the `ThisType` for `IFoo` as `IFoo` itself, which is not especially correct, but doesn't matter since the concrete initializer won't ever be called. * testing: add more debug output to Unix process launch function * testing: increase timeout when running command-line tests
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()
+