summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-check-overload.cpp
diff options
context:
space:
mode:
authorEllie Hermaszewska <ellieh@nvidia.com>2024-10-29 14:49:26 +0800
committerGitHub <noreply@github.com>2024-10-29 14:49:26 +0800
commitf65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch)
treeea1d61342cd29368e19135000ec2948813096205 /source/slang/slang-check-overload.cpp
parenta729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff)
format
* format * Minor test fixes * enable checking cpp format in ci
Diffstat (limited to 'source/slang/slang-check-overload.cpp')
-rw-r--r--source/slang/slang-check-overload.cpp4668
1 files changed, 2370 insertions, 2298 deletions
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index 0e0d64f67..609fa8635 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -1,9 +1,8 @@
// slang-check-overload.cpp
#include "slang-ast-base.h"
+#include "slang-ast-print.h"
#include "slang-check-impl.h"
-
#include "slang-lookup.h"
-#include "slang-ast-print.h"
// This file implements semantic checking logic related
// to resolving overloading call operations, by checking
@@ -11,1692 +10,1718 @@
namespace Slang
{
- SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(FilteredMemberRefList<ParamDecl> params)
+SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(
+ FilteredMemberRefList<ParamDecl> params)
+{
+ ParamCounts counts = {0, 0};
+ for (auto param : params)
{
- ParamCounts counts = { 0, 0 };
- for (auto param : params)
+ Index allowedArgCountToAdd = 1;
+ auto paramType = getParamType(m_astBuilder, param);
+ if (isTypePack(paramType))
{
- Index allowedArgCountToAdd = 1;
- auto paramType = getParamType(m_astBuilder, param);
- if (isTypePack(paramType))
+ if (auto typePack = as<ConcreteTypePack>(paramType))
{
- if (auto typePack = as<ConcreteTypePack>(paramType))
- {
- counts.required += typePack->getTypeCount();
- allowedArgCountToAdd = typePack->getTypeCount();
- }
- else
- {
- counts.allowed = -1;
- }
+ counts.required += typePack->getTypeCount();
+ allowedArgCountToAdd = typePack->getTypeCount();
}
- else if (!param.getDecl()->initExpr)
+ else
{
- // 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.
- counts.required++;
+ counts.allowed = -1;
}
-
- if (counts.allowed >= 0)
- counts.allowed += allowedArgCountToAdd;
}
- return counts;
+ else if (!param.getDecl()->initExpr)
+ {
+ // 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.
+ counts.required++;
+ }
+
+ if (counts.allowed >= 0)
+ counts.allowed += allowedArgCountToAdd;
}
+ return counts;
+}
- SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(DeclRef<GenericDecl> genericRef)
+SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters(DeclRef<GenericDecl> genericRef)
+{
+ ParamCounts counts = {0, 0};
+ for (auto m : genericRef.getDecl()->members)
{
- ParamCounts counts = { 0, 0 };
- for (auto m : genericRef.getDecl()->members)
+ if (auto typeParam = as<GenericTypeParamDecl>(m))
{
- if (auto typeParam = as<GenericTypeParamDecl>(m))
- {
- if (counts.allowed >= 0)
- counts.allowed++;
- if (!typeParam->initType.Ptr())
- {
- counts.required++;
- }
- }
- else if (auto valParam = as<GenericValueParamDecl>(m))
+ if (counts.allowed >= 0)
+ counts.allowed++;
+ if (!typeParam->initType.Ptr())
{
- if (counts.allowed >= 0)
- counts.allowed++;
- if (!valParam->initExpr)
- {
- counts.required++;
- }
+ counts.required++;
}
- else if (as<GenericTypePackParamDecl>(m))
+ }
+ else if (auto valParam = as<GenericValueParamDecl>(m))
+ {
+ if (counts.allowed >= 0)
+ counts.allowed++;
+ if (!valParam->initExpr)
{
- counts.allowed = -1;
+ counts.required++;
}
}
- return counts;
+ else if (as<GenericTypePackParamDecl>(m))
+ {
+ counts.allowed = -1;
+ }
}
+ return counts;
+}
- bool SemanticsVisitor::TryCheckOverloadCandidateClassNewMatchUp(OverloadResolveContext& context, OverloadCandidate const& candidate)
+bool SemanticsVisitor::TryCheckOverloadCandidateClassNewMatchUp(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+{
+ // Check that a constructor call to a class type must be in a `new` expr, and a `new` expr
+ // is only used to construct a class.
+ bool isClassType = false;
+ bool isNewExpr = false;
+ if (auto ctorDeclRef = candidate.item.declRef.as<ConstructorDecl>())
{
- // Check that a constructor call to a class type must be in a `new` expr, and a `new` expr
- // is only used to construct a class.
- bool isClassType = false;
- bool isNewExpr = false;
- if (auto ctorDeclRef = candidate.item.declRef.as<ConstructorDecl>())
+ if (auto resultType = as<DeclRefType>(candidate.resultType))
{
- if (auto resultType = as<DeclRefType>(candidate.resultType))
+ if (resultType->getDeclRef().as<ClassDecl>())
{
- if (resultType->getDeclRef().as<ClassDecl>())
- {
- isClassType = true;
- }
+ isClassType = true;
}
}
- if (as<NewExpr>(context.originalExpr))
+ }
+ if (as<NewExpr>(context.originalExpr))
+ {
+ isNewExpr = true;
+ }
+
+ if (isNewExpr && !isClassType)
+ {
+ getSink()->diagnose(context.originalExpr, Diagnostics::newCanOnlyBeUsedToInitializeAClass);
+ return false;
+ }
+ if (!isNewExpr && isClassType && context.originalExpr)
+ {
+ getSink()->diagnose(context.originalExpr, Diagnostics::classCanOnlyBeInitializedWithNew);
+ return false;
+ }
+ return true;
+}
+
+bool SemanticsVisitor::TryCheckOverloadCandidateArity(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+{
+ Count argCount = context.getArgCount();
+ ParamCounts paramCounts = {0, 0};
+ switch (candidate.flavor)
+ {
+ case OverloadCandidate::Flavor::Func:
+ paramCounts =
+ CountParameters(getParameters(m_astBuilder, candidate.item.declRef.as<CallableDecl>()));
+ break;
+
+ case OverloadCandidate::Flavor::Generic:
+ paramCounts = CountParameters(candidate.item.declRef.as<GenericDecl>());
+
+ // A generic can be applied to any number of arguments less
+ // than or equal to the number of explicitly declared parameters.
+ // When a program provides fewer arguments than their are parameters,
+ // the rest will be inferred.
+ //
+ paramCounts.required = 0;
+ break;
+
+ case OverloadCandidate::Flavor::Expr:
{
- isNewExpr = true;
+ auto paramCount = candidate.funcType->getParamCount();
+ paramCounts.allowed = paramCount;
+ paramCounts.required = paramCount;
}
+ break;
+
+ default: SLANG_UNEXPECTED("unknown flavor of overload candidate"); break;
+ }
+
+ if (argCount >= paramCounts.required &&
+ (paramCounts.allowed == -1 || argCount <= paramCounts.allowed))
+ return true;
- if (isNewExpr && !isClassType)
+ // 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.originalExpr, Diagnostics::newCanOnlyBeUsedToInitializeAClass);
- return false;
+ getSink()->diagnose(
+ context.loc,
+ Diagnostics::notEnoughArguments,
+ argCount,
+ paramCounts.required);
}
- if (!isNewExpr && isClassType && context.originalExpr)
+ else
{
- getSink()->diagnose(context.originalExpr, Diagnostics::classCanOnlyBeInitializedWithNew);
- return false;
+ SLANG_ASSERT(argCount > paramCounts.allowed);
+ getSink()->diagnose(
+ context.loc,
+ Diagnostics::tooManyArguments,
+ argCount,
+ paramCounts.allowed);
}
- return true;
}
- bool SemanticsVisitor::TryCheckOverloadCandidateArity(
- OverloadResolveContext& context,
- OverloadCandidate const& candidate)
- {
- Count argCount = context.getArgCount();
- ParamCounts paramCounts = { 0, 0 };
- switch (candidate.flavor)
- {
- case OverloadCandidate::Flavor::Func:
- paramCounts = CountParameters(getParameters(m_astBuilder, candidate.item.declRef.as<CallableDecl>()));
- break;
+ return false;
+}
- case OverloadCandidate::Flavor::Generic:
- paramCounts = CountParameters(candidate.item.declRef.as<GenericDecl>());
+bool SemanticsVisitor::TryCheckOverloadCandidateFixity(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+{
+ auto expr = context.originalExpr;
- // A generic can be applied to any number of arguments less
- // than or equal to the number of explicitly declared parameters.
- // When a program provides fewer arguments than their are parameters,
- // the rest will be inferred.
- //
- paramCounts.required = 0;
- break;
+ auto decl = candidate.item.declRef.getDecl();
- case OverloadCandidate::Flavor::Expr:
- {
- auto paramCount = candidate.funcType->getParamCount();
- paramCounts.allowed = paramCount;
- paramCounts.required = paramCount;
- }
- break;
+ if (const auto prefixExpr = as<PrefixExpr>(expr))
+ {
+ if (decl->hasModifier<PrefixModifier>())
+ return true;
- default:
- SLANG_UNEXPECTED("unknown flavor of overload candidate");
- break;
+ if (context.mode != OverloadResolveContext::Mode::JustTrying)
+ {
+ getSink()->diagnose(context.loc, Diagnostics::expectedPrefixOperator);
+ getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName());
}
- if (argCount >= paramCounts.required && (paramCounts.allowed == -1 || argCount <= paramCounts.allowed))
+ return false;
+ }
+ else if (const auto postfixExpr = as<PostfixExpr>(expr))
+ {
+ if (decl->hasModifier<PostfixModifier>())
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);
- }
+ getSink()->diagnose(context.loc, Diagnostics::expectedPostfixOperator);
+ getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName());
}
return false;
}
+ else
+ {
+ return true;
+ }
+}
- bool SemanticsVisitor::TryCheckOverloadCandidateFixity(
- OverloadResolveContext& context,
- OverloadCandidate const& candidate)
+bool SemanticsVisitor::TryCheckOverloadCandidateVisibility(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+{
+ // Always succeeds when we are trying out constructors.
+ if (context.mode == OverloadResolveContext::Mode::JustTrying)
{
- auto expr = context.originalExpr;
+ if (as<ConstructorDecl>(candidate.item.declRef))
+ return true;
+ }
- auto decl = candidate.item.declRef.getDecl();
+ if (!context.sourceScope)
+ return true;
- if(const auto prefixExpr = as<PrefixExpr>(expr))
+ if (!candidate.item.declRef)
+ return true;
+
+ if (!isDeclVisibleFromScope(candidate.item.declRef, context.sourceScope))
+ {
+ if (context.mode == OverloadResolveContext::Mode::ForReal)
{
- if(decl->hasModifier<PrefixModifier>())
- return true;
+ getSink()->diagnose(context.loc, Diagnostics::declIsNotVisible, candidate.item.declRef);
+ }
+ return false;
+ }
- if (context.mode != OverloadResolveContext::Mode::JustTrying)
- {
- getSink()->diagnose(context.loc, Diagnostics::expectedPrefixOperator);
- getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName());
- }
+ return true;
+}
- return false;
- }
- else if(const auto postfixExpr = as<PostfixExpr>(expr))
- {
- if(decl->hasModifier<PostfixModifier>())
- return true;
+bool SemanticsVisitor::TryCheckGenericOverloadCandidateTypes(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ auto genericDeclRef = candidate.item.declRef.as<GenericDecl>();
- if (context.mode != OverloadResolveContext::Mode::JustTrying)
- {
- getSink()->diagnose(context.loc, Diagnostics::expectedPostfixOperator);
- getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName());
- }
+ // Only allow constructing a PartialGenericAppExpr when referencing a callable decl.
+ // Other types of generic decls must be fully specified.
+ bool allowPartialGenericApp = false;
+ if (as<CallableDecl>(genericDeclRef.getDecl()->inner))
+ {
+ allowPartialGenericApp = true;
+ }
- return false;
- }
- else
+ // The basic idea here is that we need to check that the
+ // arguments to a generic application (e.g., `F<A1, A2, ...>`)
+ // have the right "type," which in this context means
+ // checking that:
+ //
+ // * The argument for any generic type parameter is a (proper) type.
+ //
+ // * The argument for any generic value parameter is a
+ // specialization-time constant value of the appropriate type.
+ //
+ // Some additional checks are *not* handled at this point:
+ //
+ // * We don't check that a type argument actually conforms to
+ // the constraints on the parameter.
+ //
+ // Along the way we will build up a `GenericSubstitution`
+ // to represent the arguments that have been coerced to
+ // appropriate forms.
+ //
+ List<Val*> checkedArgs;
+
+ // Rather than bail out as soon as we hit a problem,
+ // we are going to process *all* of the parameters of the
+ // generic and place suitable arguments into the `checkedArgs`
+ // array. This is important so that we don't cause crashes
+ // in cases where the arguments fail this step of checking,
+ // but we decide to proceed with subsequent steps (e.g.,
+ // because the candidate we are trying here is the *only*
+ // candidate).
+ //
+ bool success = true;
+
+ auto maybeReportGeneralError = [&]()
+ {
+ if (context.mode != OverloadResolveContext::Mode::JustTrying)
{
- return true;
+ getSink()->diagnose(
+ context.loc,
+ Diagnostics::cannotSpecializeGeneric,
+ candidate.item.declRef);
}
- }
-
- bool SemanticsVisitor::TryCheckOverloadCandidateVisibility(OverloadResolveContext& context, OverloadCandidate const& candidate)
+ };
+ List<QualType> paramTypes;
+ for (auto memberRef : getMembers(m_astBuilder, genericDeclRef))
{
- // Always succeeds when we are trying out constructors.
- if (context.mode == OverloadResolveContext::Mode::JustTrying)
+ if (auto typeParamRef = memberRef.as<GenericTypeParamDecl>())
{
- if (as<ConstructorDecl>(candidate.item.declRef))
- return true;
+ paramTypes.add(DeclRefType::create(m_astBuilder, typeParamRef));
}
-
- if (!context.sourceScope)
- return true;
-
- if (!candidate.item.declRef)
- return true;
-
- if (!isDeclVisibleFromScope(candidate.item.declRef, context.sourceScope))
+ else if (auto valParamRef = memberRef.as<GenericValueParamDecl>())
{
- if (context.mode == OverloadResolveContext::Mode::ForReal)
- {
- getSink()->diagnose(context.loc, Diagnostics::declIsNotVisible, candidate.item.declRef);
- }
- return false;
+ paramTypes.add(getType(m_astBuilder, valParamRef));
}
-
- return true;
- }
-
- bool SemanticsVisitor::TryCheckGenericOverloadCandidateTypes(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
- {
- auto genericDeclRef = candidate.item.declRef.as<GenericDecl>();
-
- // Only allow constructing a PartialGenericAppExpr when referencing a callable decl.
- // Other types of generic decls must be fully specified.
- bool allowPartialGenericApp = false;
- if (as<CallableDecl>(genericDeclRef.getDecl()->inner))
+ else if (auto typePackParam = memberRef.as<GenericTypePackParamDecl>())
{
- allowPartialGenericApp = true;
+ paramTypes.add(DeclRefType::create(m_astBuilder, typePackParam));
}
+ }
+ ShortList<OverloadResolveContext::MatchedArg> matchedArgs;
+ if (!context.matchArgumentsToParams(this, paramTypes, false, matchedArgs))
+ {
+ maybeReportGeneralError();
+ return false;
+ }
- // The basic idea here is that we need to check that the
- // arguments to a generic application (e.g., `F<A1, A2, ...>`)
- // have the right "type," which in this context means
- // checking that:
- //
- // * The argument for any generic type parameter is a (proper) type.
- //
- // * The argument for any generic value parameter is a
- // specialization-time constant value of the appropriate type.
- //
- // Some additional checks are *not* handled at this point:
- //
- // * We don't check that a type argument actually conforms to
- // the constraints on the parameter.
- //
- // Along the way we will build up a `GenericSubstitution`
- // to represent the arguments that have been coerced to
- // appropriate forms.
- //
- List<Val*> checkedArgs;
-
- // Rather than bail out as soon as we hit a problem,
- // we are going to process *all* of the parameters of the
- // generic and place suitable arguments into the `checkedArgs`
- // array. This is important so that we don't cause crashes
- // in cases where the arguments fail this step of checking,
- // but we decide to proceed with subsequent steps (e.g.,
- // because the candidate we are trying here is the *only*
- // candidate).
- //
- bool success = true;
-
- auto maybeReportGeneralError = [&]()
+ Index aa = 0;
+ for (auto memberRef : getMembers(m_astBuilder, genericDeclRef))
+ {
+ if (auto typeParamRef = memberRef.as<GenericTypeParamDecl>())
{
- if (context.mode != OverloadResolveContext::Mode::JustTrying)
+ if (aa >= matchedArgs.getCount())
{
- getSink()->diagnose(context.loc, Diagnostics::cannotSpecializeGeneric, candidate.item.declRef);
+ if (allowPartialGenericApp)
+ {
+ // If we have run out of arguments, and the referenced decl
+ // allows partially applied specialization (i.e. a callable
+ // decl) then we don't apply any more checks at this step.
+ // We will instead attempt to *infer* an argument at this
+ // position at a later stage.
+ //
+ candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
+ break;
+ }
+ else
+ {
+ // Otherwise, the generic decl had better provide a default value
+ // or this reference is ill-formed.
+ auto substType = typeParamRef.substitute(
+ m_astBuilder,
+ typeParamRef.getDecl()->initType.type);
+ if (!substType)
+ {
+ maybeReportGeneralError();
+ return false;
+ }
+ checkedArgs.add(substType);
+ continue;
+ }
}
- };
- List<QualType> paramTypes;
- for (auto memberRef : getMembers(m_astBuilder, genericDeclRef))
- {
- if (auto typeParamRef = memberRef.as<GenericTypeParamDecl>())
+
+ // We have a type parameter, and we expect to find
+ // a type argument.
+ //
+ TypeExp typeArg;
+
+ // Per the earlier check, we have at least one
+ // argument left, so we will grab
+ // it and try to coerce it to a proper type. The
+ // manner in which we handle the coercion depends
+ // on whether we are "just trying" the candidate
+ // (so a failure would rule out the candidate, but
+ // shouldn't be reported to the user), or are doing
+ // the checking "for real" in which case any errors
+ // we run into need to be reported.
+ //
+ auto arg = matchedArgs[aa++];
+ if (context.mode == OverloadResolveContext::Mode::JustTrying)
{
- paramTypes.add(DeclRefType::create(m_astBuilder, typeParamRef));
+ typeArg = tryCoerceToProperType(TypeExp(arg.argExpr));
}
- else if (auto valParamRef = memberRef.as<GenericValueParamDecl>())
+ else
{
- paramTypes.add(getType(m_astBuilder, valParamRef));
+ arg.argExpr = ExpectATypeRepr(arg.argExpr);
+ typeArg = CoerceToProperType(TypeExp(arg.argExpr));
}
- else if (auto typePackParam = memberRef.as<GenericTypePackParamDecl>())
+
+ // If we failed to get a valid type (either because
+ // there was no matching argument, or because the
+ // "just trying" coercion failed), then we create
+ // an error type to stand in for the argument
+ //
+ if (!typeArg.type)
{
- paramTypes.add(DeclRefType::create(m_astBuilder, typePackParam));
+ typeArg.type = m_astBuilder->getErrorType();
+ success = false;
}
- }
- ShortList<OverloadResolveContext::MatchedArg> matchedArgs;
- if (!context.matchArgumentsToParams(this, paramTypes, false, matchedArgs))
- {
- maybeReportGeneralError();
- return false;
- }
- Index aa = 0;
- for (auto memberRef : getMembers(m_astBuilder, genericDeclRef))
+ checkedArgs.add(typeArg.type);
+ }
+ else if (auto valParamRef = memberRef.as<GenericValueParamDecl>())
{
- if (auto typeParamRef = memberRef.as<GenericTypeParamDecl>())
+ if (aa >= matchedArgs.getCount())
{
- if (aa >= matchedArgs.getCount())
- {
- if (allowPartialGenericApp)
- {
- // If we have run out of arguments, and the referenced decl
- // allows partially applied specialization (i.e. a callable
- // decl) then we don't apply any more checks at this step.
- // We will instead attempt to *infer* an argument at this
- // position at a later stage.
- //
- candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
- break;
- }
- else
- {
- // Otherwise, the generic decl had better provide a default value
- // or this reference is ill-formed.
- auto substType = typeParamRef.substitute(m_astBuilder, typeParamRef.getDecl()->initType.type);
- if (!substType)
- {
- maybeReportGeneralError();
- return false;
- }
- checkedArgs.add(substType);
- continue;
- }
- }
-
- // We have a type parameter, and we expect to find
- // a type argument.
- //
- TypeExp typeArg;
-
- // Per the earlier check, we have at least one
- // argument left, so we will grab
- // it and try to coerce it to a proper type. The
- // manner in which we handle the coercion depends
- // on whether we are "just trying" the candidate
- // (so a failure would rule out the candidate, but
- // shouldn't be reported to the user), or are doing
- // the checking "for real" in which case any errors
- // we run into need to be reported.
- //
- auto arg = matchedArgs[aa++];
- if (context.mode == OverloadResolveContext::Mode::JustTrying)
+ if (allowPartialGenericApp)
{
- typeArg = tryCoerceToProperType(TypeExp(arg.argExpr));
+ // If we have run out of arguments and the decl allows
+ // partial specialization, then we don't apply any more
+ // checks at this step. We will instead attempt to
+ // *infer* an argument at this position at a later
+ // stage.
+ //
+ candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
+ break;
}
else
{
- arg.argExpr = ExpectATypeRepr(arg.argExpr);
- typeArg = CoerceToProperType(TypeExp(arg.argExpr));
+ // Otherwise, the generic decl had better provide a default value
+ // or this reference is ill-formed.
+ ensureDecl(valParamRef, DeclCheckState::DefinitionChecked);
+ ConstantFoldingCircularityInfo newCircularityInfo(
+ valParamRef.getDecl(),
+ nullptr);
+ auto defaultVal = tryConstantFoldExpr(
+ valParamRef.substitute(m_astBuilder, valParamRef.getDecl()->initExpr),
+ ConstantFoldingKind::CompileTime,
+ &newCircularityInfo);
+ if (!defaultVal)
+ {
+ maybeReportGeneralError();
+ return false;
+ }
+ checkedArgs.add(defaultVal);
+ continue;
}
+ }
- // If we failed to get a valid type (either because
- // there was no matching argument, or because the
- // "just trying" coercion failed), then we create
- // an error type to stand in for the argument
- //
- if( !typeArg.type )
+ // The case for a generic value parameter is similar to that
+ // for a generic type parameter.
+ //
+ Expr* arg = nullptr;
+
+ // If we have an argument then we need to coerce it
+ // to the type of the parameter (and fail if the
+ // coercion is not possible)
+ //
+ arg = matchedArgs[aa++].argExpr;
+ if (context.mode == OverloadResolveContext::Mode::JustTrying)
+ {
+ ConversionCost cost = kConversionCost_None;
+ if (!canCoerce(getType(m_astBuilder, valParamRef), arg->type, arg, &cost))
{
- typeArg.type = m_astBuilder->getErrorType();
success = false;
}
-
- checkedArgs.add(typeArg.type);
+ candidate.conversionCostSum += cost;
}
- else if (auto valParamRef = memberRef.as<GenericValueParamDecl>())
+ else
{
- if (aa >= matchedArgs.getCount())
- {
- if (allowPartialGenericApp)
- {
- // If we have run out of arguments and the decl allows
- // partial specialization, then we don't apply any more
- // checks at this step. We will instead attempt to
- // *infer* an argument at this position at a later
- // stage.
- //
- candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
- break;
- }
- else
- {
- // Otherwise, the generic decl had better provide a default value
- // or this reference is ill-formed.
- ensureDecl(valParamRef, DeclCheckState::DefinitionChecked);
- ConstantFoldingCircularityInfo newCircularityInfo(valParamRef.getDecl(), nullptr);
- auto defaultVal = tryConstantFoldExpr(valParamRef.substitute(m_astBuilder, valParamRef.getDecl()->initExpr), ConstantFoldingKind::CompileTime, &newCircularityInfo);
- if (!defaultVal)
- {
- maybeReportGeneralError();
- return false;
- }
- checkedArgs.add(defaultVal);
- continue;
- }
- }
+ arg = coerce(CoercionSite::Argument, getType(m_astBuilder, valParamRef), arg);
+ }
- // The case for a generic value parameter is similar to that
- // for a generic type parameter.
- //
- Expr* arg = nullptr;
+ // If we have an argument to work with, then we will
+ // try to extract its speicalization-time constant value.
+ //
+ Val* val = nullptr;
+ if (arg)
+ {
+ val = ExtractGenericArgInteger(
+ arg,
+ getType(m_astBuilder, valParamRef),
+ context.mode == OverloadResolveContext::Mode::JustTrying ? nullptr : getSink());
+ }
- // If we have an argument then we need to coerce it
- // to the type of the parameter (and fail if the
- // coercion is not possible)
- //
- arg = matchedArgs[aa++].argExpr;
- if (context.mode == OverloadResolveContext::Mode::JustTrying)
+ // If any of the above checking steps fail and we don't
+ // have a value to work with here, we will instead
+ // use an "error" value to stand in for the argument.
+ //
+ if (!val)
+ {
+ val = m_astBuilder->getOrCreate<ErrorIntVal>(m_astBuilder->getIntType());
+ }
+ checkedArgs.add(val);
+ }
+ else if (auto typePackParam = memberRef.as<GenericTypePackParamDecl>())
+ {
+ Val* val = nullptr;
+ if (aa >= matchedArgs.getCount())
+ {
+ if (allowPartialGenericApp)
{
- ConversionCost cost = kConversionCost_None;
- if (!canCoerce(getType(m_astBuilder, valParamRef), arg->type, arg, &cost))
- {
- success = false;
- }
- candidate.conversionCostSum += cost;
+ // If we have run out of arguments and the decl allows
+ // partial specialization, then we don't apply any more
+ // checks at this step. We will instead attempt to
+ // *infer* an argument at this position at a later
+ // stage.
+ //
+ candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
+ break;
}
else
{
- arg = coerce(CoercionSite::Argument, getType(m_astBuilder, valParamRef), arg);
- }
-
- // If we have an argument to work with, then we will
- // try to extract its speicalization-time constant value.
- //
- Val* val = nullptr;
- if( arg )
- {
- val = ExtractGenericArgInteger(arg, getType(m_astBuilder, valParamRef), context.mode == OverloadResolveContext::Mode::JustTrying ? nullptr : getSink());
+ // Otherwise, we will just create an empty pack.
+ val = m_astBuilder->getTypePack(ArrayView<Type*>());
}
-
- // If any of the above checking steps fail and we don't
- // have a value to work with here, we will instead
- // use an "error" value to stand in for the argument.
- //
- if( !val )
- {
- val = m_astBuilder->getOrCreate<ErrorIntVal>(m_astBuilder->getIntType());
- }
- checkedArgs.add(val);
}
- else if (auto typePackParam = memberRef.as<GenericTypePackParamDecl>())
+ else
{
- Val* val = nullptr;
- if (aa >= matchedArgs.getCount())
- {
- if (allowPartialGenericApp)
- {
- // If we have run out of arguments and the decl allows
- // partial specialization, then we don't apply any more
- // checks at this step. We will instead attempt to
- // *infer* an argument at this position at a later
- // stage.
- //
- candidate.flags |= OverloadCandidate::Flag::IsPartiallyAppliedGeneric;
- break;
- }
- else
- {
- // Otherwise, we will just create an empty pack.
- val = m_astBuilder->getTypePack(ArrayView<Type*>());
- }
- }
- else
+ auto matchedArg = matchedArgs[aa++];
+ if (auto packExpr = as<PackExpr>(matchedArg.argExpr))
{
- auto matchedArg = matchedArgs[aa++];
- if (auto packExpr = as<PackExpr>(matchedArg.argExpr))
- {
- // We are providing a concrete pack of types as arguments to a type pack parameter.
- // We need to create a `TypePack` type to serve as the argument.
- ShortList<Type*> coercedProperTypes;
+ // We are providing a concrete pack of types as arguments to a type pack
+ // parameter. We need to create a `TypePack` type to serve as the argument.
+ ShortList<Type*> coercedProperTypes;
- // Coerce all types in the pack to proper types.
- for (Index i = 0; i < packExpr->args.getCount(); i++)
+ // Coerce all types in the pack to proper types.
+ for (Index i = 0; i < packExpr->args.getCount(); i++)
+ {
+ TypeExp typeArg;
+ auto elementTypeExpr = packExpr->args[i];
+ if (context.mode == OverloadResolveContext::Mode::JustTrying)
{
- TypeExp typeArg;
- auto elementTypeExpr = packExpr->args[i];
- if (context.mode == OverloadResolveContext::Mode::JustTrying)
- {
- typeArg = tryCoerceToProperType(TypeExp(elementTypeExpr));
- if (!typeArg.type)
- {
- typeArg.type = m_astBuilder->getErrorType();
- success = false;
- }
- }
- else
- {
- elementTypeExpr = ExpectATypeRepr(elementTypeExpr);
- typeArg = CoerceToProperType(TypeExp(elementTypeExpr));
- }
- // If we failed to get a valid type (either because
- // there was no matching argument, or because the
- // "just trying" coercion failed), then we create
- // an error type to stand in for the argument
- //
+ typeArg = tryCoerceToProperType(TypeExp(elementTypeExpr));
if (!typeArg.type)
{
typeArg.type = m_astBuilder->getErrorType();
success = false;
}
- coercedProperTypes.add(typeArg.type);
}
- val = m_astBuilder->getTypePack(coercedProperTypes.getArrayView().arrayView);
- }
- else if (auto expandExpr = as<ExpandExpr>(matchedArg.argExpr))
- {
- auto argType = expandExpr->type.type;
- if (auto typeType = as<TypeType>(argType))
- argType = typeType->getType();
- val = argType;
- }
- else if (auto typeType = as<TypeType>(matchedArg.argType))
- {
- if (isAbstractTypePack(typeType->getType()))
+ else
+ {
+ elementTypeExpr = ExpectATypeRepr(elementTypeExpr);
+ typeArg = CoerceToProperType(TypeExp(elementTypeExpr));
+ }
+ // If we failed to get a valid type (either because
+ // there was no matching argument, or because the
+ // "just trying" coercion failed), then we create
+ // an error type to stand in for the argument
+ //
+ if (!typeArg.type)
{
- val = typeType->getType();
+ typeArg.type = m_astBuilder->getErrorType();
+ success = false;
}
+ coercedProperTypes.add(typeArg.type);
}
+ val = m_astBuilder->getTypePack(coercedProperTypes.getArrayView().arrayView);
}
- if (val == nullptr)
+ else if (auto expandExpr = as<ExpandExpr>(matchedArg.argExpr))
{
- maybeReportGeneralError();
- return false;
+ auto argType = expandExpr->type.type;
+ if (auto typeType = as<TypeType>(argType))
+ argType = typeType->getType();
+ val = argType;
+ }
+ else if (auto typeType = as<TypeType>(matchedArg.argType))
+ {
+ if (isAbstractTypePack(typeType->getType()))
+ {
+ val = typeType->getType();
+ }
}
- checkedArgs.add(val);
}
- else
+ if (val == nullptr)
{
- continue;
+ maybeReportGeneralError();
+ return false;
}
+ checkedArgs.add(val);
}
+ else
+ {
+ continue;
+ }
+ }
- auto genSubst = m_astBuilder->getGenericAppDeclRef(genericDeclRef, checkedArgs.getArrayView());
- candidate.subst = SubstitutionSet(genSubst);
+ auto genSubst = m_astBuilder->getGenericAppDeclRef(genericDeclRef, checkedArgs.getArrayView());
+ candidate.subst = SubstitutionSet(genSubst);
- // Once we are done processing the parameters of the generic,
- // we will have build up a usable `checkedArgs` array and
- // can return to the caller a report of whether we
- // were successful or not.
- //
- return success;
- }
+ // Once we are done processing the parameters of the generic,
+ // we will have build up a usable `checkedArgs` array and
+ // can return to the caller a report of whether we
+ // were successful or not.
+ //
+ return success;
+}
- static QualType getParamQualType(ASTBuilder* astBuilder, DeclRef<ParamDecl> param)
+static QualType getParamQualType(ASTBuilder* astBuilder, DeclRef<ParamDecl> param)
+{
+ auto paramType = getType(astBuilder, param);
+ bool isLVal = false;
+ switch (getParameterDirection(param.getDecl()))
{
- auto paramType = getType(astBuilder, param);
- bool isLVal = false;
- switch (getParameterDirection(param.getDecl()))
- {
- case kParameterDirection_InOut:
- case kParameterDirection_Out:
- case kParameterDirection_Ref:
- isLVal = true;
- break;
- }
- return QualType(paramType, isLVal);
+ case kParameterDirection_InOut:
+ case kParameterDirection_Out:
+ case kParameterDirection_Ref: isLVal = true; break;
}
+ return QualType(paramType, isLVal);
+}
- static QualType getParamQualType(Type* paramType)
+static QualType getParamQualType(Type* paramType)
+{
+ if (auto paramDirType = as<ParamDirectionType>(paramType))
{
- if (auto paramDirType = as<ParamDirectionType>(paramType))
- {
- if (as<OutTypeBase>(paramDirType) || as<RefType>(paramDirType))
- return QualType(paramDirType->getValueType(), true);
- }
- return paramType;
+ if (as<OutTypeBase>(paramDirType) || as<RefType>(paramDirType))
+ return QualType(paramDirType->getValueType(), true);
}
+ return paramType;
+}
+
+bool SemanticsVisitor::TryCheckOverloadCandidateTypes(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ Index argCount = context.getArgCount();
- bool SemanticsVisitor::TryCheckOverloadCandidateTypes(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
+ List<QualType> paramTypes;
+ switch (candidate.flavor)
{
- Index argCount = context.getArgCount();
+ case OverloadCandidate::Flavor::Func:
+ for (auto param : getParameters(m_astBuilder, candidate.item.declRef.as<CallableDecl>()))
+ {
+ paramTypes.add(getParamQualType(m_astBuilder, param));
+ }
+ break;
- List<QualType> paramTypes;
- switch (candidate.flavor)
+ case OverloadCandidate::Flavor::Expr:
{
- case OverloadCandidate::Flavor::Func:
- for (auto param : getParameters(m_astBuilder, candidate.item.declRef.as<CallableDecl>()))
+ auto funcType = candidate.funcType;
+ Count paramCount = funcType->getParamCount();
+ for (Index i = 0; i < paramCount; ++i)
{
- paramTypes.add(getParamQualType(m_astBuilder, param));
+ auto paramType = getParamQualType(funcType->getParamType(i));
+ paramTypes.add(paramType);
}
- break;
+ }
+ break;
- case OverloadCandidate::Flavor::Expr:
+ case OverloadCandidate::Flavor::Generic:
+ {
+ return TryCheckGenericOverloadCandidateTypes(context, candidate);
+ }
+ default: SLANG_UNEXPECTED("unknown flavor of overload candidate"); break;
+ }
+
+ Index paramIndex = 0;
+ Index argIndex = 0;
+ struct Arg
+ {
+ Expr* argExpr;
+ Type* type;
+ };
+ auto readArg = [&]() -> Arg
+ {
+ if (argIndex >= argCount)
+ return {nullptr, nullptr};
+ auto arg = context.getArg(argIndex);
+ Arg result = {arg, context.getArgType(argIndex)};
+ argIndex++;
+ return result;
+ };
+
+ auto coerceArgToParam = [&](Arg arg, QualType paramType) -> Arg
+ {
+ auto argType = QualType(arg.type, paramType.isLeftValue);
+ if (!paramType)
+ return {nullptr, nullptr};
+ if (!argType)
+ return {nullptr, nullptr};
+ if (context.mode == OverloadResolveContext::Mode::JustTrying)
+ {
+ ConversionCost cost = kConversionCost_None;
+ if (context.disallowNestedConversions)
{
- auto funcType = candidate.funcType;
- Count paramCount = funcType->getParamCount();
- for (Index i = 0; i < paramCount; ++i)
- {
- auto paramType = getParamQualType(funcType->getParamType(i));
- paramTypes.add(paramType);
- }
+ // We need an exact match in this case.
+ if (!paramType->equals(argType))
+ return {nullptr, nullptr};
}
- break;
-
- case OverloadCandidate::Flavor::Generic:
+ else if (!canCoerce(paramType, argType, arg.argExpr, &cost))
{
- return TryCheckGenericOverloadCandidateTypes(context, candidate);
+ return {nullptr, nullptr};
}
- default:
- SLANG_UNEXPECTED("unknown flavor of overload candidate");
- break;
+ candidate.conversionCostSum += cost;
}
-
- Index paramIndex = 0;
- Index argIndex = 0;
- struct Arg { Expr* argExpr; Type* type; };
- auto readArg = [&]() -> Arg
+ else
{
- if (argIndex >= argCount)
- return { nullptr, nullptr };
- auto arg = context.getArg(argIndex);
- Arg result = { arg, context.getArgType(argIndex) };
- argIndex++;
- return result;
- };
-
- auto coerceArgToParam = [&](Arg arg, QualType paramType) -> Arg
- {
- auto argType = QualType(arg.type, paramType.isLeftValue);
- if (!paramType)
- return { nullptr, nullptr };
- if (!argType)
- return { nullptr, nullptr };
- if (context.mode == OverloadResolveContext::Mode::JustTrying)
- {
- ConversionCost cost = kConversionCost_None;
- if (context.disallowNestedConversions)
- {
- // We need an exact match in this case.
- if (!paramType->equals(argType))
- return { nullptr, nullptr };
- }
- else if (!canCoerce(paramType, argType, arg.argExpr, &cost))
- {
- return { nullptr, nullptr };
- }
- candidate.conversionCostSum += cost;
- }
- else
- {
- arg.argExpr = coerce(CoercionSite::Argument, paramType, arg.argExpr);
- }
- return arg;
- };
- ShortList<Expr*> resultArgs;
+ arg.argExpr = coerce(CoercionSite::Argument, paramType, arg.argExpr);
+ }
+ return arg;
+ };
+ ShortList<Expr*> resultArgs;
- while (paramIndex < paramTypes.getCount())
+ while (paramIndex < paramTypes.getCount())
+ {
+ auto paramType = paramTypes[paramIndex];
+ if (auto paramTypePack = as<ConcreteTypePack>(paramType))
{
- auto paramType = paramTypes[paramIndex];
- if (auto paramTypePack = as<ConcreteTypePack>(paramType))
- {
- ShortList<Expr*> innerArgs;
- for (Index i = 0; i < paramTypePack->getTypeCount(); i++)
- {
- auto arg = readArg();
- auto coercedArg = coerceArgToParam(arg, QualType(paramTypePack->getElementType(i), paramType.isLeftValue));
- if (!coercedArg.type)
- {
- return false;
- }
- if (context.mode == OverloadResolveContext::Mode::ForReal)
- innerArgs.add(coercedArg.argExpr);
- }
- if (context.mode == OverloadResolveContext::Mode::ForReal)
- {
- auto packArg = m_astBuilder->create<PackExpr>();
- for (auto aa : innerArgs)
- packArg->args.add(aa);
- packArg->type = paramType;
- resultArgs.add(packArg);
- }
-
- // Always add a flat cost for using an argument pack,
- // so that we prefer non-pack overloads when possible.
- candidate.conversionCostSum += kConversionCost_ParameterPack;
- }
- else
+ ShortList<Expr*> innerArgs;
+ for (Index i = 0; i < paramTypePack->getTypeCount(); i++)
{
auto arg = readArg();
- if (!arg.type)
- {
- // If we run out of arguments, we can exit the loop now.
- // Note that in this type we don't need to worry about
- // default arguments, because we already checked that
- // the number of arguments was correct in `TryCheckOverloadCandidateArity`.
- break;
- }
- auto coercedArg = coerceArgToParam(arg, paramType);
+ auto coercedArg = coerceArgToParam(
+ arg,
+ QualType(paramTypePack->getElementType(i), paramType.isLeftValue));
if (!coercedArg.type)
{
return false;
}
if (context.mode == OverloadResolveContext::Mode::ForReal)
- resultArgs.add(coercedArg.argExpr);
+ innerArgs.add(coercedArg.argExpr);
}
- paramIndex++;
+ if (context.mode == OverloadResolveContext::Mode::ForReal)
+ {
+ auto packArg = m_astBuilder->create<PackExpr>();
+ for (auto aa : innerArgs)
+ packArg->args.add(aa);
+ packArg->type = paramType;
+ resultArgs.add(packArg);
+ }
+
+ // Always add a flat cost for using an argument pack,
+ // so that we prefer non-pack overloads when possible.
+ candidate.conversionCostSum += kConversionCost_ParameterPack;
}
- if (context.mode == OverloadResolveContext::Mode::ForReal)
+ else
{
- context.argCount = resultArgs.getCount();
- if (context.args)
+ auto arg = readArg();
+ if (!arg.type)
{
- context.args->setCount(context.argCount);
- for (Index i = 0; i < context.argCount; i++)
- (*context.args)[i] = resultArgs[i];
+ // If we run out of arguments, we can exit the loop now.
+ // Note that in this type we don't need to worry about
+ // default arguments, because we already checked that
+ // the number of arguments was correct in `TryCheckOverloadCandidateArity`.
+ break;
}
+ auto coercedArg = coerceArgToParam(arg, paramType);
+ if (!coercedArg.type)
+ {
+ return false;
+ }
+ if (context.mode == OverloadResolveContext::Mode::ForReal)
+ resultArgs.add(coercedArg.argExpr);
}
- return true;
+ paramIndex++;
}
-
- bool isEffectivelyMutating(CallableDecl* decl)
+ if (context.mode == OverloadResolveContext::Mode::ForReal)
{
- if(decl->hasModifier<MutatingAttribute>())
- return true;
- if (decl->hasModifier<RefAttribute>())
- return true;
- if(decl->hasModifier<NonmutatingAttribute>())
- return false;
-
- if(as<SetterDecl>(decl))
- return true;
+ context.argCount = resultArgs.getCount();
+ if (context.args)
+ {
+ context.args->setCount(context.argCount);
+ for (Index i = 0; i < context.argCount; i++)
+ (*context.args)[i] = resultArgs[i];
+ }
+ }
+ return true;
+}
+bool isEffectivelyMutating(CallableDecl* decl)
+{
+ if (decl->hasModifier<MutatingAttribute>())
+ return true;
+ if (decl->hasModifier<RefAttribute>())
+ return true;
+ if (decl->hasModifier<NonmutatingAttribute>())
return false;
- }
- ParamDecl* SemanticsVisitor::isReferenceIntoFunctionInputParameter(
- Expr* inExpr)
+ if (as<SetterDecl>(decl))
+ return true;
+
+ return false;
+}
+
+ParamDecl* SemanticsVisitor::isReferenceIntoFunctionInputParameter(Expr* inExpr)
+{
+ auto expr = inExpr;
+ for (;;)
{
- auto expr = inExpr;
- for (;;)
+ if (auto declRefExpr = as<DeclRefExpr>(expr))
{
- if (auto declRefExpr = as<DeclRefExpr>(expr))
+ auto declRef = declRefExpr->declRef;
+ if (auto paramDeclRef = declRef.as<ParamDecl>())
{
- auto declRef = declRefExpr->declRef;
- if(auto paramDeclRef = declRef.as<ParamDecl>())
+ if (paramDeclRef.as<ModernParamDecl>())
{
- if (paramDeclRef.as<ModernParamDecl>())
- {
- // functions declared in our "modern" style (using
- // the `func` keyword) never have mutable `in`
- // parameters.
- //
- return nullptr;
- }
-
- if (paramDeclRef.getDecl()->findModifier<OutModifier>() ||
- paramDeclRef.getDecl()->findModifier<RefModifier>())
- {
- // Function parameters marked with `out`, `inout`,
- // `in out` or `ref` are all mutable in a way where
- // the result of mutations will be visible to the
- // caller.
- //
- return nullptr;
- }
+ // functions declared in our "modern" style (using
+ // the `func` keyword) never have mutable `in`
+ // parameters.
+ //
+ return nullptr;
+ }
- // At this point we have an l-value decl-ref to a
- // function parameter that is (implicitly or
- // explicitly) declared `in`.
+ if (paramDeclRef.getDecl()->findModifier<OutModifier>() ||
+ paramDeclRef.getDecl()->findModifier<RefModifier>())
+ {
+ // Function parameters marked with `out`, `inout`,
+ // `in out` or `ref` are all mutable in a way where
+ // the result of mutations will be visible to the
+ // caller.
//
- return paramDeclRef.getDecl();
+ return nullptr;
}
- }
- else if (auto memberExpr = as<MemberExpr>(expr))
- {
- expr = memberExpr->baseExpression;
- continue;
- }
- else if (auto indexExpr = as<IndexExpr>(expr))
- {
- expr = indexExpr->baseExpression;
- continue;
- }
- return nullptr;
+ // At this point we have an l-value decl-ref to a
+ // function parameter that is (implicitly or
+ // explicitly) declared `in`.
+ //
+ return paramDeclRef.getDecl();
+ }
+ }
+ else if (auto memberExpr = as<MemberExpr>(expr))
+ {
+ expr = memberExpr->baseExpression;
+ continue;
}
+ else if (auto indexExpr = as<IndexExpr>(expr))
+ {
+ expr = indexExpr->baseExpression;
+ continue;
+ }
+
+ return nullptr;
}
+}
- bool SemanticsVisitor::TryCheckOverloadCandidateDirections(
- OverloadResolveContext& context,
- OverloadCandidate const& candidate)
- {
- if(candidate.flavor != OverloadCandidate::Flavor::Func)
- return true;
+bool SemanticsVisitor::TryCheckOverloadCandidateDirections(
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
+{
+ if (candidate.flavor != OverloadCandidate::Flavor::Func)
+ return true;
- auto funcDeclRef = candidate.item.declRef.as<CallableDecl>();
- SLANG_ASSERT(funcDeclRef);
+ auto funcDeclRef = candidate.item.declRef.as<CallableDecl>();
+ SLANG_ASSERT(funcDeclRef);
- // Note: This operation was originally introduced as
- // a place to add checking around l-value-ness of arguments
- // and parameters, but currently that checking is being
- // done in other places.
- //
- // For now we will only use this step to check the
- // mutability of the `this` parameter where necessary.
- //
- if(!isEffectivelyStatic(funcDeclRef.getDecl()))
+ // Note: This operation was originally introduced as
+ // a place to add checking around l-value-ness of arguments
+ // and parameters, but currently that checking is being
+ // done in other places.
+ //
+ // For now we will only use this step to check the
+ // mutability of the `this` parameter where necessary.
+ //
+ if (!isEffectivelyStatic(funcDeclRef.getDecl()))
+ {
+ if (isEffectivelyMutating(funcDeclRef.getDecl()))
{
- if(isEffectivelyMutating(funcDeclRef.getDecl()))
+ if (context.baseExpr && !context.baseExpr->type.isLeftValue)
{
- if(context.baseExpr && !context.baseExpr->type.isLeftValue)
+ if (context.mode == OverloadResolveContext::Mode::ForReal)
{
- if(context.mode == OverloadResolveContext::Mode::ForReal)
- {
- getSink()->diagnose(context.loc, Diagnostics::mutatingMethodOnImmutableValue, funcDeclRef.getName());
- maybeDiagnoseThisNotLValue(context.baseExpr);
- }
- return false;
+ getSink()->diagnose(
+ context.loc,
+ Diagnostics::mutatingMethodOnImmutableValue,
+ funcDeclRef.getName());
+ maybeDiagnoseThisNotLValue(context.baseExpr);
}
+ return false;
+ }
- // The parameters of functions declared using traditional/legacy
- // syntax are currently exposed as mutable locals within the body
- // of the relevant function. As such, it is legal to call `[mutating]`
- // methods on such a function parameter. However, doing so is typically
- // indicative of an error on the programmer's part.
- //
- // We will detect such cases here and issue a diagnostic that explains
- // the situation.
- //
- if(context.baseExpr && context.mode == OverloadResolveContext::Mode::ForReal)
+ // The parameters of functions declared using traditional/legacy
+ // syntax are currently exposed as mutable locals within the body
+ // of the relevant function. As such, it is legal to call `[mutating]`
+ // methods on such a function parameter. However, doing so is typically
+ // indicative of an error on the programmer's part.
+ //
+ // We will detect such cases here and issue a diagnostic that explains
+ // the situation.
+ //
+ if (context.baseExpr && context.mode == OverloadResolveContext::Mode::ForReal)
+ {
+ if (auto paramDecl = isReferenceIntoFunctionInputParameter(context.baseExpr))
{
- if(auto paramDecl = isReferenceIntoFunctionInputParameter(context.baseExpr))
- {
- const bool isNonCopyable = isNonCopyableType(paramDecl->getType());
+ const bool isNonCopyable = isNonCopyableType(paramDecl->getType());
- const auto& diagnotic = isNonCopyable ?
- Diagnostics::mutatingMethodOnFunctionInputParameterError :
- Diagnostics::mutatingMethodOnFunctionInputParameterWarning;
+ const auto& diagnotic =
+ isNonCopyable ? Diagnostics::mutatingMethodOnFunctionInputParameterError
+ : Diagnostics::mutatingMethodOnFunctionInputParameterWarning;
- getSink()->diagnose(context.loc, diagnotic,
- funcDeclRef.getName(),
- paramDecl->getName());
- }
+ getSink()->diagnose(
+ context.loc,
+ diagnotic,
+ funcDeclRef.getName(),
+ paramDecl->getName());
}
}
}
+ }
+
+ return true;
+}
+bool SemanticsVisitor::TryCheckOverloadCandidateConstraints(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ // We only need this step for generics, so always succeed on
+ // everything else.
+ if (candidate.flavor != OverloadCandidate::Flavor::Generic)
return true;
- }
- bool SemanticsVisitor::TryCheckOverloadCandidateConstraints(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
- {
- // We only need this step for generics, so always succeed on
- // everything else.
- if(candidate.flavor != OverloadCandidate::Flavor::Generic)
- return true;
+ // It is possible that the overload candidate was only partially
+ // applied (the number of arguments was not equal to the number
+ // of explicit parameters). In that case, we want to defer
+ // final checking of things like constraints until later, in
+ // case a subsequent pass of overload resolution (like applying
+ // an overloaded generic function to arguments) will give us
+ // the missing information to enable inference.
+ //
+ if (candidate.flags & OverloadCandidate::Flag::IsPartiallyAppliedGeneric)
+ return true;
- // It is possible that the overload candidate was only partially
- // applied (the number of arguments was not equal to the number
- // of explicit parameters). In that case, we want to defer
- // final checking of things like constraints until later, in
- // case a subsequent pass of overload resolution (like applying
- // an overloaded generic function to arguments) will give us
- // the missing information to enable inference.
- //
- if(candidate.flags & OverloadCandidate::Flag::IsPartiallyAppliedGeneric)
- return true;
+ auto genericDeclRef = candidate.item.declRef.as<GenericDecl>();
+ SLANG_ASSERT(genericDeclRef); // otherwise we wouldn't be a generic candidate...
- auto genericDeclRef = candidate.item.declRef.as<GenericDecl>();
- 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 substArgs = tryGetGenericArguments(candidate.subst, genericDeclRef.getDecl());
+ SLANG_ASSERT(substArgs.getCount());
- // We should have the existing arguments to the generic
- // handy, so that we can construct a substitution list.
- auto substArgs = tryGetGenericArguments(candidate.subst, genericDeclRef.getDecl());
- SLANG_ASSERT(substArgs.getCount());
+ List<Val*> newArgs;
+ for (auto arg : substArgs)
+ newArgs.add(arg);
- List<Val*> newArgs;
- for (auto arg : substArgs)
- newArgs.add(arg);
+ for (auto constraintDecl :
+ genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>())
+ {
+ DeclRef<GenericTypeConstraintDecl> constraintDeclRef =
+ m_astBuilder
+ ->getGenericAppDeclRef(genericDeclRef, newArgs.getArrayView(), constraintDecl)
+ .as<GenericTypeConstraintDecl>();
- for( auto constraintDecl : genericDeclRef.getDecl()->getMembersOfType<GenericTypeConstraintDecl>() )
- {
- DeclRef<GenericTypeConstraintDecl> constraintDeclRef = m_astBuilder->getGenericAppDeclRef(
- genericDeclRef, newArgs.getArrayView(), constraintDecl).as<GenericTypeConstraintDecl>();
-
- auto sub = getSub(m_astBuilder, constraintDeclRef);
- auto sup = getSup(m_astBuilder, constraintDeclRef);
+ auto sub = getSub(m_astBuilder, constraintDeclRef);
+ auto sup = getSup(m_astBuilder, constraintDeclRef);
- auto subTypeWitness = tryGetSubtypeWitness(sub, sup);
- if(subTypeWitness)
- {
- newArgs.add(subTypeWitness);
- }
- else
+ auto subTypeWitness = tryGetSubtypeWitness(sub, sup);
+ if (subTypeWitness)
+ {
+ newArgs.add(subTypeWitness);
+ }
+ else
+ {
+ if (context.mode != OverloadResolveContext::Mode::JustTrying)
{
- if(context.mode != OverloadResolveContext::Mode::JustTrying)
- {
- subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None);
- getSink()->diagnose(context.loc, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup);
- }
- return false;
+ subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None);
+ getSink()->diagnose(
+ context.loc,
+ Diagnostics::typeArgumentDoesNotConformToInterface,
+ sub,
+ sup);
}
+ return false;
}
-
- candidate.subst = SubstitutionSet(m_astBuilder->getGenericAppDeclRef(genericDeclRef, newArgs.getArrayView()));
-
- // Done checking all the constraints, hooray.
- return true;
}
- void SemanticsVisitor::TryCheckOverloadCandidate(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
- {
- if (!TryCheckOverloadCandidateArity(context, candidate))
- return;
+ candidate.subst =
+ SubstitutionSet(m_astBuilder->getGenericAppDeclRef(genericDeclRef, newArgs.getArrayView()));
- candidate.status = OverloadCandidate::Status::ArityChecked;
- if (!TryCheckOverloadCandidateFixity(context, candidate))
- return;
+ // Done checking all the constraints, hooray.
+ return true;
+}
- candidate.status = OverloadCandidate::Status::FixityChecked;
- if (!TryCheckOverloadCandidateTypes(context, candidate))
- return;
+void SemanticsVisitor::TryCheckOverloadCandidate(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ if (!TryCheckOverloadCandidateArity(context, candidate))
+ return;
- candidate.status = OverloadCandidate::Status::TypeChecked;
- if (!TryCheckOverloadCandidateDirections(context, candidate))
- return;
+ candidate.status = OverloadCandidate::Status::ArityChecked;
+ if (!TryCheckOverloadCandidateFixity(context, candidate))
+ return;
- candidate.status = OverloadCandidate::Status::DirectionChecked;
- if (!TryCheckOverloadCandidateConstraints(context, candidate))
- return;
+ candidate.status = OverloadCandidate::Status::FixityChecked;
+ if (!TryCheckOverloadCandidateTypes(context, candidate))
+ return;
- candidate.status = OverloadCandidate::Status::VisibilityChecked;
- if (!TryCheckOverloadCandidateVisibility(context, candidate))
- return;
+ candidate.status = OverloadCandidate::Status::TypeChecked;
+ if (!TryCheckOverloadCandidateDirections(context, candidate))
+ return;
- candidate.status = OverloadCandidate::Status::Applicable;
- }
+ candidate.status = OverloadCandidate::Status::DirectionChecked;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ return;
- Expr* SemanticsVisitor::createGenericDeclRef(
- Expr* baseExpr,
- Expr* originalExpr,
- SubstitutionSet substArgs)
- {
- auto baseDeclRefExpr = as<DeclRefExpr>(baseExpr);
- if (!baseDeclRefExpr)
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration");
- return CreateErrorExpr(originalExpr);
- }
- auto baseGenericRef = baseDeclRefExpr->declRef.as<GenericDecl>();
- if (!baseGenericRef)
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), baseExpr, "expected a reference to a generic declaration");
- return CreateErrorExpr(originalExpr);
- }
- auto genSubst = substArgs.findGenericAppDeclRef(baseGenericRef.getDecl());
- SLANG_ASSERT(genSubst);
- DeclRef<Decl> innerDeclRef = m_astBuilder->getGenericAppDeclRef(baseGenericRef, genSubst->getArgs());
+ candidate.status = OverloadCandidate::Status::VisibilityChecked;
+ if (!TryCheckOverloadCandidateVisibility(context, candidate))
+ return;
- Expr* base = nullptr;
- if (auto mbrExpr = as<MemberExpr>(baseExpr))
- base = mbrExpr->baseExpression;
+ candidate.status = OverloadCandidate::Status::Applicable;
+}
- return ConstructDeclRefExpr(
- innerDeclRef,
- base,
- innerDeclRef.getName(),
- originalExpr->loc,
- originalExpr);
+Expr* SemanticsVisitor::createGenericDeclRef(
+ Expr* baseExpr,
+ Expr* originalExpr,
+ SubstitutionSet substArgs)
+{
+ auto baseDeclRefExpr = as<DeclRefExpr>(baseExpr);
+ if (!baseDeclRefExpr)
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(
+ getSink(),
+ baseExpr,
+ "expected a reference to a generic declaration");
+ return CreateErrorExpr(originalExpr);
}
+ auto baseGenericRef = baseDeclRefExpr->declRef.as<GenericDecl>();
+ if (!baseGenericRef)
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(
+ getSink(),
+ baseExpr,
+ "expected a reference to a generic declaration");
+ return CreateErrorExpr(originalExpr);
+ }
+ auto genSubst = substArgs.findGenericAppDeclRef(baseGenericRef.getDecl());
+ SLANG_ASSERT(genSubst);
+ DeclRef<Decl> innerDeclRef =
+ m_astBuilder->getGenericAppDeclRef(baseGenericRef, genSubst->getArgs());
+
+ Expr* base = nullptr;
+ if (auto mbrExpr = as<MemberExpr>(baseExpr))
+ base = mbrExpr->baseExpression;
+
+ return ConstructDeclRefExpr(
+ innerDeclRef,
+ base,
+ innerDeclRef.getName(),
+ originalExpr->loc,
+ originalExpr);
+}
- Expr* SemanticsVisitor::CompleteOverloadCandidate(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
+Expr* SemanticsVisitor::CompleteOverloadCandidate(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ // special case for generic argument inference failure
+ if (candidate.status == OverloadCandidate::Status::GenericArgumentInferenceFailed)
{
- // 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 callString = getCallSignatureString(context);
+ getSink()->diagnose(context.loc, Diagnostics::genericArgumentInferenceFailed, callString);
- String declString = ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder);
- getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString);
- goto error;
- }
+ String declString = ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder);
+ getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString);
+ goto error;
+ }
- context.mode = OverloadResolveContext::Mode::ForReal;
+ context.mode = OverloadResolveContext::Mode::ForReal;
- if (!TryCheckOverloadCandidateClassNewMatchUp(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateClassNewMatchUp(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateArity(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateArity(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateFixity(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateFixity(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateTypes(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateTypes(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateDirections(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateDirections(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateConstraints(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateConstraints(context, candidate))
+ goto error;
- if (!TryCheckOverloadCandidateVisibility(context, candidate))
- goto error;
+ if (!TryCheckOverloadCandidateVisibility(context, candidate))
+ goto error;
+ {
+ Expr* baseExpr;
+ switch (candidate.flavor)
{
- Expr* baseExpr;
- switch(candidate.flavor)
- {
- case OverloadCandidate::Flavor::Func:
- case OverloadCandidate::Flavor::Generic:
- baseExpr = ConstructLookupResultExpr(
- candidate.item,
- context.baseExpr,
- candidate.item.declRef.getName(),
- context.funcLoc,
- context.originalExpr);
- break;
- case OverloadCandidate::Flavor::Expr:
- default:
- baseExpr = nullptr;
- break;
- }
+ case OverloadCandidate::Flavor::Func:
+ case OverloadCandidate::Flavor::Generic:
+ baseExpr = ConstructLookupResultExpr(
+ candidate.item,
+ context.baseExpr,
+ candidate.item.declRef.getName(),
+ context.funcLoc,
+ context.originalExpr);
+ break;
+ case OverloadCandidate::Flavor::Expr:
+ default: baseExpr = nullptr; break;
+ }
- switch(candidate.flavor)
+ switch (candidate.flavor)
+ {
+ case OverloadCandidate::Flavor::Func:
{
- case OverloadCandidate::Flavor::Func:
+ AppExprBase* callExpr = as<InvokeExpr>(context.originalExpr);
+ if (!callExpr)
{
- AppExprBase* callExpr = as<InvokeExpr>(context.originalExpr);
- if(!callExpr)
- {
- callExpr = m_astBuilder->create<InvokeExpr>();
- callExpr->loc = context.loc;
- for(Index aa = 0; aa < context.argCount; ++aa)
- callExpr->arguments.add(context.getArg(aa));
- }
+ callExpr = m_astBuilder->create<InvokeExpr>();
+ callExpr->loc = context.loc;
+ for (Index aa = 0; aa < context.argCount; ++aa)
+ callExpr->arguments.add(context.getArg(aa));
+ }
- callExpr->originalFunctionExpr = callExpr->functionExpr;
- callExpr->functionExpr = baseExpr;
- callExpr->type = QualType(candidate.resultType);
+ callExpr->originalFunctionExpr = callExpr->functionExpr;
+ 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<SubscriptDecl>())
+ // 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<SubscriptDecl>())
+ {
+ const auto& decl = subscriptDeclRef.getDecl();
+ for (auto member : decl->members)
{
- const auto& decl = subscriptDeclRef.getDecl();
- for (auto member : decl->members)
+ if (as<SetterDecl>(member) || as<RefAccessorDecl>(member))
{
- if (as<SetterDecl>(member) || as<RefAccessorDecl>(member))
+ // If the subscript decl has a setter,
+ // then the call is an l-value if base is l-value.
+ if (auto base = GetBaseExpr(baseExpr))
{
- // If the subscript decl has a setter,
- // then the call is an l-value if base is l-value.
- if (auto base = GetBaseExpr(baseExpr))
- {
- if (base->type.isLeftValue)
- {
- callExpr->type.isLeftValue = true;
- break;
- }
- }
- // Otherwise, if the accessor is [nonmutating], we can
- // also consider the result of the subscript call as l-value
- // regardless of the base.
- if (member->findModifier<NonmutatingAttribute>())
+ if (base->type.isLeftValue)
{
callExpr->type.isLeftValue = true;
break;
}
}
+ // Otherwise, if the accessor is [nonmutating], we can
+ // also consider the result of the subscript call as l-value
+ // regardless of the base.
+ if (member->findModifier<NonmutatingAttribute>())
+ {
+ callExpr->type.isLeftValue = true;
+ break;
+ }
}
}
-
- // TODO: there may be other cases that confer l-value-ness
-
- return callExpr;
}
- break;
+ // TODO: there may be other cases that confer l-value-ness
- case OverloadCandidate::Flavor::Expr:
- {
- AppExprBase* callExpr = as<InvokeExpr>(context.originalExpr);
- if (!callExpr)
- {
- callExpr = m_astBuilder->create<InvokeExpr>();
- callExpr->loc = context.loc;
- for (Index aa = 0; aa < context.argCount; ++aa)
- callExpr->arguments.add(context.getArg(aa));
- }
-
- callExpr->originalFunctionExpr = callExpr->functionExpr;
- callExpr->type = QualType(candidate.resultType);
- callExpr->functionExpr = candidate.exprVal;
- return callExpr;
+ return callExpr;
+ }
- }
- break;
+ break;
- case OverloadCandidate::Flavor::Generic:
- // We allow a generic to be applied to fewer arguments than its number
- // of parameters, and defer the process of inferring the remaining
- // arguments until later.
- //
- if(candidate.flags & OverloadCandidate::Flag::IsPartiallyAppliedGeneric)
+ case OverloadCandidate::Flavor::Expr:
+ {
+ AppExprBase* callExpr = as<InvokeExpr>(context.originalExpr);
+ if (!callExpr)
{
- auto expr = m_astBuilder->create<PartiallyAppliedGenericExpr>();
- expr->loc = context.loc;
- expr->originalExpr = baseExpr;
- expr->baseGenericDeclRef = as<DeclRefExpr>(baseExpr)->declRef.as<GenericDecl>();
- auto args = tryGetGenericArguments(candidate.subst, expr->baseGenericDeclRef.getDecl());
- for (auto arg : args)
- expr->knownGenericArgs.add(arg);
- return expr;
+ callExpr = m_astBuilder->create<InvokeExpr>();
+ callExpr->loc = context.loc;
+ for (Index aa = 0; aa < context.argCount; ++aa)
+ callExpr->arguments.add(context.getArg(aa));
}
- return createGenericDeclRef(
- baseExpr,
- context.originalExpr,
- candidate.subst);
- break;
-
- default:
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "unknown overload candidate flavor");
- break;
+ callExpr->originalFunctionExpr = callExpr->functionExpr;
+ callExpr->type = QualType(candidate.resultType);
+ callExpr->functionExpr = candidate.exprVal;
+ return callExpr;
}
- }
+ break;
+ case OverloadCandidate::Flavor::Generic:
+ // We allow a generic to be applied to fewer arguments than its number
+ // of parameters, and defer the process of inferring the remaining
+ // arguments until later.
+ //
+ if (candidate.flags & OverloadCandidate::Flag::IsPartiallyAppliedGeneric)
+ {
+ auto expr = m_astBuilder->create<PartiallyAppliedGenericExpr>();
+ expr->loc = context.loc;
+ expr->originalExpr = baseExpr;
+ expr->baseGenericDeclRef = as<DeclRefExpr>(baseExpr)->declRef.as<GenericDecl>();
+ auto args =
+ tryGetGenericArguments(candidate.subst, expr->baseGenericDeclRef.getDecl());
+ for (auto arg : args)
+ expr->knownGenericArgs.add(arg);
+ return expr;
+ }
- error:
+ return createGenericDeclRef(baseExpr, context.originalExpr, candidate.subst);
+ break;
- if(context.originalExpr)
- {
- return CreateErrorExpr(context.originalExpr);
- }
- else
- {
- return nullptr;
+ default:
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), context.loc, "unknown overload candidate flavor");
+ break;
}
}
- /// Does the given `declRef` represent an interface requirement?
- bool isInterfaceRequirement(ASTBuilder* builder, DeclRef<Decl> const& declRef)
- {
- SLANG_UNUSED(builder);
- if(!declRef)
- return false;
+error:
- auto parent = declRef.getParent();
- if(parent.as<GenericDecl>())
- parent = parent.getParent();
+ if (context.originalExpr)
+ {
+ return CreateErrorExpr(context.originalExpr);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
- if(parent.as<InterfaceDecl>())
- return true;
+/// Does the given `declRef` represent an interface requirement?
+bool isInterfaceRequirement(ASTBuilder* builder, DeclRef<Decl> const& declRef)
+{
+ SLANG_UNUSED(builder);
+ if (!declRef)
return false;
- }
- /// If `declRef` representations a specialization of a generic, returns the number of specialized generic arguments.
- /// Otherwise, returns zero.
- ///
- Int SemanticsVisitor::getSpecializedParamCount(DeclRef<Decl> const& declRef)
- {
- if(!declRef)
- return 0;
+ auto parent = declRef.getParent();
+ if (parent.as<GenericDecl>())
+ parent = parent.getParent();
- // A specialization of a generic must point at the
- // "inner" declaration of a generic. That means that
- // the parent of the decl ref must be a generic.
- //
- auto parentGeneric = declRef.getParent().as<GenericDecl>();
- if(!parentGeneric)
- return 0;
- //
- // Furthermore, the declaration we are considering
- // must be the single "inner" declaration of the
- // parent generic (and not somthing like a generic
- // parameter).
- //
- if( parentGeneric.getDecl()->inner != declRef.getDecl())
- return 0;
+ if (parent.as<InterfaceDecl>())
+ return true;
+
+ return false;
+}
+
+/// If `declRef` representations a specialization of a generic, returns the number of specialized
+/// generic arguments. Otherwise, returns zero.
+///
+Int SemanticsVisitor::getSpecializedParamCount(DeclRef<Decl> const& declRef)
+{
+ if (!declRef)
+ return 0;
- return CountParameters(parentGeneric).required;
+ // A specialization of a generic must point at the
+ // "inner" declaration of a generic. That means that
+ // the parent of the decl ref must be a generic.
+ //
+ auto parentGeneric = declRef.getParent().as<GenericDecl>();
+ if (!parentGeneric)
+ return 0;
+ //
+ // Furthermore, the declaration we are considering
+ // must be the single "inner" declaration of the
+ // parent generic (and not somthing like a generic
+ // parameter).
+ //
+ if (parentGeneric.getDecl()->inner != declRef.getDecl())
+ return 0;
+
+ return CountParameters(parentGeneric).required;
+}
+
+DeclRef<Decl> getParentDeclRef(DeclRef<Decl> declRef)
+{
+ auto parent = declRef.getParent();
+ while (parent.as<GenericDecl>())
+ {
+ parent = parent.getParent();
}
+ return parent;
+}
- DeclRef<Decl> getParentDeclRef(DeclRef<Decl> declRef)
+// Returns -1 if left is preferred, 1 if right is preferred, and 0 if they are equal.
+//
+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).
+ //
+ auto leftDeclRefParent = getParentDeclRef(left.declRef);
+ auto rightDeclRefParent = getParentDeclRef(right.declRef);
+ bool leftIsInterfaceRequirement = isInterfaceRequirement(left.declRef.getDecl());
+ bool rightIsInterfaceRequirement = isInterfaceRequirement(right.declRef.getDecl());
+ if (leftIsInterfaceRequirement != rightIsInterfaceRequirement)
+ return int(leftIsInterfaceRequirement) - int(rightIsInterfaceRequirement);
+
+ // Prefer non-extension declarations over extension declarations.
+ bool leftIsExtension = as<ExtensionDecl>(leftDeclRefParent.getDecl()) != nullptr;
+ bool rightIsExtension = as<ExtensionDecl>(rightDeclRefParent.getDecl()) != nullptr;
+ if (leftIsExtension != rightIsExtension)
+ {
+ return int(leftIsExtension) - int(rightIsExtension);
+ }
+ else if (leftIsExtension)
{
- auto parent = declRef.getParent();
- while (parent.as<GenericDecl>())
+ // If both are declared in extensions, prefer the one that is least generic.
+ bool leftIsGeneric = leftDeclRefParent.getParent().as<GenericDecl>() != nullptr;
+ bool rightIsGeneric = rightDeclRefParent.getParent().as<GenericDecl>() != nullptr;
+ if (leftIsGeneric != rightIsGeneric)
{
- parent = parent.getParent();
+ return int(leftIsGeneric) - int(rightIsGeneric);
}
- return parent;
}
- // Returns -1 if left is preferred, 1 if right is preferred, and 0 if they are equal.
- //
- 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).
- //
- auto leftDeclRefParent = getParentDeclRef(left.declRef);
- auto rightDeclRefParent = getParentDeclRef(right.declRef);
- bool leftIsInterfaceRequirement = isInterfaceRequirement(left.declRef.getDecl());
- bool rightIsInterfaceRequirement = isInterfaceRequirement(right.declRef.getDecl());
- if(leftIsInterfaceRequirement != rightIsInterfaceRequirement)
- return int(leftIsInterfaceRequirement) - int(rightIsInterfaceRequirement);
-
- // Prefer non-extension declarations over extension declarations.
- bool leftIsExtension = as<ExtensionDecl>(leftDeclRefParent.getDecl()) != nullptr;
- bool rightIsExtension = as<ExtensionDecl>(rightDeclRefParent.getDecl()) != nullptr;
- if (leftIsExtension != rightIsExtension)
- {
- return int(leftIsExtension) - int(rightIsExtension);
- }
- else if (leftIsExtension)
- {
- // If both are declared in extensions, prefer the one that is least generic.
- bool leftIsGeneric = leftDeclRefParent.getParent().as<GenericDecl>() != nullptr;
- bool rightIsGeneric = rightDeclRefParent.getParent().as<GenericDecl>() != nullptr;
- if (leftIsGeneric != rightIsGeneric)
- {
- return int(leftIsGeneric) - int(rightIsGeneric);
- }
- }
+ // Any decl is strictly better than a module decl.
+ bool leftIsModule = (as<ModuleDeclarationDecl>(left.declRef) != nullptr);
+ bool rightIsModule = (as<ModuleDeclarationDecl>(right.declRef) != nullptr);
+ if (leftIsModule != rightIsModule)
+ return int(rightIsModule) - int(leftIsModule);
- // Any decl is strictly better than a module decl.
- bool leftIsModule = (as<ModuleDeclarationDecl>(left.declRef) != nullptr);
- bool rightIsModule = (as<ModuleDeclarationDecl>(right.declRef) != nullptr);
- if(leftIsModule != rightIsModule)
- return int(rightIsModule) - int(leftIsModule);
+ // If both are interface requirements, prefer the more derived interface.
+ if (leftIsInterfaceRequirement && rightIsInterfaceRequirement)
+ {
+ auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent);
+ auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent);
- // If both are interface requirements, prefer the more derived interface.
- if (leftIsInterfaceRequirement && rightIsInterfaceRequirement)
+ if (!leftType->equals(rightType))
{
- auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent);
- auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent);
-
- if (!leftType->equals(rightType))
- {
- if (isSubtype(leftType, rightType, IsSubTypeOptions::None))
- return -1;
- if (isSubtype(rightType, leftType, IsSubTypeOptions::None))
- return 1;
- }
+ if (isSubtype(leftType, rightType, IsSubTypeOptions::None))
+ return -1;
+ if (isSubtype(rightType, leftType, IsSubTypeOptions::None))
+ return 1;
}
+ }
- // If both parents are the same we have ambiguity
- if(left.declRef.getParent() == right.declRef.getParent())
- return 0;
-
- auto leftAggType = leftDeclRefParent.as<AggTypeDeclBase>();
- auto rightAggType = rightDeclRefParent.as<AggTypeDeclBase>();
- if (leftAggType && rightAggType)
- {
- auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent);
- auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent);
+ // If both parents are the same we have ambiguity
+ if (left.declRef.getParent() == right.declRef.getParent())
+ return 0;
- auto inheritanceInfo = getShared()->getInheritanceInfo(rightType);
- for (auto facet : inheritanceInfo.facets)
- if (facet.getImpl()->getDeclRef().equals(leftDeclRefParent))
- return 1;
- inheritanceInfo = getShared()->getInheritanceInfo(leftType);
- for (auto facet : inheritanceInfo.facets)
- if (facet.getImpl()->getDeclRef().equals(rightDeclRefParent))
- return -1;
- }
+ auto leftAggType = leftDeclRefParent.as<AggTypeDeclBase>();
+ auto rightAggType = rightDeclRefParent.as<AggTypeDeclBase>();
+ if (leftAggType && rightAggType)
+ {
+ auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent);
+ auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent);
+
+ auto inheritanceInfo = getShared()->getInheritanceInfo(rightType);
+ for (auto facet : inheritanceInfo.facets)
+ if (facet.getImpl()->getDeclRef().equals(leftDeclRefParent))
+ return 1;
+ inheritanceInfo = getShared()->getInheritanceInfo(leftType);
+ for (auto facet : inheritanceInfo.facets)
+ if (facet.getImpl()->getDeclRef().equals(rightDeclRefParent))
+ return -1;
+ }
- // If both are subscript decls, prefer the one that provides more
- // accessors.
- if (auto leftSubscriptDecl = left.declRef.as<SubscriptDecl>())
+ // If both are subscript decls, prefer the one that provides more
+ // accessors.
+ if (auto leftSubscriptDecl = left.declRef.as<SubscriptDecl>())
+ {
+ if (auto rightSubscriptDecl = right.declRef.as<SubscriptDecl>())
{
- if (auto rightSubscriptDecl = right.declRef.as<SubscriptDecl>())
+ auto leftAccessorCount =
+ leftSubscriptDecl.getDecl()->getMembersOfType<AccessorDecl>().getCount();
+ auto rightAccessorCount =
+ rightSubscriptDecl.getDecl()->getMembersOfType<AccessorDecl>().getCount();
+ auto decl1IsSubsetOfDecl2 = [=](SubscriptDecl* decl1, SubscriptDecl* decl2)
{
- auto leftAccessorCount = leftSubscriptDecl.getDecl()->getMembersOfType<AccessorDecl>().getCount();
- auto rightAccessorCount = rightSubscriptDecl.getDecl()->getMembersOfType<AccessorDecl>().getCount();
- auto decl1IsSubsetOfDecl2 = [=](SubscriptDecl* decl1, SubscriptDecl* decl2)
+ for (auto accessorDecl1 : decl1->getMembersOfType<AccessorDecl>())
+ {
+ bool found = false;
+ for (auto accessorDecl2 : decl2->getMembersOfType<AccessorDecl>())
{
- for (auto accessorDecl1 : decl1->getMembersOfType<AccessorDecl>())
+ if (accessorDecl1->astNodeType == accessorDecl2->astNodeType)
{
- bool found = false;
- for (auto accessorDecl2 : decl2->getMembersOfType<AccessorDecl>())
- {
- if (accessorDecl1->astNodeType == accessorDecl2->astNodeType)
- {
- found = true;
- break;
- }
- }
- if (!found)
- return false;
+ found = true;
+ break;
}
- return true;
- };
- if (leftAccessorCount > rightAccessorCount
- && decl1IsSubsetOfDecl2(rightSubscriptDecl.getDecl(), leftSubscriptDecl.getDecl()))
- {
- return -1;
- }
- else if (rightAccessorCount > leftAccessorCount
- && decl1IsSubsetOfDecl2(leftSubscriptDecl.getDecl(), rightSubscriptDecl.getDecl()))
- {
- return 1;
+ }
+ if (!found)
+ return false;
}
+ return true;
+ };
+ if (leftAccessorCount > rightAccessorCount &&
+ decl1IsSubsetOfDecl2(rightSubscriptDecl.getDecl(), leftSubscriptDecl.getDecl()))
+ {
+ return -1;
+ }
+ else if (
+ rightAccessorCount > leftAccessorCount &&
+ decl1IsSubsetOfDecl2(leftSubscriptDecl.getDecl(), rightSubscriptDecl.getDecl()))
+ {
+ return 1;
}
}
+ }
- // TODO: We should generalize above 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: We should generalize above 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.
+ // 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;
- }
+ return 0;
+}
- int SemanticsVisitor::compareOverloadCandidateSpecificity(
- LookupResultItem const& left,
- LookupResultItem const& right)
- {
- // HACK: if both items refer to the same declaration,
- // then arbitrarily pick one.
- if(left.declRef.equals(right.declRef))
- return -1;
+int SemanticsVisitor::compareOverloadCandidateSpecificity(
+ LookupResultItem const& left,
+ LookupResultItem const& right)
+{
+ // HACK: if both items refer to the same declaration,
+ // then arbitrarily pick one.
+ if (left.declRef.equals(right.declRef))
+ return -1;
- // There is a very general rule that we would like to enforce
- // in principle:
- //
- // Given candidates A and B, if A being applicable to some
- // arguments implies that B is also applicable, but not vice versa,
- // then A is a more specific/specialized candidate than B.
- //
- // A number of conclusions follow from this general rule.
- // For example, a non-generic declaration will always be
- // more specific than a generic declaration that was specialized
- // to matching types:
- //
- // int doThing(int a);
- // T doThing<T>(T a);
- //
- // It is clear that if the non-generic `doThing` is applicable
- // to an argument `x`, then `doThing<int>` is also applicable to
- // `x`. However, knowing that the generic `doThing` was applicable
- // to some `y` doesn't tell us that the non-generic `doThing` can
- // be called on `y`, because `y` could have some type that can't
- // convert to `int`.
- //
- // Similarly, a generic declaration with a subset of the parameters
- // of another generic is always more specialized:
- //
- // int doThing<T>(vector<T,3> value);
- // int doThing<T, let N : int>(vector<T,N> value);
- //
- // Here we know that both overloads can apply to `float3`, but only
- // one can apply to `float4`, so the first overload is more
- // specialized/specific.
- //
- // As a final example, a generic which places more constraints
- // on its generic parameters is more specific, all other things
- // being equal:
- //
- // int doThing<T : IFoo>( T value );
- // int doThing<T>(T value);
- //
- // In this case we know that the first overload is applicable
- // to a strict subset of the types that the second overload can
- // apply to.
- //
- // The above rules represent the idealized principles we want
- // to implement, but actually implementing that full check here
- // could make overload resolution far more expensive.
- //
- // For now we are going to do something far simpler and hackier,
- // which is to say that a candidate with more generic parameters
- // is always preferred over one with fewer.
- //
- // TODO: We could extend this definition to account for constraints
- // on generic parameters in the count, which would handle the
- // need to prefer a more-constrained generic when possible.
- //
- // TODO: In the long run we should clearly replace this with
- // the more general "does A being applicable imply B being applicable"
- // test.
- //
- // TODO: The principle stated here doesn't take the actual
- // arguments or their types into account, and it might be that
- // in some cases disambiguation of which declaration should be
- // preferred will depend on knowing the actual arguments.
- //
- auto leftSpecCount = getSpecializedParamCount(left.declRef);
- auto rightSpecCount = getSpecializedParamCount(right.declRef);
- if(leftSpecCount != rightSpecCount)
- return int(leftSpecCount - rightSpecCount);
+ // There is a very general rule that we would like to enforce
+ // in principle:
+ //
+ // Given candidates A and B, if A being applicable to some
+ // arguments implies that B is also applicable, but not vice versa,
+ // then A is a more specific/specialized candidate than B.
+ //
+ // A number of conclusions follow from this general rule.
+ // For example, a non-generic declaration will always be
+ // more specific than a generic declaration that was specialized
+ // to matching types:
+ //
+ // int doThing(int a);
+ // T doThing<T>(T a);
+ //
+ // It is clear that if the non-generic `doThing` is applicable
+ // to an argument `x`, then `doThing<int>` is also applicable to
+ // `x`. However, knowing that the generic `doThing` was applicable
+ // to some `y` doesn't tell us that the non-generic `doThing` can
+ // be called on `y`, because `y` could have some type that can't
+ // convert to `int`.
+ //
+ // Similarly, a generic declaration with a subset of the parameters
+ // of another generic is always more specialized:
+ //
+ // int doThing<T>(vector<T,3> value);
+ // int doThing<T, let N : int>(vector<T,N> value);
+ //
+ // Here we know that both overloads can apply to `float3`, but only
+ // one can apply to `float4`, so the first overload is more
+ // specialized/specific.
+ //
+ // As a final example, a generic which places more constraints
+ // on its generic parameters is more specific, all other things
+ // being equal:
+ //
+ // int doThing<T : IFoo>( T value );
+ // int doThing<T>(T value);
+ //
+ // In this case we know that the first overload is applicable
+ // to a strict subset of the types that the second overload can
+ // apply to.
+ //
+ // The above rules represent the idealized principles we want
+ // to implement, but actually implementing that full check here
+ // could make overload resolution far more expensive.
+ //
+ // For now we are going to do something far simpler and hackier,
+ // which is to say that a candidate with more generic parameters
+ // is always preferred over one with fewer.
+ //
+ // TODO: We could extend this definition to account for constraints
+ // on generic parameters in the count, which would handle the
+ // need to prefer a more-constrained generic when possible.
+ //
+ // TODO: In the long run we should clearly replace this with
+ // the more general "does A being applicable imply B being applicable"
+ // test.
+ //
+ // TODO: The principle stated here doesn't take the actual
+ // arguments or their types into account, and it might be that
+ // in some cases disambiguation of which declaration should be
+ // preferred will depend on knowing the actual arguments.
+ //
+ auto leftSpecCount = getSpecializedParamCount(left.declRef);
+ auto rightSpecCount = getSpecializedParamCount(right.declRef);
+ if (leftSpecCount != rightSpecCount)
+ return int(leftSpecCount - rightSpecCount);
+
+ return 0;
+}
+int getOverloadRank(DeclRef<Decl> declRef)
+{
+ if (!declRef.getDecl())
return 0;
- }
+ if (auto attr = declRef.getDecl()->findModifier<OverloadRankAttribute>())
+ return attr->rank;
+ return 0;
+}
- int getOverloadRank(DeclRef<Decl> declRef)
+int getExportRank(DeclRef<Decl> left, DeclRef<Decl> right)
+{
+ if (left.getDecl() && left.getDecl()->hasModifier<ExternModifier>())
{
- if (!declRef.getDecl())
- return 0;
- if (auto attr = declRef.getDecl()->findModifier<OverloadRankAttribute>())
- return attr->rank;
- return 0;
+ return (right.getDecl() && right.getDecl()->hasModifier<HLSLExportModifier>()) ? -1 : 0;
}
+ return 0;
+}
- int getExportRank(DeclRef<Decl> left, DeclRef<Decl> right)
- {
- if (left.getDecl() && left.getDecl()->hasModifier<ExternModifier>())
- {
- return (right.getDecl() && right.getDecl()->hasModifier<HLSLExportModifier>()) ? -1 : 0;
- }
+int getScopeRank(
+ DeclRef<Decl> const& left,
+ DeclRef<Decl> const& right,
+ Slang::Scope* referenceSiteScope)
+{
+ if (!referenceSiteScope)
return 0;
+
+ DeclRef<Decl> prefixDecl = referenceSiteScope->containerDecl;
+
+ // Hold the path from reference site to the root
+ // key: Decl node, value: distance from reference site
+ Dictionary<Decl*, uint32_t> refPath;
+ for (auto node = prefixDecl; node != nullptr; node = node.getParent())
+ {
+ Decl* key = node.getDecl();
+ uint32_t value = (uint32_t)refPath.getCount();
+ refPath.add(key, value);
}
- int getScopeRank(DeclRef<Decl> const& left,
- DeclRef<Decl> const& right, Slang::Scope* referenceSiteScope)
+ // find the common prefix decl of reference site and left
+ int leftDistance = 0;
+ int rightDistance = 0;
+ auto distanceToCommonPrefix = [](DeclRef<Decl> const& candidate,
+ Dictionary<Decl*, uint32_t> refPath) -> int
{
- if (!referenceSiteScope)
- return 0;
+ uint32_t distanceToReferenceSite = 0;
+ uint32_t distanceToCandidate = 0;
- DeclRef<Decl> prefixDecl = referenceSiteScope->containerDecl;
+ // Sanity check
+ if (candidate.getDecl() == nullptr)
+ return -1;
- // Hold the path from reference site to the root
- // key: Decl node, value: distance from reference site
- Dictionary<Decl*, uint32_t> refPath;
- for (auto node = prefixDecl; node != nullptr; node = node.getParent())
+ // search from candidate to root, once we found the first node in the reference path, that
+ // is the first common prefix, and we can stop searching.
+ for (auto node = candidate; node != nullptr; node = node.getParent())
{
Decl* key = node.getDecl();
- uint32_t value = (uint32_t)refPath.getCount();
- refPath.add(key, value);
- }
-
- // find the common prefix decl of reference site and left
- int leftDistance = 0;
- int rightDistance = 0;
- auto distanceToCommonPrefix = [](DeclRef<Decl> const& candidate, Dictionary<Decl*, uint32_t> refPath) -> int
- {
- uint32_t distanceToReferenceSite = 0;
- uint32_t distanceToCandidate = 0;
-
- // Sanity check
- if (candidate.getDecl() == nullptr)
- return -1;
-
- // search from candidate to root, once we found the first node in the reference path, that is the first
- // common prefix, and we can stop searching.
- for (auto node = candidate; node != nullptr; node = node.getParent())
+ if (refPath.tryGetValue(key, distanceToReferenceSite))
{
- Decl* key = node.getDecl();
- if (refPath.tryGetValue(key, distanceToReferenceSite))
- {
- break;
- }
- distanceToCandidate++;
+ break;
}
+ distanceToCandidate++;
+ }
- // If we don't find the common prefix, there must be something wrong, return the max value.
- if (distanceToReferenceSite == 0)
- return -1;
+ // If we don't find the common prefix, there must be something wrong, return the max value.
+ if (distanceToReferenceSite == 0)
+ return -1;
- return distanceToReferenceSite + distanceToCandidate;
- };
+ return distanceToReferenceSite + distanceToCandidate;
+ };
- leftDistance = distanceToCommonPrefix(left, refPath);
- rightDistance = distanceToCommonPrefix(right, refPath);
+ leftDistance = distanceToCommonPrefix(left, refPath);
+ rightDistance = distanceToCommonPrefix(right, refPath);
- if (leftDistance == rightDistance)
- return 0;
+ if (leftDistance == rightDistance)
+ return 0;
- if (leftDistance == -1)
- return 1;
+ if (leftDistance == -1)
+ return 1;
- if (rightDistance == -1)
- return -1;
+ if (rightDistance == -1)
+ return -1;
- return leftDistance < rightDistance ? -1 : 1;
- }
+ return leftDistance < rightDistance ? -1 : 1;
+}
- int SemanticsVisitor::CompareOverloadCandidates(
- OverloadCandidate* left,
- OverloadCandidate* right)
+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 one candidate got further along in validation, pick it
- if (left->status != right->status)
- return int(right->status) - int(left->status);
+ // If one candidate incurred less cost related to
+ // implicit conversion of arguments to matching
+ // parameter types, then we should prefer that
+ // candidate.
+ //
+ // TODO: This eventually should be refined into
+ // a test that checks conversion cost per-argument,
+ // and only considers a candidate "better" if it
+ // has lower cost for at least one argument, and
+ // does not have higher cost for any.
+ //
+ if (left->conversionCostSum != right->conversionCostSum)
+ return left->conversionCostSum - right->conversionCostSum;
- // If both candidates are applicable, then we need to compare
- // the costs of their type conversion sequences
- if(left->status == OverloadCandidate::Status::Applicable)
+ // If both candidates appear to be equally good when it
+ // comes to the per-argument conversions required,
+ // then we have two other categories of criteria we
+ // can look at to disambiguate things:
+ //
+ // 1. We can look at how the lookup process found `left` and `right`
+ // do decide which is a better match based purely on how "far away"
+ // they are for lookup purposes. A canonincal example here would
+ // be if one declaration shadows or overrides the other.
+ //
+ // 2. We can look at parameter lists of `left` and `right`, their types, etc.
+ // do decide which is a better match based purely on structure.
+ // Canonical examples in this case would be preferring a non-generic
+ // candidate over a generic one, preferring a non-variadic candidate
+ // over a variadic one, and preferring a candidate with fewer
+ // default parameters over one with more.
+ //
+ // Deciding how to order/interleave these two categories of criteria
+ // is an important design decision.
+ //
+ // For example, consider:
+ //
+ // float f(float x);
+ //
+ // struct S
+ // {
+ // int f<T>(T x);
+ //
+ // float g(float y) { return f(y); }
+ // }
+ //
+ // In terms of structural/type matching, the global `f` is a more specialized
+ // candidate at the call site, while in terms of lookup/lexical crieteria
+ // the `S.f` declaration is better.
+ //
+ // For now we are considering lookup/overriding concerns first (so
+ // we would bias in favor of selecting `S.f` in the above example), and then
+ // structural/type concerns, but a more nuanced approach may be
+ // required in the future to better match programmer intuition.
+ //
+ auto itemDiff = CompareLookupResultItems(left->item, right->item);
+ if (itemDiff)
+ return itemDiff;
+
+ auto specificityDiff = compareOverloadCandidateSpecificity(left->item, right->item);
+ if (specificityDiff)
+ return specificityDiff;
+
+ // `export` function is more flavored than `extern` function. But other modifiers are not
+ // considered.
+ auto externExportDiff = getExportRank(left->item.declRef, right->item.declRef);
+ if (externExportDiff)
+ return externExportDiff;
+
+ // We need to consider the distance of the declarations to the global scope to resolve this
+ // case:
+ // float f(float x);
+ // struct S
+ // {
+ // float f(float x);
+ // float g(float y) { return f(y); } // will call S::f() instead of ::f()
+ // }
+ // we will count the distance from the reference site to the declaration in the scope tree.
+
+ // NOTE: We CAN'T do this for the generic function, because generic lookup is little bit
+ // complicated. It will go through multiple passes of candidates compare. In the first
+ // pass, it will lookup all the generic candidates that matches the generic parameter only,
+ // e.g., the following generic functions are totally different, but they will be selected
+ // as candidates because the function name and the generic parameters are the same: void
+ // func<let Z0 : uint, let Z1 : uint>(Z0 a, Z1 b); void func<let Z0 : uint, let Z1 :
+ // uint>(Z0 a, Z1 b, Z0 c); void func<let Z0 : uint, let Z1 : uint>(Z0 a, Z1 b, Z0 c, Z1
+ // d);
+ //
+ // So in this case, we should not consider the scope rank and overload rank at all, because
+ // there is only one of above candidates is valid, and the rank calculation doesn't
+ // consider the correctness of the candidates, so it could select the wrong candidate.
+ //
+ // In the next pass, the lookup system will match the input parameters in those candidates
+ // to find out the valid match, the "flavor" field will become "Func" or "Expr". So the
+ // rank calculation can be applied.
+ if (left->flavor == OverloadCandidate::Flavor::Generic ||
+ left->flavor == OverloadCandidate::Flavor::UnspecializedGeneric ||
+ right->flavor == OverloadCandidate::Flavor::Generic ||
+ right->flavor == OverloadCandidate::Flavor::UnspecializedGeneric)
{
- // If one candidate incurred less cost related to
- // implicit conversion of arguments to matching
- // parameter types, then we should prefer that
- // candidate.
- //
- // TODO: This eventually should be refined into
- // a test that checks conversion cost per-argument,
- // and only considers a candidate "better" if it
- // has lower cost for at least one argument, and
- // does not have higher cost for any.
- //
- if (left->conversionCostSum != right->conversionCostSum)
- return left->conversionCostSum - right->conversionCostSum;
-
- // If both candidates appear to be equally good when it
- // comes to the per-argument conversions required,
- // then we have two other categories of criteria we
- // can look at to disambiguate things:
- //
- // 1. We can look at how the lookup process found `left` and `right`
- // do decide which is a better match based purely on how "far away"
- // they are for lookup purposes. A canonincal example here would
- // be if one declaration shadows or overrides the other.
- //
- // 2. We can look at parameter lists of `left` and `right`, their types, etc.
- // do decide which is a better match based purely on structure.
- // Canonical examples in this case would be preferring a non-generic
- // candidate over a generic one, preferring a non-variadic candidate
- // over a variadic one, and preferring a candidate with fewer
- // default parameters over one with more.
- //
- // Deciding how to order/interleave these two categories of criteria
- // is an important design decision.
- //
- // For example, consider:
- //
- // float f(float x);
- //
- // struct S
- // {
- // int f<T>(T x);
- //
- // float g(float y) { return f(y); }
- // }
- //
- // In terms of structural/type matching, the global `f` is a more specialized
- // candidate at the call site, while in terms of lookup/lexical crieteria
- // the `S.f` declaration is better.
- //
- // For now we are considering lookup/overriding concerns first (so
- // we would bias in favor of selecting `S.f` in the above example), and then
- // structural/type concerns, but a more nuanced approach may be
- // required in the future to better match programmer intuition.
- //
- auto itemDiff = CompareLookupResultItems(left->item, right->item);
- if(itemDiff)
- return itemDiff;
-
- auto specificityDiff = compareOverloadCandidateSpecificity(left->item, right->item);
- if(specificityDiff)
- return specificityDiff;
-
- // `export` function is more flavored than `extern` function. But other modifiers are not considered.
- auto externExportDiff = getExportRank(left->item.declRef, right->item.declRef);
- if (externExportDiff)
- return externExportDiff;
-
- // We need to consider the distance of the declarations to the global scope to resolve this case:
- // float f(float x);
- // struct S
- // {
- // float f(float x);
- // float g(float y) { return f(y); } // will call S::f() instead of ::f()
- // }
- // we will count the distance from the reference site to the declaration in the scope tree.
-
- // NOTE: We CAN'T do this for the generic function, because generic lookup is little bit complicated.
- // It will go through multiple passes of candidates compare.
- // In the first pass, it will lookup all the generic candidates that matches the generic parameter only,
- // e.g., the following generic functions are totally different, but they will be selected as candidates
- // because the function name and the generic parameters are the same:
- // void func<let Z0 : uint, let Z1 : uint>(Z0 a, Z1 b);
- // void func<let Z0 : uint, let Z1 : uint>(Z0 a, Z1 b, Z0 c);
- // void func<let Z0 : uint, let Z1 : uint>(Z0 a, Z1 b, Z0 c, Z1 d);
- //
- // So in this case, we should not consider the scope rank and overload rank at all, because there is only
- // one of above candidates is valid, and the rank calculation doesn't consider the correctness of the
- // candidates, so it could select the wrong candidate.
- //
- // In the next pass, the lookup system will match the input parameters in those candidates to find out the valid
- // match, the "flavor" field will become "Func" or "Expr". So the rank calculation can be applied.
- if (left->flavor == OverloadCandidate::Flavor::Generic ||
- left->flavor == OverloadCandidate::Flavor::UnspecializedGeneric ||
- right->flavor == OverloadCandidate::Flavor::Generic ||
- right->flavor == OverloadCandidate::Flavor::UnspecializedGeneric)
- {
- return 0;
- }
-
- auto scopeRank = getScopeRank(left->item.declRef, right->item.declRef, this->m_outerScope);
- if (scopeRank)
- return scopeRank;
-
- // If we reach here, we will attempt to use overload rank to break the ties.
- auto overloadRankDiff = getOverloadRank(right->item.declRef) - getOverloadRank(left->item.declRef);
- if (overloadRankDiff)
- return overloadRankDiff;
+ return 0;
}
- return 0;
+ auto scopeRank = getScopeRank(left->item.declRef, right->item.declRef, this->m_outerScope);
+ if (scopeRank)
+ return scopeRank;
+
+ // If we reach here, we will attempt to use overload rank to break the ties.
+ auto overloadRankDiff =
+ getOverloadRank(right->item.declRef) - getOverloadRank(left->item.declRef);
+ if (overloadRankDiff)
+ return overloadRankDiff;
}
- void SemanticsVisitor::AddOverloadCandidateInner(
- OverloadResolveContext& context,
- OverloadCandidate& candidate)
- {
- // Filter our existing candidates, to remove any that are worse than our new one
+ return 0;
+}
- bool keepThisCandidate = true; // should this candidate be kept?
+void SemanticsVisitor::AddOverloadCandidateInner(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate)
+{
+ // Filter our existing candidates, to remove any that are worse than our new one
- if (context.bestCandidates.getCount() != 0)
+ bool keepThisCandidate = true; // should this candidate be kept?
+
+ if (context.bestCandidates.getCount() != 0)
+ {
+ // We have multiple candidates right now, so filter them.
+ // This is only used in an assert in debug builds
+ [[maybe_unused]] 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)
{
- // We have multiple candidates right now, so filter them.
- // This is only used in an assert in debug builds
- [[maybe_unused]] 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)
{
- int cmp = CompareOverloadCandidates(&candidate, &context.bestCandidates[cc]);
- if (cmp < 0)
- {
- // our new candidate is better!
+ // 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;
+ // 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;
+ anyFiltered = true;
}
else if (cmp > 0)
{
@@ -1704,1134 +1729,1181 @@ namespace Slang
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)
+ // 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)
{
- // There was a unique best candidate, but now we are ambiguous
- context.bestCandidates.add(*context.bestCandidate);
- context.bestCandidates.add(candidate);
+ // our new candidate is better!
context.bestCandidate = nullptr;
}
- else
+ else if (cmp > 0)
{
- // This is the only candidate worth keeping track of right now
- context.bestCandidateStorage = candidate;
- context.bestCandidate = &context.bestCandidateStorage;
+ // our candidate is worse!
+ keepThisCandidate = false;
}
}
- void SemanticsVisitor::AddOverloadCandidate(
- OverloadResolveContext& context,
- OverloadCandidate& candidate,
- ConversionCost baseCost)
- {
- // Try the candidate out, to see if it is applicable at all.
- TryCheckOverloadCandidate(context, candidate);
+ // If our candidate isn't good enough, then drop it
+ if (!keepThisCandidate)
+ return;
- candidate.conversionCostSum += baseCost;
-
- // Now (potentially) add it to the set of candidate overloads to consider.
- AddOverloadCandidateInner(context, candidate);
+ // 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);
}
-
- void SemanticsVisitor::AddFuncOverloadCandidate(
- LookupResultItem item,
- DeclRef<CallableDecl> funcDeclRef,
- OverloadResolveContext& context,
- ConversionCost baseCost)
+ else if (context.bestCandidate)
{
- auto funcDecl = funcDeclRef.getDecl();
- ensureDecl(funcDecl, DeclCheckState::CanUseFuncSignature);
+ // 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;
+ }
+}
- // 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;
- }
- }
+void SemanticsVisitor::AddOverloadCandidate(
+ OverloadResolveContext& context,
+ OverloadCandidate& candidate,
+ ConversionCost baseCost)
+{
+ // Try the candidate out, to see if it is applicable at all.
+ TryCheckOverloadCandidate(context, candidate);
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Func;
- candidate.item = item;
- candidate.resultType = getResultType(m_astBuilder, funcDeclRef);
+ candidate.conversionCostSum += baseCost;
- AddOverloadCandidate(context, candidate, baseCost);
- }
+ // Now (potentially) add it to the set of candidate overloads to consider.
+ AddOverloadCandidateInner(context, candidate);
+}
- void SemanticsVisitor::AddFuncOverloadCandidate(
- FuncType* funcType,
- OverloadResolveContext& context,
- ConversionCost baseCost)
- {
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Expr;
- candidate.funcType = funcType;
- candidate.resultType = funcType->getResultType();
+void SemanticsVisitor::AddFuncOverloadCandidate(
+ LookupResultItem item,
+ DeclRef<CallableDecl> funcDeclRef,
+ OverloadResolveContext& context,
+ ConversionCost baseCost)
+{
+ auto funcDecl = funcDeclRef.getDecl();
+ ensureDecl(funcDecl, DeclCheckState::CanUseFuncSignature);
- AddOverloadCandidate(context, candidate, baseCost);
+ // 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;
+ }
}
- void SemanticsVisitor::AddFuncExprOverloadCandidate(
- FuncType* funcType,
- OverloadResolveContext& context,
- Expr* expr,
- ConversionCost baseCost)
- {
- SLANG_ASSERT(expr);
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Expr;
- candidate.funcType = funcType;
- candidate.resultType = funcType->getResultType();
- candidate.exprVal = expr;
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Func;
+ candidate.item = item;
+ candidate.resultType = getResultType(m_astBuilder, funcDeclRef);
- AddOverloadCandidate(context, candidate, baseCost);
- }
+ AddOverloadCandidate(context, candidate, baseCost);
+}
- void SemanticsVisitor::AddCtorOverloadCandidate(
- LookupResultItem typeItem,
- Type* type,
- DeclRef<ConstructorDecl> ctorDeclRef,
- OverloadResolveContext& context,
- Type* resultType,
- ConversionCost baseCost)
- {
- SLANG_UNUSED(type)
+void SemanticsVisitor::AddFuncOverloadCandidate(
+ FuncType* funcType,
+ OverloadResolveContext& context,
+ ConversionCost baseCost)
+{
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Expr;
+ candidate.funcType = funcType;
+ candidate.resultType = funcType->getResultType();
- ensureDecl(ctorDeclRef, DeclCheckState::CanUseFuncSignature);
+ AddOverloadCandidate(context, candidate, baseCost);
+}
- // `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
+void SemanticsVisitor::AddFuncExprOverloadCandidate(
+ FuncType* funcType,
+ OverloadResolveContext& context,
+ Expr* expr,
+ ConversionCost baseCost)
+{
+ SLANG_ASSERT(expr);
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Expr;
+ candidate.funcType = funcType;
+ candidate.resultType = funcType->getResultType();
+ candidate.exprVal = expr;
+
+ AddOverloadCandidate(context, candidate, baseCost);
+}
- LookupResultItem ctorItem;
- ctorItem.declRef = ctorDeclRef;
- ctorItem.breadcrumbs = new LookupResultItem::Breadcrumb(
- LookupResultItem::Breadcrumb::Kind::Member,
- typeItem.declRef,
- nullptr,
- typeItem.breadcrumbs);
+void SemanticsVisitor::AddCtorOverloadCandidate(
+ LookupResultItem typeItem,
+ Type* type,
+ DeclRef<ConstructorDecl> ctorDeclRef,
+ OverloadResolveContext& context,
+ Type* resultType,
+ ConversionCost baseCost)
+{
+ SLANG_UNUSED(type)
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Func;
- candidate.item = ctorItem;
- candidate.resultType = resultType;
+ ensureDecl(ctorDeclRef, DeclCheckState::CanUseFuncSignature);
- AddOverloadCandidate(context, candidate, baseCost);
- }
+ // `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,
+ nullptr,
+ typeItem.breadcrumbs);
+
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Func;
+ candidate.item = ctorItem;
+ candidate.resultType = resultType;
+
+ AddOverloadCandidate(context, candidate, baseCost);
+}
- bool SemanticsVisitor::OverloadResolveContext::matchArgumentsToParams(
- SemanticsVisitor* semantics,
- const List<QualType>& params,
- bool computeTypes,
- ShortList<MatchedArg>& outMatchedArgs)
+bool SemanticsVisitor::OverloadResolveContext::matchArgumentsToParams(
+ SemanticsVisitor* semantics,
+ const List<QualType>& params,
+ bool computeTypes,
+ ShortList<MatchedArg>& outMatchedArgs)
+{
+ // We allow params to end with one or more variadic packs.
+ // We will first find out how many type packs there are.
+ Index typePackCount = 0;
+ for (Index i = params.getCount() - 1; i >= 0; --i)
{
- // We allow params to end with one or more variadic packs.
- // We will first find out how many type packs there are.
- Index typePackCount = 0;
- for (Index i = params.getCount() - 1; i >= 0; --i)
- {
- if (isTypePack(params[i].type))
- typePackCount++;
- else
- break;
- }
- auto fixedParamCount = params.getCount() - typePackCount;
+ if (isTypePack(params[i].type))
+ typePackCount++;
+ else
+ break;
+ }
+ auto fixedParamCount = params.getCount() - typePackCount;
- auto remainingArgCount = getArgCount() - fixedParamCount;
+ auto remainingArgCount = getArgCount() - fixedParamCount;
- // If there are remaining arguments after matching all fixed parameters,
- // we'd better have at least one type pack.
- if (remainingArgCount > 0 && typePackCount == 0)
- return false;
+ // If there are remaining arguments after matching all fixed parameters,
+ // we'd better have at least one type pack.
+ if (remainingArgCount > 0 && typePackCount == 0)
+ return false;
- // Now we can match the arguments to the parameters.
+ // Now we can match the arguments to the parameters.
- // The fixed part comes first.
- for (Index i = 0; i < Math::Min(getArgCount(), fixedParamCount); ++i)
- {
- MatchedArg arg;
- arg.argExpr = getArg(i);
- arg.argType = getArgType(i);
- outMatchedArgs.add(arg);
- }
+ // The fixed part comes first.
+ for (Index i = 0; i < Math::Min(getArgCount(), fixedParamCount); ++i)
+ {
+ MatchedArg arg;
+ arg.argExpr = getArg(i);
+ arg.argType = getArgType(i);
+ outMatchedArgs.add(arg);
+ }
- // Try to match the variadic part.
- // Is the corresponding argument a expand expr? If so it will map 1:1 to the type pack param.
- auto astBuilder = semantics->getASTBuilder();
+ // Try to match the variadic part.
+ // Is the corresponding argument a expand expr? If so it will map 1:1 to the type pack param.
+ auto astBuilder = semantics->getASTBuilder();
- if (remainingArgCount <= 0)
- return true;
- if (typePackCount == 0)
- return false;
+ if (remainingArgCount <= 0)
+ return true;
+ if (typePackCount == 0)
+ return false;
- // If the number of type packs can't evenly divide the remaining arguments,
- // there isn't a match.
- if (remainingArgCount % typePackCount != 0)
- return false;
+ // If the number of type packs can't evenly divide the remaining arguments,
+ // there isn't a match.
+ if (remainingArgCount % typePackCount != 0)
+ return false;
- // The default case is to group the remaining arguments into evenly divided PackExprs.
- Index typePackSize = remainingArgCount / typePackCount;
- for (Index i = 0; i < typePackCount; ++i)
+ // The default case is to group the remaining arguments into evenly divided PackExprs.
+ Index typePackSize = remainingArgCount / typePackCount;
+ for (Index i = 0; i < typePackCount; ++i)
+ {
+ // If type pack size is 1, we may not need to wrap things in a PackExpr,
+ // if the argument is already a pack.
+ if (typePackSize == 1)
{
- // If type pack size is 1, we may not need to wrap things in a PackExpr,
- // if the argument is already a pack.
- if (typePackSize == 1)
+ auto argType = getArgType(fixedParamCount + i);
+ if (auto typeType = as<TypeType>(argType))
{
- auto argType = getArgType(fixedParamCount + i);
- if (auto typeType = as<TypeType>(argType))
- {
- argType = typeType->getType();
- }
- if (isTypePack(argType))
- {
- MatchedArg arg;
- arg.argExpr = getArg(fixedParamCount + i);
- arg.argType = getArgType(fixedParamCount + i);
- outMatchedArgs.add(arg);
- continue;
- }
+ argType = typeType->getType();
}
- PackExpr* packExpr = nullptr;
- if (mode == Mode::ForReal)
+ if (isTypePack(argType))
{
- packExpr = astBuilder->create<PackExpr>();
- packExpr->loc = loc;
+ MatchedArg arg;
+ arg.argExpr = getArg(fixedParamCount + i);
+ arg.argType = getArgType(fixedParamCount + i);
+ outMatchedArgs.add(arg);
+ continue;
}
- ShortList<Type*> types;
- for (Index j = 0; j < typePackSize; ++j)
+ }
+ PackExpr* packExpr = nullptr;
+ if (mode == Mode::ForReal)
+ {
+ packExpr = astBuilder->create<PackExpr>();
+ packExpr->loc = loc;
+ }
+ ShortList<Type*> types;
+ for (Index j = 0; j < typePackSize; ++j)
+ {
+ if (packExpr)
{
- if (packExpr)
- {
- auto arg = getArg(fixedParamCount + i * typePackSize + j);
- packExpr->args.add(arg);
- }
- if (computeTypes)
- types.add(getArgTypeForInference(fixedParamCount + i * typePackSize + j, semantics));
+ auto arg = getArg(fixedParamCount + i * typePackSize + j);
+ packExpr->args.add(arg);
}
- MatchedArg matchedArg;
- matchedArg.argExpr = packExpr;
if (computeTypes)
- {
- matchedArg.argType = astBuilder->getTypePack(types.getArrayView().arrayView);
- if (packExpr)
- packExpr->type = matchedArg.argType;
- }
- outMatchedArgs.add(matchedArg);
+ types.add(
+ getArgTypeForInference(fixedParamCount + i * typePackSize + j, semantics));
}
- return true;
+ MatchedArg matchedArg;
+ matchedArg.argExpr = packExpr;
+ if (computeTypes)
+ {
+ matchedArg.argType = astBuilder->getTypePack(types.getArrayView().arrayView);
+ if (packExpr)
+ packExpr->type = matchedArg.argType;
+ }
+ outMatchedArgs.add(matchedArg);
}
+ return true;
+}
- DeclRef<Decl> SemanticsVisitor::inferGenericArguments(
- DeclRef<GenericDecl> genericDeclRef,
- OverloadResolveContext& context,
- ArrayView<Val*> knownGenericArgs,
- ConversionCost& outBaseCost,
- List<QualType> *innerParameterTypes)
- {
- // We have been asked to infer zero or more arguments to
- // `genericDeclRef`, in a context where it is being applied
- // to value-level arguments in `context`.
- //
- // It is possible that the call site included one or more
- // explicit arguments, in which case `substWithKnownGenericArgs`
- // will have been filled in and contain those. Otherwise,
- // that parameter will be null, and we are expected to
- // infer all arguments.
-
- // The declaration of the generic must be checked up to a point
- // where we can attempt to form specializations of it (which in
- // practice means that the declarations of its parameters and
- // their constraints must have been checked).
- //
- ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
+DeclRef<Decl> SemanticsVisitor::inferGenericArguments(
+ DeclRef<GenericDecl> genericDeclRef,
+ OverloadResolveContext& context,
+ ArrayView<Val*> knownGenericArgs,
+ ConversionCost& outBaseCost,
+ List<QualType>* innerParameterTypes)
+{
+ // We have been asked to infer zero or more arguments to
+ // `genericDeclRef`, in a context where it is being applied
+ // to value-level arguments in `context`.
+ //
+ // It is possible that the call site included one or more
+ // explicit arguments, in which case `substWithKnownGenericArgs`
+ // will have been filled in and contain those. Otherwise,
+ // that parameter will be null, and we are expected to
+ // infer all arguments.
+
+ // The declaration of the generic must be checked up to a point
+ // where we can attempt to form specializations of it (which in
+ // practice means that the declarations of its parameters and
+ // their constraints must have been checked).
+ //
+ ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
- // Conceptually, we are going to be trying to infer any unspecified
- // generic arguments by forming a system of constraints on those arguments
- // and then attempting to solve the constraint system.
- //
- // While the constraint solver we have implemented today is not especially
- // clever, we follow a flow that should in principle allow us to plug in
- // something more clever down the line.
- //
- ConstraintSystem constraints;
- constraints.loc = context.loc;
- constraints.genericDecl = genericDeclRef.getDecl();
-
- // In order to perform matching between the types passed in at the
- // call site represented by `context` and the parameters of the
- // declaraiton being applied, we want to form a reference to
- // the "inner" declaration of the generic (e.g., the `FuncitonDecl`
- // under the `GenericDecl`).
- //
- // Check what type of declaration we are dealing with, and then try
- // to match it up with the arguments accordingly...
+ // Conceptually, we are going to be trying to infer any unspecified
+ // generic arguments by forming a system of constraints on those arguments
+ // and then attempting to solve the constraint system.
+ //
+ // While the constraint solver we have implemented today is not especially
+ // clever, we follow a flow that should in principle allow us to plug in
+ // something more clever down the line.
+ //
+ ConstraintSystem constraints;
+ constraints.loc = context.loc;
+ constraints.genericDecl = genericDeclRef.getDecl();
+
+ // In order to perform matching between the types passed in at the
+ // call site represented by `context` and the parameters of the
+ // declaraiton being applied, we want to form a reference to
+ // the "inner" declaration of the generic (e.g., the `FuncitonDecl`
+ // under the `GenericDecl`).
+ //
+ // Check what type of declaration we are dealing with, and then try
+ // to match it up with the arguments accordingly...
- if (auto funcDeclRef = as<CallableDecl>(genericDeclRef.getDecl()->inner))
+ if (auto funcDeclRef = as<CallableDecl>(genericDeclRef.getDecl()->inner))
+ {
+ List<QualType> paramTypes;
+ if (!innerParameterTypes)
{
- List<QualType> paramTypes;
- if (!innerParameterTypes)
+ auto params = getParameters(m_astBuilder, funcDeclRef).toArray();
+ for (auto param : params)
{
- auto params = getParameters(m_astBuilder, funcDeclRef).toArray();
- for (auto param : params)
- {
- paramTypes.add(getParamQualType(m_astBuilder, param));
- }
- innerParameterTypes = &paramTypes;
+ paramTypes.add(getParamQualType(m_astBuilder, param));
}
+ innerParameterTypes = &paramTypes;
+ }
- ShortList<OverloadResolveContext::MatchedArg> matchedArgs;
+ ShortList<OverloadResolveContext::MatchedArg> matchedArgs;
- // We now try to match arguments to parameters.
+ // We now try to match arguments to parameters.
+ //
+ // Note that if there are *too few* arguments, we might still have
+ // a match, because the other arguments might have default values
+ // that can be used.
+ //
+ if (!context.matchArgumentsToParams(this, *innerParameterTypes, true, matchedArgs))
+ {
+ return DeclRef<Decl>();
+ }
+
+ // Perform type unification between arguments and parameters, so
+ // we can populate the resolve system with inital constraints.
+ //
+ for (Index aa = 0; aa < matchedArgs.getCount(); ++aa)
+ {
+ // The question here is whether failure to "unify" an argument
+ // and parameter should lead to immediate failure.
//
- // Note that if there are *too few* arguments, we might still have
- // a match, because the other arguments might have default values
- // that can be used.
+ // The case that is interesting is if we want to unify, say:
+ // `vector<float,N>` and `vector<int,3>`
//
- if (!context.matchArgumentsToParams(this, *innerParameterTypes, true, matchedArgs))
- {
- return DeclRef<Decl>();
- }
-
- // Perform type unification between arguments and parameters, so
- // we can populate the resolve system with inital constraints.
+ // 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<T,3>`)
//
- for (Index aa = 0; aa < matchedArgs.getCount(); ++aa)
+ // So the question is then whether a mismatch during the
+ // unification step should be taken as an immediate failure...
+ auto argType = matchedArgs[aa].argType;
+ auto paramType = (*innerParameterTypes)[aa];
+ auto canUnify = TryUnifyTypes(
+ constraints,
+ ValUnificationContext(),
+ QualType(argType, paramType.isLeftValue),
+ paramType);
+
+ // It is an error if we can't unify the argument with a type pack parameter.
+ if (!canUnify && isTypePack(paramType))
{
- // 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<float,N>` and `vector<int,3>`
- //
- // 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<T,3>`)
- //
- // So the question is then whether a mismatch during the
- // unification step should be taken as an immediate failure...
- auto argType = matchedArgs[aa].argType;
- auto paramType = (*innerParameterTypes)[aa];
- auto canUnify = TryUnifyTypes(
- constraints,
- ValUnificationContext(),
- QualType(argType, paramType.isLeftValue),
- paramType);
-
- // It is an error if we can't unify the argument with a type pack parameter.
- if (!canUnify && isTypePack(paramType))
- {
- return DeclRef<Decl>();
- }
+ return DeclRef<Decl>();
}
}
- else
- {
- // TODO(tfoley): any other cases needed here?
- return DeclRef<Decl>();
- }
-
- // Once we have added all the appropriate constraints to the system, we
- // will try to solve for a set of arguments to the generic that satisfy
- // those constraints.
- //
- // Note that this step *also* attempts to infer arguments for all the
- // implicit parameters of a generic. Notably, this means inferring
- // witnesses for interface conformance constraints.
- //
- // TODO(tfoley): We probably need to pass along the explicit arguments here,
- // so that the solver knows to accept those arguments as-is.
- //
- return trySolveConstraintSystem(
- &constraints, genericDeclRef, knownGenericArgs, outBaseCost);
}
-
- void SemanticsVisitor::AddTypeOverloadCandidates(
- Type* type,
- OverloadResolveContext& context)
+ else
{
- // 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.
- //
- // Internally, all `__init` declarations are stored with the name
- // `$init`, to avoid potential conflicts if a user decided to name
- // a field/method `__init`.
- //
- // 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.
- //
- // 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 core module currently has to define
- // some just to make code that does, e.g., `float(1.0f)` work.)
+ // TODO(tfoley): any other cases needed here?
+ return DeclRef<Decl>();
+ }
- LookupResult initializers = lookUpMember(
- m_astBuilder,
- this,
- getName("$init"),
- type,
- context.sourceScope,
- LookupMask::Default,
- LookupOptions::NoDeref);
+ // Once we have added all the appropriate constraints to the system, we
+ // will try to solve for a set of arguments to the generic that satisfy
+ // those constraints.
+ //
+ // Note that this step *also* attempts to infer arguments for all the
+ // implicit parameters of a generic. Notably, this means inferring
+ // witnesses for interface conformance constraints.
+ //
+ // TODO(tfoley): We probably need to pass along the explicit arguments here,
+ // so that the solver knows to accept those arguments as-is.
+ //
+ return trySolveConstraintSystem(&constraints, genericDeclRef, knownGenericArgs, outBaseCost);
+}
- AddOverloadCandidates(initializers, context);
- }
+void SemanticsVisitor::AddTypeOverloadCandidates(Type* type, OverloadResolveContext& context)
+{
+ // 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.
+ //
+ // Internally, all `__init` declarations are stored with the name
+ // `$init`, to avoid potential conflicts if a user decided to name
+ // a field/method `__init`.
+ //
+ // 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.
+ //
+ // 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 core module currently has to define
+ // some just to make code that does, e.g., `float(1.0f)` work.)
+
+ LookupResult initializers = lookUpMember(
+ m_astBuilder,
+ this,
+ getName("$init"),
+ type,
+ context.sourceScope,
+ LookupMask::Default,
+ LookupOptions::NoDeref);
+
+ AddOverloadCandidates(initializers, context);
+}
- void SemanticsVisitor::addOverloadCandidatesForCallToGeneric(
- LookupResultItem genericItem,
- OverloadResolveContext& context,
- ArrayView<Val*> knownGenericArgs)
- {
- auto genericDeclRef = genericItem.declRef.as<GenericDecl>();
- SLANG_ASSERT(genericDeclRef);
+void SemanticsVisitor::addOverloadCandidatesForCallToGeneric(
+ LookupResultItem genericItem,
+ OverloadResolveContext& context,
+ ArrayView<Val*> knownGenericArgs)
+{
+ auto genericDeclRef = genericItem.declRef.as<GenericDecl>();
+ SLANG_ASSERT(genericDeclRef);
- ConversionCost baseCost = kConversionCost_None;
+ ConversionCost baseCost = kConversionCost_None;
- // Try to infer generic arguments, based on the context
- DeclRef<Decl> innerRef = inferGenericArguments(genericDeclRef, context, knownGenericArgs, baseCost);
+ // Try to infer generic arguments, based on the context
+ DeclRef<Decl> innerRef =
+ inferGenericArguments(genericDeclRef, context, knownGenericArgs, baseCost);
- if (innerRef)
- {
- // If inference works, then we've now got a
- // specialized declaration reference we can apply.
+ if (innerRef)
+ {
+ // If inference works, then we've now got a
+ // specialized declaration reference we can apply.
- LookupResultItem innerItem;
- innerItem.breadcrumbs = genericItem.breadcrumbs;
- innerItem.declRef = innerRef;
- AddDeclRefOverloadCandidates(innerItem, context, baseCost);
- }
- 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 = genericItem;
- candidate.flavor = OverloadCandidate::Flavor::UnspecializedGeneric;
- candidate.status = OverloadCandidate::Status::GenericArgumentInferenceFailed;
+ LookupResultItem innerItem;
+ innerItem.breadcrumbs = genericItem.breadcrumbs;
+ innerItem.declRef = innerRef;
+ AddDeclRefOverloadCandidates(innerItem, context, baseCost);
+ }
+ 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 = genericItem;
+ candidate.flavor = OverloadCandidate::Flavor::UnspecializedGeneric;
+ candidate.status = OverloadCandidate::Status::GenericArgumentInferenceFailed;
- AddOverloadCandidateInner(context, candidate);
- }
+ AddOverloadCandidateInner(context, candidate);
}
+}
- void SemanticsVisitor::AddDeclRefOverloadCandidates(
- LookupResultItem item,
- OverloadResolveContext& context,
- ConversionCost baseCost)
+void SemanticsVisitor::AddDeclRefOverloadCandidates(
+ LookupResultItem item,
+ OverloadResolveContext& context,
+ ConversionCost baseCost)
+{
+ if (auto funcDeclRef = item.declRef.as<CallableDecl>())
{
- if (auto funcDeclRef = item.declRef.as<CallableDecl>())
- {
- AddFuncOverloadCandidate(item, funcDeclRef, context, baseCost);
- }
- else if (auto aggTypeDeclRef = item.declRef.as<AggTypeDecl>())
- {
- auto type = DeclRefType::create(m_astBuilder, aggTypeDeclRef);
- AddTypeOverloadCandidates(type, context);
- }
- else if (auto genericDeclRef = item.declRef.as<GenericDecl>())
- {
- LookupResultItem innerItem;
- innerItem.breadcrumbs = item.breadcrumbs;
- innerItem.declRef = genericDeclRef;
- addOverloadCandidatesForCallToGeneric(innerItem, context, ArrayView<Val*>());
- }
- else if( auto typeDefDeclRef = item.declRef.as<TypeDefDecl>() )
- {
- auto type = getNamedType(m_astBuilder, typeDefDeclRef);
- AddTypeOverloadCandidates(type, context);
- }
- else if( auto genericTypeParamDeclRef = item.declRef.as<GenericTypeParamDecl>() )
- {
- auto type = DeclRefType::create(m_astBuilder, genericTypeParamDeclRef);
- AddTypeOverloadCandidates(type, context);
- }
- else if( auto localDeclRef = item.declRef.as<ParamDecl>() )
- {
- // We could probably be broader than just parameters here
- // eventually.
- // Limit it for now though to make the specialization easier
- // TODO: why can't this use DeclCheckState::CanUseFuncSignature
- ensureDecl(localDeclRef, DeclCheckState::TypesFullyResolved);
- const auto type = localDeclRef.getDecl()->getType();
- // We can only add overload candidates if this is known to be a function
- if(const auto funType = as<FuncType>(type))
- AddFuncExprOverloadCandidate(funType, context, context.originalExpr->functionExpr, baseCost);
- else
- return;
- }
+ AddFuncOverloadCandidate(item, funcDeclRef, context, baseCost);
+ }
+ else if (auto aggTypeDeclRef = item.declRef.as<AggTypeDecl>())
+ {
+ auto type = DeclRefType::create(m_astBuilder, aggTypeDeclRef);
+ AddTypeOverloadCandidates(type, context);
+ }
+ else if (auto genericDeclRef = item.declRef.as<GenericDecl>())
+ {
+ LookupResultItem innerItem;
+ innerItem.breadcrumbs = item.breadcrumbs;
+ innerItem.declRef = genericDeclRef;
+ addOverloadCandidatesForCallToGeneric(innerItem, context, ArrayView<Val*>());
+ }
+ else if (auto typeDefDeclRef = item.declRef.as<TypeDefDecl>())
+ {
+ auto type = getNamedType(m_astBuilder, typeDefDeclRef);
+ AddTypeOverloadCandidates(type, context);
+ }
+ else if (auto genericTypeParamDeclRef = item.declRef.as<GenericTypeParamDecl>())
+ {
+ auto type = DeclRefType::create(m_astBuilder, genericTypeParamDeclRef);
+ AddTypeOverloadCandidates(type, context);
+ }
+ else if (auto localDeclRef = item.declRef.as<ParamDecl>())
+ {
+ // We could probably be broader than just parameters here
+ // eventually.
+ // Limit it for now though to make the specialization easier
+ // TODO: why can't this use DeclCheckState::CanUseFuncSignature
+ ensureDecl(localDeclRef, DeclCheckState::TypesFullyResolved);
+ const auto type = localDeclRef.getDecl()->getType();
+ // We can only add overload candidates if this is known to be a function
+ if (const auto funType = as<FuncType>(type))
+ AddFuncExprOverloadCandidate(
+ funType,
+ context,
+ context.originalExpr->functionExpr,
+ baseCost);
else
- {
- // TODO(tfoley): any other cases needed here?
return;
- }
}
+ else
+ {
+ // TODO(tfoley): any other cases needed here?
+ return;
+ }
+}
- void SemanticsVisitor::AddOverloadCandidates(
- LookupResult const& result,
- OverloadResolveContext& context)
+void SemanticsVisitor::AddOverloadCandidates(
+ LookupResult const& result,
+ OverloadResolveContext& context)
+{
+ if (result.isOverloaded())
{
- if(result.isOverloaded())
- {
- for(auto item : result.items)
- {
- AddDeclRefOverloadCandidates(item, context, kConversionCost_None);
- }
- }
- else
+ for (auto item : result.items)
{
- AddDeclRefOverloadCandidates(result.item, context, kConversionCost_None);
+ AddDeclRefOverloadCandidates(item, context, kConversionCost_None);
}
}
+ else
+ {
+ AddDeclRefOverloadCandidates(result.item, context, kConversionCost_None);
+ }
+}
- void SemanticsVisitor::AddOverloadCandidates(
- Expr* funcExpr,
- OverloadResolveContext& context)
+void SemanticsVisitor::AddOverloadCandidates(Expr* funcExpr, OverloadResolveContext& context)
+{
+ // A call of the form `(<something>)(<args>)` should be
+ // resolved as if the user wrote `<something>(<args>)`,
+ // so that we avoid introducing intermediate expressions
+ // of function type in cases where they are not needed.
+ //
+ while (auto parenExpr = as<ParenExpr>(funcExpr))
{
- // A call of the form `(<something>)(<args>)` should be
- // resolved as if the user wrote `<something>(<args>)`,
- // so that we avoid introducing intermediate expressions
- // of function type in cases where they are not needed.
- //
- while(auto parenExpr = as<ParenExpr>(funcExpr))
- {
- funcExpr = parenExpr->base;
- }
+ funcExpr = parenExpr->base;
+ }
- auto funcExprType = funcExpr->type;
+ auto funcExprType = funcExpr->type;
- if (auto declRefExpr = as<DeclRefExpr>(funcExpr))
- {
- // The expression directly referenced a declaration,
- // so we can use that declaration directly to look
- // for anything applicable.
- AddDeclRefOverloadCandidates(LookupResultItem(declRefExpr->declRef), context, kConversionCost_None);
- }
- else if (auto higherOrderExpr = as<HigherOrderInvokeExpr>(funcExpr))
- {
- // The expression is the result of a higher order function application.
- AddHigherOrderOverloadCandidates(higherOrderExpr, context, kConversionCost_None);
- }
- else if (auto funcType = as<FuncType>(funcExprType))
- {
- // TODO(tfoley): deprecate this path...
- AddFuncOverloadCandidate(funcType, context, kConversionCost_None);
- }
- else if (auto overloadedExpr = as<OverloadedExpr>(funcExpr))
+ if (auto declRefExpr = as<DeclRefExpr>(funcExpr))
+ {
+ // The expression directly referenced a declaration,
+ // so we can use that declaration directly to look
+ // for anything applicable.
+ AddDeclRefOverloadCandidates(
+ LookupResultItem(declRefExpr->declRef),
+ context,
+ kConversionCost_None);
+ }
+ else if (auto higherOrderExpr = as<HigherOrderInvokeExpr>(funcExpr))
+ {
+ // The expression is the result of a higher order function application.
+ AddHigherOrderOverloadCandidates(higherOrderExpr, context, kConversionCost_None);
+ }
+ else if (auto funcType = as<FuncType>(funcExprType))
+ {
+ // TODO(tfoley): deprecate this path...
+ AddFuncOverloadCandidate(funcType, context, kConversionCost_None);
+ }
+ else if (auto overloadedExpr = as<OverloadedExpr>(funcExpr))
+ {
+ AddOverloadCandidates(overloadedExpr->lookupResult2, context);
+ }
+ else if (auto overloadedExpr2 = as<OverloadedExpr2>(funcExpr))
+ {
+ for (auto item : overloadedExpr2->candidiateExprs)
{
- AddOverloadCandidates(overloadedExpr->lookupResult2, context);
+ AddOverloadCandidates(item, context);
}
- else if (auto overloadedExpr2 = as<OverloadedExpr2>(funcExpr))
+ }
+ else if (auto partiallyAppliedGenericExpr = as<PartiallyAppliedGenericExpr>(funcExpr))
+ {
+ // A partially-applied generic is allowed as an overload candidate,
+ // and carries along an (incomplete) substitution that can be used
+ // to carry the arguments known so far.
+ //
+ addOverloadCandidatesForCallToGeneric(
+ LookupResultItem(partiallyAppliedGenericExpr->baseGenericDeclRef),
+ context,
+ partiallyAppliedGenericExpr->knownGenericArgs.getArrayView());
+ }
+ else if (auto typeType = as<TypeType>(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->getType();
+ AddTypeOverloadCandidates(type, context);
+ return;
+ }
+}
+
+void SemanticsVisitor::AddHigherOrderOverloadCandidates(
+ Expr* funcExpr,
+ OverloadResolveContext& context,
+ ConversionCost baseCost)
+{
+ // Lookup the higher order function and process types accordingly. In the future,
+ // if there are enough varieties, we can have dispatch logic instead of an
+ // if-else ladder.
+ if (auto expr = as<HigherOrderInvokeExpr>(funcExpr))
+ {
+ auto funcDeclRefExpr =
+ as<DeclRefExpr>(getInnerMostExprFromHigherOrderExpr(expr->baseFunction));
+ if (!funcDeclRefExpr)
+ return;
+ if (auto baseFuncDeclRef = funcDeclRefExpr->declRef.as<CallableDecl>())
{
- for (auto item : overloadedExpr2->candidiateExprs)
+ // Base is a normal or fully specialized generic function.
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Expr;
+ if (auto diffExpr = as<HigherOrderInvokeExpr>(expr))
{
- AddOverloadCandidates(item, context);
+ candidate.funcType = as<FuncType>(diffExpr->type.type);
}
+ candidate.resultType = candidate.funcType->getResultType();
+ candidate.item = LookupResultItem(baseFuncDeclRef);
+ candidate.exprVal = expr;
+ AddOverloadCandidate(context, candidate, baseCost);
}
- else if (auto partiallyAppliedGenericExpr = as<PartiallyAppliedGenericExpr>(funcExpr))
+ else if (auto baseFuncGenericDeclRef = funcDeclRefExpr->declRef.as<GenericDecl>())
{
- // A partially-applied generic is allowed as an overload candidate,
- // and carries along an (incomplete) substitution that can be used
- // to carry the arguments known so far.
- //
- addOverloadCandidatesForCallToGeneric(
- LookupResultItem(partiallyAppliedGenericExpr->baseGenericDeclRef),
+ // Process func type to generate JVP func type.
+ auto diffFuncType = as<FuncType>(expr->type.type);
+ SLANG_ASSERT(diffFuncType);
+
+ // Extract parameter list from processed type.
+ List<QualType> paramTypes;
+
+ for (Index ii = 0; ii < diffFuncType->getParamCount(); ii++)
+ paramTypes.add(getParamQualType(diffFuncType->getParamType(ii)));
+
+ // Try to infer generic arguments, based on the updated context.
+ OverloadResolveContext subContext = context;
+ ConversionCost baseCost1 = kConversionCost_None;
+ DeclRef<Decl> innerRef = inferGenericArguments(
+ baseFuncGenericDeclRef,
context,
- partiallyAppliedGenericExpr->knownGenericArgs.getArrayView());
+ ArrayView<Val*>(),
+ baseCost1,
+ &paramTypes);
+
+ if (!innerRef)
+ return;
+
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Expr;
+ if (innerRef)
+ {
+ diffFuncType = as<FuncType>(innerRef.substitute(m_astBuilder, diffFuncType));
+ candidate.item = LookupResultItem(innerRef);
+ }
+ else
+ {
+ candidate.item = LookupResultItem(funcDeclRefExpr->declRef);
+ }
+ candidate.funcType = as<FuncType>(diffFuncType);
+ candidate.resultType = candidate.funcType->getResultType();
+
+ // Substitute all types in the high-order expression chain.
+ Expr* inner = expr;
+ HigherOrderInvokeExpr* lastInner = nullptr;
+ while (auto hoInner = as<HigherOrderInvokeExpr>(inner))
+ {
+ lastInner = hoInner;
+ if (innerRef)
+ hoInner->type = innerRef.substitute(m_astBuilder, hoInner->type.type);
+ inner = hoInner->baseFunction;
+ }
+ // Set inner expression to resolved declref expr.
+ if (lastInner)
+ {
+ auto baseExpr = GetBaseExpr(funcDeclRefExpr);
+ lastInner->baseFunction = ConstructLookupResultExpr(
+ candidate.item,
+ baseExpr,
+ funcDeclRefExpr->name,
+ funcDeclRefExpr->loc,
+ funcDeclRefExpr);
+ }
+ candidate.exprVal = expr;
+ expr->type.type = diffFuncType;
+ AddOverloadCandidate(context, candidate, baseCost + baseCost1);
}
- else if (auto typeType = as<TypeType>(funcExprType))
+ else
{
- // 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->getType();
- AddTypeOverloadCandidates(type, context);
- return;
+ // Unhandled case for the inner expr.
+ getSink()->diagnose(funcExpr->loc, Diagnostics::expectedFunction, funcExpr->type);
+ funcExpr->type = this->getASTBuilder()->getErrorType();
}
}
+}
+
+String SemanticsVisitor::getCallSignatureString(OverloadResolveContext& context)
+{
+ StringBuilder argsListBuilder;
+ argsListBuilder << "(";
+
+ UInt argCount = context.getArgCount();
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0)
+ argsListBuilder << ", ";
+ auto argType = context.getArgType(aa);
+ if (argType)
+ context.getArgType(aa)->toText(argsListBuilder);
+ else
+ argsListBuilder << "error";
+ }
+ argsListBuilder << ")";
+ return argsListBuilder.produceString();
+}
- void SemanticsVisitor::AddHigherOrderOverloadCandidates(
- Expr* funcExpr,
- OverloadResolveContext& context,
- ConversionCost baseCost)
+Expr* SemanticsVisitor::ResolveInvoke(InvokeExpr* expr)
+{
+ OverloadResolveContext context;
+ // check if this is a core module operator call, if so we want to use cached results
+ // to speed up compilation
+ bool shouldAddToCache = false;
+ OperatorOverloadCacheKey key;
+ TypeCheckingCache* typeCheckingCache = getLinkage()->getTypeCheckingCache();
+ if (auto opExpr = as<OperatorExpr>(expr))
{
- // Lookup the higher order function and process types accordingly. In the future,
- // if there are enough varieties, we can have dispatch logic instead of an
- // if-else ladder.
- if (auto expr = as<HigherOrderInvokeExpr>(funcExpr))
+ if (key.fromOperatorExpr(opExpr))
{
- auto funcDeclRefExpr = as<DeclRefExpr>(getInnerMostExprFromHigherOrderExpr(expr->baseFunction));
- if (!funcDeclRefExpr)
- return;
- if (auto baseFuncDeclRef = funcDeclRefExpr->declRef.as<CallableDecl>())
+ OverloadCandidate candidate;
+ if (typeCheckingCache->resolvedOperatorOverloadCache.tryGetValue(key, candidate))
{
- // Base is a normal or fully specialized generic function.
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Expr;
- if (auto diffExpr = as<HigherOrderInvokeExpr>(expr))
- {
- candidate.funcType = as<FuncType>(diffExpr->type.type);
- }
- candidate.resultType = candidate.funcType->getResultType();
- candidate.item = LookupResultItem(baseFuncDeclRef);
- candidate.exprVal = expr;
- AddOverloadCandidate(context, candidate, baseCost);
- }
- else if (auto baseFuncGenericDeclRef = funcDeclRefExpr->declRef.as<GenericDecl>())
- {
- // Process func type to generate JVP func type.
- auto diffFuncType = as<FuncType>(expr->type.type);
- SLANG_ASSERT(diffFuncType);
-
- // Extract parameter list from processed type.
- List<QualType> paramTypes;
-
- for (Index ii = 0; ii < diffFuncType->getParamCount(); ii++)
- paramTypes.add(getParamQualType(diffFuncType->getParamType(ii)));
-
- // Try to infer generic arguments, based on the updated context.
- OverloadResolveContext subContext = context;
- ConversionCost baseCost1 = kConversionCost_None;
- DeclRef<Decl> innerRef = inferGenericArguments(
- baseFuncGenericDeclRef,
- context,
- ArrayView<Val*>(),
- baseCost1,
- &paramTypes);
-
- if (!innerRef)
- return;
-
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Expr;
- if (innerRef)
- {
- diffFuncType = as<FuncType>(innerRef.substitute(m_astBuilder, diffFuncType));
- candidate.item = LookupResultItem(innerRef);
- }
- else
- {
- candidate.item = LookupResultItem(funcDeclRefExpr->declRef);
- }
- candidate.funcType = as<FuncType>(diffFuncType);
- candidate.resultType = candidate.funcType->getResultType();
-
- // Substitute all types in the high-order expression chain.
- Expr* inner = expr;
- HigherOrderInvokeExpr* lastInner = nullptr;
- while (auto hoInner = as<HigherOrderInvokeExpr>(inner))
- {
- lastInner = hoInner;
- if (innerRef)
- hoInner->type = innerRef.substitute(m_astBuilder, hoInner->type.type);
- inner = hoInner->baseFunction;
- }
- // Set inner expression to resolved declref expr.
- if (lastInner)
- {
- auto baseExpr = GetBaseExpr(funcDeclRefExpr);
- lastInner->baseFunction = ConstructLookupResultExpr(candidate.item, baseExpr, funcDeclRefExpr->name, funcDeclRefExpr->loc, funcDeclRefExpr);
- }
- candidate.exprVal = expr;
- expr->type.type = diffFuncType;
- AddOverloadCandidate(context, candidate, baseCost + baseCost1);
+ context.bestCandidateStorage = candidate;
+ context.bestCandidate = &context.bestCandidateStorage;
}
else
{
- // Unhandled case for the inner expr.
- getSink()->diagnose(funcExpr->loc,
- Diagnostics::expectedFunction,
- funcExpr->type);
- funcExpr->type = this->getASTBuilder()->getErrorType();
+ shouldAddToCache = true;
}
-
}
}
- String SemanticsVisitor::getCallSignatureString(
- OverloadResolveContext& context)
+ // Look at the base expression for the call, and figure out how to invoke it.
+ auto funcExpr = expr->functionExpr;
+
+ // If we are trying to apply an erroneous expression, then just bail out now.
+ if (IsErrorExpr(funcExpr))
{
- StringBuilder argsListBuilder;
- argsListBuilder << "(";
+ 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);
- UInt argCount = context.getArgCount();
- for( UInt aa = 0; aa < argCount; ++aa )
+ // If this argument is itself an overloaded value without a type
+ // then we can't sensibly continue
+ if (!arg->type && (as<OverloadedExpr>(arg) || as<OverloadedExpr2>(arg)))
{
- if(aa != 0) argsListBuilder << ", ";
- auto argType = context.getArgType(aa);
- if (argType)
- context.getArgType(aa)->toText(argsListBuilder);
- else
- argsListBuilder << "error";
+ getSink()->diagnose(expr->loc, Diagnostics::overloadedParameterToHigherOrderFunction);
+ return CreateErrorExpr(expr);
}
- argsListBuilder << ")";
- return argsListBuilder.produceString();
}
- Expr* SemanticsVisitor::ResolveInvoke(InvokeExpr* expr)
+ for (auto& arg : expr->arguments)
{
- OverloadResolveContext context;
- // check if this is a core module operator call, if so we want to use cached results
- // to speed up compilation
- bool shouldAddToCache = false;
- OperatorOverloadCacheKey key;
- TypeCheckingCache* typeCheckingCache = getLinkage()->getTypeCheckingCache();
- if (auto opExpr = as<OperatorExpr>(expr))
- {
- if (key.fromOperatorExpr(opExpr))
- {
- OverloadCandidate candidate;
- if (typeCheckingCache->resolvedOperatorOverloadCache.tryGetValue(key, candidate))
- {
- context.bestCandidateStorage = candidate;
- context.bestCandidate = &context.bestCandidateStorage;
- }
- else
- {
- shouldAddToCache = true;
- }
+ arg = maybeOpenRef(arg);
+ arg = maybeOpenExistential(arg);
+ }
+
+ context.originalExpr = expr;
+ context.funcLoc = funcExpr->loc;
+ context.argCount = expr->arguments.getCount();
+ context.args = &expr->arguments;
+ context.loc = expr->loc;
+ context.sourceScope = m_outerScope;
+ context.baseExpr = GetBaseExpr(funcExpr);
+
+ // We run 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,
+ // that `(T) expr` and `T(expr)` continue to be semantically
+ // `visitTypeCastExpr`) would allow us to continue to ensure
+ // equivalent in (almost) all cases.
+ // If callee is a type, and we are calling with one argument, then treat it as a
+ // type coercion.
+ bool typeOverloadChecked = false;
+
+ if (expr->arguments.getCount() == 1)
+ {
+ if (const auto typeType = as<TypeType>(funcExpr->type))
+ {
+ if (isDeclRefTypeOf<AggTypeDeclBase>(typeType->getType()))
+ {
+ Expr* resultExpr = nullptr;
+ DiagnosticSink tempSink(getSourceManager(), nullptr);
+ ConversionCost conversionCost = kConversionCost_None;
+ auto coerceResult = SemanticsVisitor(withSink(&tempSink))
+ ._coerce(
+ CoercionSite::ExplicitCoercion,
+ typeType->getType(),
+ &resultExpr,
+ expr->arguments[0]->type,
+ expr->arguments[0],
+ &conversionCost);
+ if (coerceResult)
+ return resultExpr;
+ typeOverloadChecked = true;
}
}
+ }
+ if (!context.bestCandidate && !typeOverloadChecked)
+ {
+ AddOverloadCandidates(funcExpr, context);
+ }
- // Look at the base expression for the call, and figure out how to invoke it.
- auto funcExpr = expr->functionExpr;
+ if (context.bestCandidates.getCount() > 0)
+ {
+ // Things were ambiguous.
- // 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.
+ // 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);
-
- // If this argument is itself an overloaded value without a type
- // then we can't sensibly continue
- if(!arg->type && (as<OverloadedExpr>(arg) || as<OverloadedExpr2>(arg)))
{
- getSink()->diagnose(
- expr->loc,
- Diagnostics::overloadedParameterToHigherOrderFunction);
return CreateErrorExpr(expr);
}
}
- for (auto& arg : expr->arguments)
+ Name* funcName = nullptr;
{
- arg = maybeOpenRef(arg);
- arg = maybeOpenExistential(arg);
- }
+ Expr* baseExpr = funcExpr;
- context.originalExpr = expr;
- context.funcLoc = funcExpr->loc;
- context.argCount = expr->arguments.getCount();
- context.args = &expr->arguments;
- context.loc = expr->loc;
- context.sourceScope = m_outerScope;
- context.baseExpr = GetBaseExpr(funcExpr);
+ if (auto baseGenericApp = as<GenericAppExpr>(baseExpr))
+ baseExpr = baseGenericApp->functionExpr;
- // We run 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,
- // that `(T) expr` and `T(expr)` continue to be semantically
- // `visitTypeCastExpr`) would allow us to continue to ensure
- // equivalent in (almost) all cases.
- // If callee is a type, and we are calling with one argument, then treat it as a
- // type coercion.
- bool typeOverloadChecked = false;
+ if (auto baseVar = as<VarExpr>(baseExpr))
+ funcName = baseVar->name;
+ else if (auto baseMemberRef = as<MemberExpr>(baseExpr))
+ funcName = baseMemberRef->name;
+ else if (auto baseOverloaded = as<OverloadedExpr>(baseExpr))
+ funcName = baseOverloaded->name;
+ }
- if (expr->arguments.getCount() == 1)
+ String argsList = getCallSignatureString(context);
+
+ if (context.bestCandidates[0].status != OverloadCandidate::Status::Applicable)
{
- if (const auto typeType = as<TypeType>(funcExpr->type))
+ // There were multiple equally-good candidates, but none actually usable.
+ // We will construct a diagnostic message to help out.
+
+ if (funcName)
{
- if (isDeclRefTypeOf<AggTypeDeclBase>(typeType->getType()))
- {
- Expr* resultExpr = nullptr;
- DiagnosticSink tempSink(getSourceManager(), nullptr);
- ConversionCost conversionCost = kConversionCost_None;
- auto coerceResult = SemanticsVisitor(withSink(&tempSink))._coerce(
- CoercionSite::ExplicitCoercion,
- typeType->getType(),
- &resultExpr,
- expr->arguments[0]->type,
- expr->arguments[0],
- &conversionCost);
- if (coerceResult)
- return resultExpr;
- typeOverloadChecked = true;
- }
+ getSink()->diagnose(
+ expr,
+ Diagnostics::noApplicableOverloadForNameWithArgs,
+ funcName,
+ argsList);
+ }
+ else
+ {
+ getSink()->diagnose(expr, Diagnostics::noApplicableWithArgs, argsList);
}
}
- if (!context.bestCandidate && !typeOverloadChecked)
- {
- AddOverloadCandidates(funcExpr, context);
- }
-
- if (context.bestCandidates.getCount() > 0)
+ else
{
- // Things were ambiguous.
+ // There were multiple applicable candidates, so we need to report them.
- // 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 (funcName)
{
- if (IsErrorExpr(arg))
- {
- return CreateErrorExpr(expr);
- }
+ getSink()->diagnose(
+ expr,
+ Diagnostics::ambiguousOverloadForNameWithArgs,
+ funcName,
+ argsList);
}
-
- Name* funcName = nullptr;
+ else
{
- Expr* baseExpr = funcExpr;
-
- if(auto baseGenericApp = as<GenericAppExpr>(baseExpr))
- baseExpr = baseGenericApp->functionExpr;
-
- if (auto baseVar = as<VarExpr>(baseExpr))
- funcName = baseVar->name;
- else if(auto baseMemberRef = as<MemberExpr>(baseExpr))
- funcName = baseMemberRef->name;
- else if(auto baseOverloaded = as<OverloadedExpr>(baseExpr))
- funcName = baseOverloaded->name;
+ getSink()->diagnose(expr, Diagnostics::ambiguousOverloadWithArgs, argsList);
}
+ }
- String argsList = getCallSignatureString(context);
+ {
+ Index candidateCount = context.bestCandidates.getCount();
+ Index maxCandidatesToPrint = 10; // don't show too many candidates at once...
+ Index candidateIndex = 0;
+ context.bestCandidates.sort([](const OverloadCandidate& c1, const OverloadCandidate& c2)
+ { return c1.status < c2.status; });
- if (context.bestCandidates[0].status != OverloadCandidate::Status::Applicable)
+ for (auto candidate : context.bestCandidates)
{
- // There were multiple equally-good candidates, but none actually usable.
- // We will construct a diagnostic message to help out.
+ String declString =
+ ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder);
- if (funcName)
- {
- getSink()->diagnose(expr, Diagnostics::noApplicableOverloadForNameWithArgs, funcName, argsList);
- }
+ if (candidate.status == OverloadCandidate::Status::VisibilityChecked)
+ getSink()->diagnose(
+ candidate.item.declRef,
+ Diagnostics::invisibleOverloadCandidate,
+ declString);
else
- {
- getSink()->diagnose(expr, Diagnostics::noApplicableWithArgs, argsList);
- }
- }
- else
- {
- // There were multiple applicable candidates, so we need to report them.
+ getSink()->diagnose(
+ candidate.item.declRef,
+ Diagnostics::overloadCandidate,
+ declString);
- if (funcName)
- {
- getSink()->diagnose(expr, Diagnostics::ambiguousOverloadForNameWithArgs, funcName, argsList);
- }
- else
- {
- getSink()->diagnose(expr, Diagnostics::ambiguousOverloadWithArgs, argsList);
- }
+ candidateIndex++;
+ if (candidateIndex == maxCandidatesToPrint)
+ break;
}
-
+ if (candidateIndex != candidateCount)
{
- Index candidateCount = context.bestCandidates.getCount();
- Index maxCandidatesToPrint = 10; // don't show too many candidates at once...
- Index candidateIndex = 0;
- context.bestCandidates.sort([](const OverloadCandidate& c1, const OverloadCandidate& c2) { return c1.status < c2.status; });
-
- for (auto candidate : context.bestCandidates)
- {
- String declString = ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder);
-
- if (candidate.status == OverloadCandidate::Status::VisibilityChecked)
- getSink()->diagnose(candidate.item.declRef, Diagnostics::invisibleOverloadCandidate, declString);
- else
- getSink()->diagnose(candidate.item.declRef, Diagnostics::overloadCandidate, declString);
-
- candidateIndex++;
- if (candidateIndex == maxCandidatesToPrint)
- break;
- }
- if (candidateIndex != candidateCount)
- {
- getSink()->diagnose(expr, Diagnostics::moreOverloadCandidates, candidateCount - candidateIndex);
- }
+ 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;
- // Now that we have resolved the overload candidate, we need to undo an `openExistential`
- // operation that was applied to `out` arguments.
- //
- auto funcType = context.bestCandidate->funcType;
- ShortList<ParameterDirection> paramDirections;
- if (funcType)
+ 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;
+
+ // Now that we have resolved the overload candidate, we need to undo an `openExistential`
+ // operation that was applied to `out` arguments.
+ //
+ auto funcType = context.bestCandidate->funcType;
+ ShortList<ParameterDirection> paramDirections;
+ if (funcType)
+ {
+ for (Index i = 0; i < funcType->getParamCount(); i++)
{
- for (Index i = 0; i < funcType->getParamCount(); i++)
- {
- paramDirections.add(funcType->getParamDirection(i));
- }
+ paramDirections.add(funcType->getParamDirection(i));
}
- else if (auto callableDeclRef = context.bestCandidate->item.declRef.as<CallableDecl>())
+ }
+ else if (auto callableDeclRef = context.bestCandidate->item.declRef.as<CallableDecl>())
+ {
+ for (auto param : callableDeclRef.getDecl()->getParameters())
{
- for (auto param : callableDeclRef.getDecl()->getParameters())
- {
- paramDirections.add(getParameterDirection(param));
- }
+ paramDirections.add(getParameterDirection(param));
}
- for (Index i = 0; i < expr->arguments.getCount(); i++)
+ }
+ for (Index i = 0; i < expr->arguments.getCount(); i++)
+ {
+ auto& arg = expr->arguments[i];
+ if (i < paramDirections.getCount())
{
- auto& arg = expr->arguments[i];
- if (i < paramDirections.getCount())
+ switch (paramDirections[i])
{
- switch (paramDirections[i])
- {
- case kParameterDirection_Out:
- case kParameterDirection_InOut:
- case kParameterDirection_Ref:
- case kParameterDirection_ConstRef:
- break;
- default:
- continue;
- }
+ case kParameterDirection_Out:
+ case kParameterDirection_InOut:
+ case kParameterDirection_Ref:
+ case kParameterDirection_ConstRef: break;
+ default: continue;
}
- if (auto extractExistentialExpr = as<ExtractExistentialValueExpr>(arg))
- arg = extractExistentialExpr->originalExpr;
}
- return CompleteOverloadCandidate(context, *context.bestCandidate);
+ if (auto extractExistentialExpr = as<ExtractExistentialValueExpr>(arg))
+ arg = extractExistentialExpr->originalExpr;
}
+ return CompleteOverloadCandidate(context, *context.bestCandidate);
+ }
- // If absolutely no viable candidates were extracted from the overloaded expression,
- // we may be dealing with a composite type or an overloaded expression with composite types.
- //
-
- auto typeExpr = funcExpr;
- if (auto overloadedExpr = as<OverloadedExpr>(funcExpr))
- {
- if (overloadedExpr->lookupResult2.isValid() && overloadedExpr->lookupResult2.isOverloaded())
- {
- typeExpr = maybeResolveOverloadedExpr(overloadedExpr, LookupMask::type, nullptr);
- }
- }
+ // If absolutely no viable candidates were extracted from the overloaded expression,
+ // we may be dealing with a composite type or an overloaded expression with composite types.
+ //
- if (auto typetype = as<TypeType>(typeExpr->type))
+ auto typeExpr = funcExpr;
+ if (auto overloadedExpr = as<OverloadedExpr>(funcExpr))
+ {
+ if (overloadedExpr->lookupResult2.isValid() && overloadedExpr->lookupResult2.isOverloaded())
{
- // We allow a special case when `funcExpr` represents a composite type,
- // in which case we will try to construct the type via memberwise assignment from the arguments.
- //
- auto initListExpr = m_astBuilder->create<InitializerListExpr>();
- initListExpr->loc = expr->loc;
- initListExpr->args.addRange(expr->arguments);
- initListExpr->type = m_astBuilder->getInitializerListType();
- Expr* outExpr = nullptr;
- if (_coerceInitializerList(typetype->getType(), &outExpr, initListExpr))
- return outExpr;
+ typeExpr = maybeResolveOverloadedExpr(overloadedExpr, LookupMask::type, nullptr);
}
+ }
- // Nothing at all was found that we could even consider invoking.
- // In all other cases, this is an error.
- getSink()->diagnose(expr->functionExpr, Diagnostics::expectedFunction, funcExpr->type);
- expr->type = QualType(m_astBuilder->getErrorType());
- return expr;
+ if (auto typetype = as<TypeType>(typeExpr->type))
+ {
+ // We allow a special case when `funcExpr` represents a composite type,
+ // in which case we will try to construct the type via memberwise assignment from the
+ // arguments.
+ //
+ auto initListExpr = m_astBuilder->create<InitializerListExpr>();
+ initListExpr->loc = expr->loc;
+ initListExpr->args.addRange(expr->arguments);
+ initListExpr->type = m_astBuilder->getInitializerListType();
+ Expr* outExpr = nullptr;
+ if (_coerceInitializerList(typetype->getType(), &outExpr, initListExpr))
+ return outExpr;
}
- void SemanticsVisitor::AddGenericOverloadCandidate(
- LookupResultItem baseItem,
- OverloadResolveContext& context)
+ // Nothing at all was found that we could even consider invoking.
+ // In all other cases, this is an error.
+ getSink()->diagnose(expr->functionExpr, Diagnostics::expectedFunction, funcExpr->type);
+ expr->type = QualType(m_astBuilder->getErrorType());
+ return expr;
+}
+
+void SemanticsVisitor::AddGenericOverloadCandidate(
+ LookupResultItem baseItem,
+ OverloadResolveContext& context)
+{
+ if (auto genericDeclRef = baseItem.declRef.as<GenericDecl>())
{
- if (auto genericDeclRef = baseItem.declRef.as<GenericDecl>())
- {
- ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
+ ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
- OverloadCandidate candidate;
- candidate.flavor = OverloadCandidate::Flavor::Generic;
- candidate.item = baseItem;
- candidate.resultType = nullptr;
+ OverloadCandidate candidate;
+ candidate.flavor = OverloadCandidate::Flavor::Generic;
+ candidate.item = baseItem;
+ candidate.resultType = nullptr;
- AddOverloadCandidate(context, candidate, kConversionCost_None);
- }
+ AddOverloadCandidate(context, candidate, kConversionCost_None);
}
+}
- void SemanticsVisitor::AddGenericOverloadCandidates(
- Expr* baseExpr,
- OverloadResolveContext& context)
+void SemanticsVisitor::AddGenericOverloadCandidates(Expr* baseExpr, OverloadResolveContext& context)
+{
+ if (auto baseDeclRefExpr = as<DeclRefExpr>(baseExpr))
{
- if(auto baseDeclRefExpr = as<DeclRefExpr>(baseExpr))
- {
- auto declRef = baseDeclRefExpr->declRef;
- AddGenericOverloadCandidate(LookupResultItem(declRef), context);
- }
- else if (auto overloadedExpr = as<OverloadedExpr>(baseExpr))
- {
- // We are referring to a bunch of declarations, each of which might be generic
- for (auto item : overloadedExpr->lookupResult2)
- {
- AddGenericOverloadCandidate(item, context);
- }
- }
- else
- {
- // any other cases?
- }
+ auto declRef = baseDeclRefExpr->declRef;
+ AddGenericOverloadCandidate(LookupResultItem(declRef), context);
}
-
- Expr* SemanticsExprVisitor::visitGenericAppExpr(GenericAppExpr* genericAppExpr)
+ else if (auto overloadedExpr = as<OverloadedExpr>(baseExpr))
{
- // Start by checking the base expression and arguments.
-
- // Disable the short-circuiting logic expression when the experssion is in
- // the generic parameter.
- if (this->m_shouldShortCircuitLogicExpr)
+ // We are referring to a bunch of declarations, each of which might be generic
+ for (auto item : overloadedExpr->lookupResult2)
{
- auto subContext = disableShortCircuitLogicalExpr();
- return dispatchExpr(genericAppExpr, subContext);
+ AddGenericOverloadCandidate(item, context);
}
+ }
+ else
+ {
+ // any other cases?
+ }
+}
- auto& baseExpr = genericAppExpr->functionExpr;
- baseExpr = CheckTerm(baseExpr);
- auto& args = genericAppExpr->arguments;
- for (auto& arg : args)
- {
- arg = CheckTerm(arg);
- }
+Expr* SemanticsExprVisitor::visitGenericAppExpr(GenericAppExpr* genericAppExpr)
+{
+ // Start by checking the base expression and arguments.
- return checkGenericAppWithCheckedArgs(genericAppExpr);
+ // Disable the short-circuiting logic expression when the experssion is in
+ // the generic parameter.
+ if (this->m_shouldShortCircuitLogicExpr)
+ {
+ auto subContext = disableShortCircuitLogicalExpr();
+ return dispatchExpr(genericAppExpr, subContext);
}
- /// Check a generic application where the operands have already been checked.
- Expr* SemanticsVisitor::checkGenericAppWithCheckedArgs(GenericAppExpr* genericAppExpr)
+ auto& baseExpr = genericAppExpr->functionExpr;
+ baseExpr = CheckTerm(baseExpr);
+ auto& args = genericAppExpr->arguments;
+ for (auto& arg : args)
{
- // 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.
+ arg = CheckTerm(arg);
+ }
+
+ return checkGenericAppWithCheckedArgs(genericAppExpr);
+}
- auto& baseExpr = genericAppExpr->functionExpr;
- auto& args = genericAppExpr->arguments;
+/// Check a generic application where the operands have already been checked.
+Expr* 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))
+ // 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);
}
- for (auto argExpr : args)
- {
- if (IsErrorExpr(argExpr))
- {
- return CreateErrorExpr(genericAppExpr);
- }
- }
+ }
- // Otherwise, let's start looking at how to find an overload...
+ // 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;
- context.loc = genericAppExpr->loc;
- context.sourceScope = m_outerScope;
- context.baseExpr = GetBaseExpr(baseExpr);
+ OverloadResolveContext context;
+ context.originalExpr = genericAppExpr;
+ context.funcLoc = baseExpr->loc;
+ context.argCount = args.getCount();
+ context.args = &args;
+ context.loc = genericAppExpr->loc;
+ context.sourceScope = m_outerScope;
+ context.baseExpr = GetBaseExpr(baseExpr);
- AddGenericOverloadCandidates(baseExpr, context);
+ AddGenericOverloadCandidates(baseExpr, context);
- if (context.bestCandidates.getCount() > 0)
+ if (context.bestCandidates.getCount() > 0)
+ {
+ // Things were ambiguous.
+ if (context.bestCandidates[0].status != OverloadCandidate::Status::Applicable)
{
- // 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.
+ // 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...
+ // 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.
+ getSink()->diagnose(
+ genericAppExpr,
+ Diagnostics::unimplemented,
+ "no applicable generic");
- auto overloadedExpr = m_astBuilder->create<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);
+ return CreateErrorExpr(genericAppExpr);
}
else
{
- // Nothing at all was found that we could even consider invoking
- getSink()->diagnose(genericAppExpr, Diagnostics::expectedAGeneric, baseExpr->type);
- return CreateErrorExpr(genericAppExpr);
+ // 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 = m_astBuilder->create<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::expectedAGeneric, baseExpr->type);
+ return CreateErrorExpr(genericAppExpr);
+ }
}
+
+} // namespace Slang