diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/unix/slang-unix-process-util.cpp | 15 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 42 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 50 | ||||
| -rw-r--r-- | source/slang/slang-check-conversion.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 117 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 110 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 52 | ||||
| -rw-r--r-- | source/slang/slang-check-overload.cpp | 248 | ||||
| -rw-r--r-- | source/slang/slang-check-type.cpp | 10 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-lookup.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-lookup.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 21 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 64 | ||||
| -rw-r--r-- | source/slang/slang-type-defs.h | 14 |
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() + |
