// slang-check-overload.cpp #include "slang-check-impl.h" // This file implements semantic checking logic related // to resolving overloading call operations, by checking // the applicability and relative priority of various candidates. namespace Slang { SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(FilteredMemberRefList params) { ParamCounts counts = { 0, 0 }; for (auto param : params) { counts.allowed++; // No initializer means no default value // // TODO(tfoley): The logic here is currently broken in two ways: // // 1. We are assuming that once one parameter has a default, then all do. // This can/should be validated earlier, so that we can assume it here. // // 2. We are not handling the possibility of multiple declarations for // a single function, where we'd need to merge default parameters across // all the declarations. if (!param.getDecl()->initExpr) { counts.required++; } } return counts; } SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(DeclRef genericRef) { ParamCounts counts = { 0, 0 }; for (auto m : genericRef.getDecl()->Members) { if (auto typeParam = as(m)) { counts.allowed++; if (!typeParam->initType.Ptr()) { counts.required++; } } else if (auto valParam = as(m)) { counts.allowed++; if (!valParam->initExpr) { counts.required++; } } } return counts; } bool SemanticsVisitor::TryCheckOverloadCandidateArity( OverloadResolveContext& context, OverloadCandidate const& candidate) { UInt argCount = context.getArgCount(); ParamCounts paramCounts = { 0, 0 }; switch (candidate.flavor) { case OverloadCandidate::Flavor::Func: paramCounts = CountParameters(GetParameters(candidate.item.declRef.as())); break; case OverloadCandidate::Flavor::Generic: paramCounts = CountParameters(candidate.item.declRef.as()); break; default: SLANG_UNEXPECTED("unknown flavor of overload candidate"); break; } if (argCount >= paramCounts.required && argCount <= paramCounts.allowed) return true; // Emit an error message if we are checking this call for real if (context.mode != OverloadResolveContext::Mode::JustTrying) { if (argCount < paramCounts.required) { getSink()->diagnose(context.loc, Diagnostics::notEnoughArguments, argCount, paramCounts.required); } else { SLANG_ASSERT(argCount > paramCounts.allowed); getSink()->diagnose(context.loc, Diagnostics::tooManyArguments, argCount, paramCounts.allowed); } } return false; } bool SemanticsVisitor::TryCheckOverloadCandidateFixity( OverloadResolveContext& context, OverloadCandidate const& candidate) { auto expr = context.originalExpr; auto decl = candidate.item.declRef.decl; if(auto prefixExpr = as(expr)) { if(decl->HasModifier()) return true; if (context.mode != OverloadResolveContext::Mode::JustTrying) { getSink()->diagnose(context.loc, Diagnostics::expectedPrefixOperator); getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); } return false; } else if(auto postfixExpr = as(expr)) { if(decl->HasModifier()) return true; if (context.mode != OverloadResolveContext::Mode::JustTrying) { getSink()->diagnose(context.loc, Diagnostics::expectedPostfixOperator); getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); } return false; } else { return true; } return false; } bool SemanticsVisitor::TryCheckGenericOverloadCandidateTypes( OverloadResolveContext& context, OverloadCandidate& candidate) { auto genericDeclRef = candidate.item.declRef.as(); // We will go ahead and hang onto the arguments that we've // already checked, since downstream validation might need // them. auto genSubst = new GenericSubstitution(); candidate.subst = genSubst; auto& checkedArgs = genSubst->args; Index aa = 0; for (auto memberRef : getMembers(genericDeclRef)) { if (auto typeParamRef = memberRef.as()) { if (aa >= context.argCount) { return false; } auto arg = context.getArg(aa++); TypeExp typeExp; if (context.mode == OverloadResolveContext::Mode::JustTrying) { typeExp = tryCoerceToProperType(TypeExp(arg)); if(!typeExp.type) { return false; } } else { typeExp = CoerceToProperType(TypeExp(arg)); } checkedArgs.add(typeExp.type); } else if (auto valParamRef = memberRef.as()) { auto arg = context.getArg(aa++); if (context.mode == OverloadResolveContext::Mode::JustTrying) { ConversionCost cost = kConversionCost_None; if (!canCoerce(GetType(valParamRef), arg->type, &cost)) { return false; } candidate.conversionCostSum += cost; } arg = coerce(GetType(valParamRef), arg); auto val = ExtractGenericArgInteger(arg); checkedArgs.add(val); } else { continue; } } // Okay, we've made it! return true; } bool SemanticsVisitor::TryCheckOverloadCandidateTypes( OverloadResolveContext& context, OverloadCandidate& candidate) { Index argCount = context.getArgCount(); List> params; switch (candidate.flavor) { case OverloadCandidate::Flavor::Func: params = GetParameters(candidate.item.declRef.as()).ToArray(); break; case OverloadCandidate::Flavor::Generic: return TryCheckGenericOverloadCandidateTypes(context, candidate); default: SLANG_UNEXPECTED("unknown flavor of overload candidate"); break; } // Note(tfoley): We might have fewer arguments than parameters in the // case where one or more parameters had defaults. SLANG_RELEASE_ASSERT(argCount <= params.getCount()); for (Index ii = 0; ii < argCount; ++ii) { auto& arg = context.getArg(ii); auto argType = context.getArgType(ii); auto param = params[ii]; if (context.mode == OverloadResolveContext::Mode::JustTrying) { ConversionCost cost = kConversionCost_None; if( context.disallowNestedConversions ) { // We need an exact match in this case. if(!GetType(param)->Equals(argType)) return false; } else if (!canCoerce(GetType(param), argType, &cost)) { return false; } candidate.conversionCostSum += cost; } else { arg = coerce(GetType(param), arg); } } return true; } bool SemanticsVisitor::TryCheckOverloadCandidateDirections( OverloadResolveContext& /*context*/, OverloadCandidate const& /*candidate*/) { // TODO(tfoley): check `in` and `out` markers, as needed. return true; } bool SemanticsVisitor::TryCheckOverloadCandidateConstraints( OverloadResolveContext& context, OverloadCandidate const& candidate) { // We only need this step for generics, so always succeed on // everything else. if(candidate.flavor != OverloadCandidate::Flavor::Generic) return true; auto genericDeclRef = candidate.item.declRef.as(); SLANG_ASSERT(genericDeclRef); // otherwise we wouldn't be a generic candidate... // We should have the existing arguments to the generic // handy, so that we can construct a substitution list. auto subst = candidate.subst.as(); SLANG_ASSERT(subst); subst->genericDecl = genericDeclRef.getDecl(); subst->outer = genericDeclRef.substitutions.substitutions; for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType() ) { auto subset = genericDeclRef.substitutions; subset.substitutions = subst; DeclRef constraintDeclRef( constraintDecl, subset); auto sub = GetSub(constraintDeclRef); auto sup = GetSup(constraintDeclRef); auto subTypeWitness = tryGetSubtypeWitness(sub, sup); if(subTypeWitness) { subst->args.add(subTypeWitness); } else { if(context.mode != OverloadResolveContext::Mode::JustTrying) { getSink()->diagnose(context.loc, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup); } return false; } } // Done checking all the constraints, hooray. return true; } void SemanticsVisitor::TryCheckOverloadCandidate( OverloadResolveContext& context, OverloadCandidate& candidate) { if (!TryCheckOverloadCandidateArity(context, candidate)) return; candidate.status = OverloadCandidate::Status::ArityChecked; if (!TryCheckOverloadCandidateFixity(context, candidate)) return; candidate.status = OverloadCandidate::Status::FixityChecked; if (!TryCheckOverloadCandidateTypes(context, candidate)) return; candidate.status = OverloadCandidate::Status::TypeChecked; if (!TryCheckOverloadCandidateDirections(context, candidate)) return; candidate.status = OverloadCandidate::Status::DirectionChecked; if (!TryCheckOverloadCandidateConstraints(context, candidate)) return; candidate.status = OverloadCandidate::Status::Applicable; } RefPtr SemanticsVisitor::createGenericDeclRef( RefPtr baseExpr, RefPtr originalExpr, RefPtr subst) { auto baseDeclRefExpr = as(baseExpr); if (!baseDeclRefExpr) { SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration"); return CreateErrorExpr(originalExpr); } auto baseGenericRef = baseDeclRefExpr->declRef.as(); if (!baseGenericRef) { SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration"); return CreateErrorExpr(originalExpr); } subst->genericDecl = baseGenericRef.getDecl(); subst->outer = baseGenericRef.substitutions.substitutions; DeclRef innerDeclRef(GetInner(baseGenericRef), subst); RefPtr base; if (auto mbrExpr = as(baseExpr)) base = mbrExpr->BaseExpression; return ConstructDeclRefExpr( innerDeclRef, base, originalExpr->loc); } RefPtr SemanticsVisitor::CompleteOverloadCandidate( OverloadResolveContext& context, OverloadCandidate& candidate) { // special case for generic argument inference failure if (candidate.status == OverloadCandidate::Status::GenericArgumentInferenceFailed) { String callString = getCallSignatureString(context); getSink()->diagnose( context.loc, Diagnostics::genericArgumentInferenceFailed, callString); String declString = getDeclSignatureString(candidate.item); getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString); goto error; } context.mode = OverloadResolveContext::Mode::ForReal; if (!TryCheckOverloadCandidateArity(context, candidate)) goto error; if (!TryCheckOverloadCandidateFixity(context, candidate)) goto error; if (!TryCheckOverloadCandidateTypes(context, candidate)) goto error; if (!TryCheckOverloadCandidateDirections(context, candidate)) goto error; if (!TryCheckOverloadCandidateConstraints(context, candidate)) goto error; { auto baseExpr = ConstructLookupResultExpr( candidate.item, context.baseExpr, context.funcLoc); switch(candidate.flavor) { case OverloadCandidate::Flavor::Func: { RefPtr callExpr = as(context.originalExpr); if(!callExpr) { callExpr = new InvokeExpr(); callExpr->loc = context.loc; for(Index aa = 0; aa < context.argCount; ++aa) callExpr->Arguments.add(context.getArg(aa)); } callExpr->FunctionExpr = baseExpr; callExpr->type = QualType(candidate.resultType); // A call may yield an l-value, and we should take a look at the candidate to be sure if(auto subscriptDeclRef = candidate.item.declRef.as()) { for(auto setter : subscriptDeclRef.getDecl()->getMembersOfType()) { callExpr->type.IsLeftValue = true; } for(auto refAccessor : subscriptDeclRef.getDecl()->getMembersOfType()) { callExpr->type.IsLeftValue = true; } } // TODO: there may be other cases that confer l-value-ness return callExpr; } break; case OverloadCandidate::Flavor::Generic: return createGenericDeclRef( baseExpr, context.originalExpr, candidate.subst.as()); break; default: SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "unknown overload candidate flavor"); break; } } error: if(context.originalExpr) { return CreateErrorExpr(context.originalExpr.Ptr()); } else { SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "no original expression for overload result"); return nullptr; } } int SemanticsVisitor::CompareOverloadCandidates( OverloadCandidate* left, OverloadCandidate* right) { // If one candidate got further along in validation, pick it if (left->status != right->status) return int(right->status) - int(left->status); // If both candidates are applicable, then we need to compare // the costs of their type conversion sequences if(left->status == OverloadCandidate::Status::Applicable) { if (left->conversionCostSum != right->conversionCostSum) return left->conversionCostSum - right->conversionCostSum; } return 0; } void SemanticsVisitor::AddOverloadCandidateInner( OverloadResolveContext& context, OverloadCandidate& candidate) { // Filter our existing candidates, to remove any that are worse than our new one bool keepThisCandidate = true; // should this candidate be kept? if (context.bestCandidates.getCount() != 0) { // We have multiple candidates right now, so filter them. bool anyFiltered = false; // Note that we are querying the list length on every iteration, // because we might remove things. for (Index cc = 0; cc < context.bestCandidates.getCount(); ++cc) { int cmp = CompareOverloadCandidates(&candidate, &context.bestCandidates[cc]); if (cmp < 0) { // our new candidate is better! // remove it from the list (by swapping in a later one) context.bestCandidates.fastRemoveAt(cc); // and then reduce our index so that we re-visit the same index --cc; anyFiltered = true; } else if(cmp > 0) { // our candidate is worse! keepThisCandidate = false; } } // It should not be possible that we removed some existing candidate *and* // chose not to keep this candidate (otherwise the better-ness relation // isn't transitive). Therefore we confirm that we either chose to keep // this candidate (in which case filtering is okay), or we didn't filter // anything. SLANG_ASSERT(keepThisCandidate || !anyFiltered); } else if(context.bestCandidate) { // There's only one candidate so far int cmp = CompareOverloadCandidates(&candidate, context.bestCandidate); if(cmp < 0) { // our new candidate is better! context.bestCandidate = nullptr; } else if (cmp > 0) { // our candidate is worse! keepThisCandidate = false; } } // If our candidate isn't good enough, then drop it if (!keepThisCandidate) return; // Otherwise we want to keep the candidate if (context.bestCandidates.getCount() > 0) { // There were already multiple candidates, and we are adding one more context.bestCandidates.add(candidate); } else if (context.bestCandidate) { // There was a unique best candidate, but now we are ambiguous context.bestCandidates.add(*context.bestCandidate); context.bestCandidates.add(candidate); context.bestCandidate = nullptr; } else { // This is the only candidate worth keeping track of right now context.bestCandidateStorage = candidate; context.bestCandidate = &context.bestCandidateStorage; } } void SemanticsVisitor::AddOverloadCandidate( OverloadResolveContext& context, OverloadCandidate& candidate) { // Try the candidate out, to see if it is applicable at all. TryCheckOverloadCandidate(context, candidate); // Now (potentially) add it to the set of candidate overloads to consider. AddOverloadCandidateInner(context, candidate); } void SemanticsVisitor::AddFuncOverloadCandidate( LookupResultItem item, DeclRef funcDeclRef, OverloadResolveContext& context) { auto funcDecl = funcDeclRef.getDecl(); ensureDecl(funcDecl, DeclCheckState::CanUseFuncSignature); // If this function is a redeclaration, // then we don't want to include it multiple times, // and mistakenly think we have an ambiguous call. // // Instead, we will carefully consider only the // "primary" declaration of any callable. if (auto primaryDecl = funcDecl->primaryDecl) { if (funcDecl != primaryDecl) { // This is a redeclaration, so we don't // want to consider it. The primary // declaration should also get considered // for the call site and it will match // anything this declaration would have // matched. return; } } OverloadCandidate candidate; candidate.flavor = OverloadCandidate::Flavor::Func; candidate.item = item; candidate.resultType = GetResultType(funcDeclRef); AddOverloadCandidate(context, candidate); } void SemanticsVisitor::AddFuncOverloadCandidate( RefPtr /*funcType*/, OverloadResolveContext& /*context*/) { #if 0 if (funcType->decl) { AddFuncOverloadCandidate(funcType->decl, context); } else if (funcType->Func) { AddFuncOverloadCandidate(funcType->Func->SyntaxNode, context); } else if (funcType->Component) { AddComponentFuncOverloadCandidate(funcType->Component, context); } #else throw "unimplemented"; #endif } void SemanticsVisitor::AddCtorOverloadCandidate( LookupResultItem typeItem, RefPtr type, DeclRef ctorDeclRef, OverloadResolveContext& context, RefPtr resultType) { ensureDecl(ctorDeclRef, DeclCheckState::CanUseFuncSignature); // `typeItem` refers to the type being constructed (the thing // that was applied as a function) so we need to construct // a `LookupResultItem` that refers to the constructor instead LookupResultItem ctorItem; ctorItem.declRef = ctorDeclRef; ctorItem.breadcrumbs = new LookupResultItem::Breadcrumb( LookupResultItem::Breadcrumb::Kind::Member, typeItem.declRef, typeItem.breadcrumbs); OverloadCandidate candidate; candidate.flavor = OverloadCandidate::Flavor::Func; candidate.item = ctorItem; candidate.resultType = resultType; AddOverloadCandidate(context, candidate); } DeclRef SemanticsVisitor::SpecializeGenericForOverload( DeclRef genericDeclRef, OverloadResolveContext& context) { ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric); ConstraintSystem constraints; constraints.loc = context.loc; constraints.genericDecl = genericDeclRef.getDecl(); // Construct a reference to the inner declaration that has any generic // parameter substitutions in place already, but *not* any substutions // for the generic declaration we are currently trying to infer. auto innerDecl = GetInner(genericDeclRef); DeclRef unspecializedInnerRef = DeclRef(innerDecl, genericDeclRef.substitutions); // Check what type of declaration we are dealing with, and then try // to match it up with the arguments accordingly... if (auto funcDeclRef = unspecializedInnerRef.as()) { auto params = GetParameters(funcDeclRef).ToArray(); Index argCount = context.getArgCount(); Index paramCount = params.getCount(); // Bail out on mismatch. // TODO(tfoley): need more nuance here if (argCount != paramCount) { return DeclRef(nullptr, nullptr); } for (Index aa = 0; aa < argCount; ++aa) { #if 0 if (!TryUnifyArgAndParamTypes(constraints, args[aa], params[aa])) return DeclRef(nullptr, nullptr); #else // The question here is whether failure to "unify" an argument // and parameter should lead to immediate failure. // // The case that is interesting is if we want to unify, say: // `vector` and `vector` // // It is clear that we should solve with `N = 3`, and then // a later step may find that the resulting types aren't // actually a match. // // A more refined approach to "unification" could of course // see that `int` can convert to `float` and use that fact. // (and indeed we already use something like this to unify // `float` and `vector`) // // So the question is then whether a mismatch during the // unification step should be taken as an immediate failure... TryUnifyTypes(constraints, context.getArgType(aa), GetType(params[aa])); #endif } } else { // TODO(tfoley): any other cases needed here? return DeclRef(nullptr, nullptr); } auto constraintSubst = TrySolveConstraintSystem(&constraints, genericDeclRef); if (!constraintSubst) { // constraint solving failed return DeclRef(nullptr, nullptr); } // We can now construct a reference to the inner declaration using // the solution to our constraints. return DeclRef(innerDecl, constraintSubst); } void SemanticsVisitor::AddAggTypeOverloadCandidates( LookupResultItem typeItem, RefPtr type, DeclRef aggTypeDeclRef, OverloadResolveContext& context, RefPtr resultType) { for (auto ctorDeclRef : getMembersOfType(aggTypeDeclRef)) { // now work through this candidate... AddCtorOverloadCandidate(typeItem, type, ctorDeclRef, context, resultType); } // Also check for generic constructors. // // 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. // // 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. // for (auto genericDeclRef : getMembersOfType(aggTypeDeclRef)) { if (auto ctorDecl = as(genericDeclRef.getDecl()->inner)) { DeclRef innerRef = SpecializeGenericForOverload(genericDeclRef, context); if (!innerRef) continue; DeclRef innerCtorRef = innerRef.as(); 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(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(extDeclRef)) { if (auto ctorDecl = genericDeclRef.getDecl()->inner.as()) { DeclRef innerRef = SpecializeGenericForOverload(genericDeclRef, context); if (!innerRef) continue; DeclRef innerCtorRef = innerRef.as(); AddCtorOverloadCandidate(typeItem, type, innerCtorRef, context, resultType); // TODO(tfoley): need a way to do the solving step for the constraint system } } } } void SemanticsVisitor::addGenericTypeParamOverloadCandidates( DeclRef typeDeclRef, OverloadResolveContext& context, RefPtr 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(); SLANG_ASSERT(genericDeclRef); for(auto constraintDeclRef : getMembersOfType(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(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, OverloadResolveContext& context, RefPtr resultType) { if (auto declRefType = as(type)) { auto declRef = declRefType->declRef; if (auto aggTypeDeclRef = declRef.as()) { AddAggTypeOverloadCandidates(LookupResultItem(aggTypeDeclRef), type, aggTypeDeclRef, context, resultType); } else if(auto genericTypeParamDeclRef = declRef.as()) { addGenericTypeParamOverloadCandidates( genericTypeParamDeclRef, context, resultType); } } } void SemanticsVisitor::AddDeclRefOverloadCandidates( LookupResultItem item, OverloadResolveContext& context) { auto declRef = item.declRef; if (auto funcDeclRef = item.declRef.as()) { AddFuncOverloadCandidate(item, funcDeclRef, context); } else if (auto aggTypeDeclRef = item.declRef.as()) { auto type = DeclRefType::Create( getSession(), aggTypeDeclRef); AddAggTypeOverloadCandidates(item, type, aggTypeDeclRef, context, type); } else if (auto genericDeclRef = item.declRef.as()) { // Try to infer generic arguments, based on the context DeclRef innerRef = SpecializeGenericForOverload(genericDeclRef, context); if (innerRef) { // If inference works, then we've now got a // specialized declaration reference we can apply. LookupResultItem innerItem; innerItem.breadcrumbs = item.breadcrumbs; innerItem.declRef = innerRef; AddDeclRefOverloadCandidates(innerItem, context); } else { // If inference failed, then we need to create // a candidate that can be used to reflect that fact // (so we can report a good error) OverloadCandidate candidate; candidate.item = item; candidate.flavor = OverloadCandidate::Flavor::UnspecializedGeneric; candidate.status = OverloadCandidate::Status::GenericArgumentInferenceFailed; AddOverloadCandidateInner(context, candidate); } } else if( auto typeDefDeclRef = item.declRef.as() ) { auto type = getNamedType(getSession(), typeDefDeclRef); AddTypeOverloadCandidates(GetType(typeDefDeclRef), context, type); } else if( auto genericTypeParamDeclRef = item.declRef.as() ) { auto type = DeclRefType::Create( getSession(), genericTypeParamDeclRef); addGenericTypeParamOverloadCandidates(genericTypeParamDeclRef, context, type); } else { // TODO(tfoley): any other cases needed here? } } void SemanticsVisitor::AddOverloadCandidates( RefPtr funcExpr, OverloadResolveContext& context) { auto funcExprType = funcExpr->type; if (auto declRefExpr = as(funcExpr)) { // The expression directly referenced a declaration, // so we can use that declaration directly to look // for anything applicable. AddDeclRefOverloadCandidates(LookupResultItem(declRefExpr->declRef), context); } else if (auto funcType = as(funcExprType)) { // TODO(tfoley): deprecate this path... AddFuncOverloadCandidate(funcType, context); } else if (auto overloadedExpr = as(funcExpr)) { auto lookupResult = overloadedExpr->lookupResult2; SLANG_RELEASE_ASSERT(lookupResult.isOverloaded()); for(auto item : lookupResult.items) { AddDeclRefOverloadCandidates(item, context); } } else if (auto overloadedExpr2 = as(funcExpr)) { for (auto item : overloadedExpr2->candidiateExprs) { AddOverloadCandidates(item, context); } } else if (auto typeType = as(funcExprType)) { // If none of the above cases matched, but we are // looking at a type, then I suppose we have // a constructor call on our hands. // // TODO(tfoley): are there any meaningful types left // that aren't declaration references? auto type = typeType->type; AddTypeOverloadCandidates(type, context, type); return; } } void SemanticsVisitor::formatType(StringBuilder& sb, RefPtr type) { sb << type->ToString(); } void SemanticsVisitor::formatVal(StringBuilder& sb, RefPtr val) { sb << val->ToString(); } void SemanticsVisitor::formatDeclPath(StringBuilder& sb, DeclRef declRef) { // Find the parent declaration auto parentDeclRef = declRef.GetParent(); // If the immediate parent is a generic, then we probably // want the declaration above that... auto parentGenericDeclRef = parentDeclRef.as(); if(parentGenericDeclRef) { parentDeclRef = parentGenericDeclRef.GetParent(); } // Depending on what the parent is, we may want to format things specially if(auto aggTypeDeclRef = parentDeclRef.as()) { formatDeclPath(sb, aggTypeDeclRef); sb << "."; } sb << getText(declRef.GetName()); // If the parent declaration is a generic, then we need to print out its // signature if( parentGenericDeclRef ) { auto genSubst = declRef.substitutions.substitutions.as(); SLANG_RELEASE_ASSERT(genSubst); SLANG_RELEASE_ASSERT(genSubst->genericDecl == parentGenericDeclRef.getDecl()); sb << "<"; bool first = true; for(auto arg : genSubst->args) { if(!first) sb << ", "; formatVal(sb, arg); first = false; } sb << ">"; } } void SemanticsVisitor::formatDeclParams(StringBuilder& sb, DeclRef declRef) { if (auto funcDeclRef = declRef.as()) { // This is something callable, so we need to also print parameter types for overloading sb << "("; bool first = true; for (auto paramDeclRef : GetParameters(funcDeclRef)) { if (!first) sb << ", "; formatType(sb, GetType(paramDeclRef)); first = false; } sb << ")"; } else if(auto genericDeclRef = declRef.as()) { sb << "<"; bool first = true; for (auto paramDeclRef : getMembers(genericDeclRef)) { if(auto genericTypeParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; sb << getText(genericTypeParam.GetName()); } else if(auto genericValParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; formatType(sb, GetType(genericValParam)); sb << " "; sb << getText(genericValParam.GetName()); } else {} } sb << ">"; formatDeclParams(sb, DeclRef(GetInner(genericDeclRef), genericDeclRef.substitutions)); } else { } } void SemanticsVisitor::formatDeclSignature(StringBuilder& sb, DeclRef declRef) { formatDeclPath(sb, declRef); formatDeclParams(sb, declRef); } String SemanticsVisitor::getDeclSignatureString(DeclRef declRef) { StringBuilder sb; formatDeclSignature(sb, declRef); return sb.ProduceString(); } String SemanticsVisitor::getDeclSignatureString(LookupResultItem item) { return getDeclSignatureString(item.declRef); } String SemanticsVisitor::getCallSignatureString( OverloadResolveContext& context) { StringBuilder argsListBuilder; argsListBuilder << "("; UInt argCount = context.getArgCount(); for( UInt aa = 0; aa < argCount; ++aa ) { if(aa != 0) argsListBuilder << ", "; argsListBuilder << context.getArgType(aa)->ToString(); } argsListBuilder << ")"; return argsListBuilder.ProduceString(); } RefPtr SemanticsVisitor::ResolveInvoke(InvokeExpr * expr) { OverloadResolveContext context; // check if this is a stdlib operator call, if so we want to use cached results // to speed up compilation bool shouldAddToCache = false; OperatorOverloadCacheKey key; TypeCheckingCache* typeCheckingCache = getSession()->getTypeCheckingCache(); if (auto opExpr = as(expr)) { if (key.fromOperatorExpr(opExpr)) { OverloadCandidate candidate; if (typeCheckingCache->resolvedOperatorOverloadCache.TryGetValue(key, candidate)) { context.bestCandidateStorage = candidate; context.bestCandidate = &context.bestCandidateStorage; } else { shouldAddToCache = true; } } } // Look at the base expression for the call, and figure out how to invoke it. auto funcExpr = expr->FunctionExpr; auto funcExprType = funcExpr->type; // If we are trying to apply an erroneous expression, then just bail out now. if(IsErrorExpr(funcExpr)) { return CreateErrorExpr(expr); } // If any of the arguments is an error, then we should bail out, to avoid // cascading errors where we successfully pick an overload, but not the one // the user meant. for (auto arg : expr->Arguments) { if (IsErrorExpr(arg)) return CreateErrorExpr(expr); } context.originalExpr = expr; context.funcLoc = funcExpr->loc; context.argCount = expr->Arguments.getCount(); context.args = expr->Arguments.getBuffer(); context.loc = expr->loc; if (auto funcMemberExpr = as(funcExpr)) { context.baseExpr = funcMemberExpr->BaseExpression; } else if (auto funcOverloadExpr = as(funcExpr)) { context.baseExpr = funcOverloadExpr->base; } else if (auto funcOverloadExpr2 = as(funcExpr)) { context.baseExpr = funcOverloadExpr2->base; } // TODO: We should have a special case here where an `InvokeExpr` // with a single argument where the base/func expression names // a type should always be treated as an explicit type coercion // (and hence bottleneck through `coerce()`) instead of just // as a constructor call. // // Such a special-case would help us handle cases of identity // casts (casting an expression to the type it already has), // without needing dummy initializer/constructor declarations. // // Handling that special casing here (rather than in, say, // `visitTypeCastExpr`) would allow us to continue to ensure // that `(T) expr` and `T(expr)` continue to be semantically // equivalent in (almost) all cases. if (!context.bestCandidate) { AddOverloadCandidates(funcExpr, context); } if (context.bestCandidates.getCount() > 0) { // Things were ambiguous. // It might be that things were only ambiguous because // one of the argument expressions had an error, and // so a bunch of candidates could match at that position. // // If any argument was an error, we skip out on printing // another message, to avoid cascading errors. for (auto arg : expr->Arguments) { if (IsErrorExpr(arg)) { return CreateErrorExpr(expr); } } Name* funcName = nullptr; if (auto baseVar = as(funcExpr)) funcName = baseVar->name; else if(auto baseMemberRef = as(funcExpr)) funcName = baseMemberRef->name; String argsList = getCallSignatureString(context); if (context.bestCandidates[0].status != OverloadCandidate::Status::Applicable) { // There were multiple equally-good candidates, but none actually usable. // We will construct a diagnostic message to help out. if (funcName) { getSink()->diagnose(expr, Diagnostics::noApplicableOverloadForNameWithArgs, funcName, argsList); } else { getSink()->diagnose(expr, Diagnostics::noApplicableWithArgs, argsList); } } else { // There were multiple applicable candidates, so we need to report them. if (funcName) { getSink()->diagnose(expr, Diagnostics::ambiguousOverloadForNameWithArgs, funcName, argsList); } else { getSink()->diagnose(expr, Diagnostics::ambiguousOverloadWithArgs, argsList); } } { Index candidateCount = context.bestCandidates.getCount(); Index maxCandidatesToPrint = 10; // don't show too many candidates at once... Index candidateIndex = 0; for (auto candidate : context.bestCandidates) { String declString = getDeclSignatureString(candidate.item); // declString = declString + "[" + String(candidate.conversionCostSum) + "]"; #if 0 // Debugging: ensure that we don't consider multiple declarations of the same operation if (auto decl = as(candidate.item.declRef.decl)) { char buffer[1024]; sprintf_s(buffer, sizeof(buffer), "[this:%p, primary:%p, next:%p]", decl, decl->primaryDecl, decl->nextDecl); declString.append(buffer); } #endif getSink()->diagnose(candidate.item.declRef, Diagnostics::overloadCandidate, declString); candidateIndex++; if (candidateIndex == maxCandidatesToPrint) break; } if (candidateIndex != candidateCount) { getSink()->diagnose(expr, Diagnostics::moreOverloadCandidates, candidateCount - candidateIndex); } } return CreateErrorExpr(expr); } else if (context.bestCandidate) { // There was one best candidate, even if it might not have been // applicable in the end. // We will report errors for this one candidate, then, to give // the user the most help we can. if (shouldAddToCache) typeCheckingCache->resolvedOperatorOverloadCache[key] = *context.bestCandidate; return CompleteOverloadCandidate(context, *context.bestCandidate); } else { // Nothing at all was found that we could even consider invoking getSink()->diagnose(expr->FunctionExpr, Diagnostics::expectedFunction, funcExprType); expr->type = QualType(getSession()->getErrorType()); return expr; } } void SemanticsVisitor::AddGenericOverloadCandidate( LookupResultItem baseItem, OverloadResolveContext& context) { if (auto genericDeclRef = baseItem.declRef.as()) { ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric); OverloadCandidate candidate; candidate.flavor = OverloadCandidate::Flavor::Generic; candidate.item = baseItem; candidate.resultType = nullptr; AddOverloadCandidate(context, candidate); } } void SemanticsVisitor::AddGenericOverloadCandidates( RefPtr baseExpr, OverloadResolveContext& context) { if(auto baseDeclRefExpr = as(baseExpr)) { auto declRef = baseDeclRefExpr->declRef; AddGenericOverloadCandidate(LookupResultItem(declRef), context); } else if (auto overloadedExpr = as(baseExpr)) { // We are referring to a bunch of declarations, each of which might be generic LookupResult result; for (auto item : overloadedExpr->lookupResult2.items) { AddGenericOverloadCandidate(item, context); } } else { // any other cases? } } RefPtr SemanticsExprVisitor::visitGenericAppExpr(GenericAppExpr* genericAppExpr) { // Start by checking the base expression and arguments. auto& baseExpr = genericAppExpr->FunctionExpr; baseExpr = CheckTerm(baseExpr); auto& args = genericAppExpr->Arguments; for (auto& arg : args) { arg = CheckTerm(arg); } return checkGenericAppWithCheckedArgs(genericAppExpr); } /// Check a generic application where the operands have already been checked. RefPtr SemanticsVisitor::checkGenericAppWithCheckedArgs(GenericAppExpr* genericAppExpr) { // We are applying a generic to arguments, but there might be multiple generic // declarations with the same name, so this becomes a specialized case of // overload resolution. auto& baseExpr = genericAppExpr->FunctionExpr; auto& args = genericAppExpr->Arguments; // If there was an error in the base expression, or in any of // the arguments, then just bail. if (IsErrorExpr(baseExpr)) { return CreateErrorExpr(genericAppExpr); } for (auto argExpr : args) { if (IsErrorExpr(argExpr)) { return CreateErrorExpr(genericAppExpr); } } // Otherwise, let's start looking at how to find an overload... OverloadResolveContext context; context.originalExpr = genericAppExpr; context.funcLoc = baseExpr->loc; context.argCount = args.getCount(); context.args = args.getBuffer(); context.loc = genericAppExpr->loc; context.baseExpr = GetBaseExpr(baseExpr); AddGenericOverloadCandidates(baseExpr, context); if (context.bestCandidates.getCount() > 0) { // Things were ambiguous. if (context.bestCandidates[0].status != OverloadCandidate::Status::Applicable) { // There were multiple equally-good candidates, but none actually usable. // We will construct a diagnostic message to help out. // TODO(tfoley): print a reasonable message here... getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "no applicable generic"); return CreateErrorExpr(genericAppExpr); } else { // There were multiple viable candidates, but that isn't an error: we just need // to complete all of them and create an overloaded expression as a result. auto overloadedExpr = new OverloadedExpr2(); overloadedExpr->base = context.baseExpr; for (auto candidate : context.bestCandidates) { auto candidateExpr = CompleteOverloadCandidate(context, candidate); overloadedExpr->candidiateExprs.add(candidateExpr); } return overloadedExpr; } } else if (context.bestCandidate) { // There was one best candidate, even if it might not have been // applicable in the end. // We will report errors for this one candidate, then, to give // the user the most help we can. return CompleteOverloadCandidate(context, *context.bestCandidate); } else { // Nothing at all was found that we could even consider invoking getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "expected a generic"); return CreateErrorExpr(genericAppExpr); } } }