summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-constraint.cpp
diff options
context:
space:
mode:
authorTheresa Foley <10618364+tangent-vector@users.noreply.github.com>2022-09-20 12:37:33 -0700
committerGitHub <noreply@github.com>2022-09-20 12:37:33 -0700
commit5ac7ba2c6d3405f1a59f4350c753ec990af8f6dc (patch)
tree13aba4c8e57cd5cbe6e3859bea130a8091c0a13a /source/slang/slang-check-constraint.cpp
parent8e44968be297c0fa0ab00510a5e5922630d8c401 (diff)
Support partial inference of generic arguments (#2404)
A commonly requested feature is to be able to supply only some of the arguments to a generic explicitly, while allowing the rest to be inferred. A common example is a function that performs some kind of conversion: To convert<To, From>( From fromValue ) { .... } A user would like to be able to call this operation like: int i = convert<int>( 1.0f ); but the current Slang type checker requires all or none of the generic arguments be supplied. Supplying all of the arguments is tedious: int i = convert<int, float>( 1.0f ); In this case, the `float` type argument is redundant and could be inferred from context. However, if the user tries to omit the generic argument list: int i = convert( 1.0f ); The current type-checker cannot infer the `int` type argument (even if one might claim it *should* infer based on the desired result type). This change adds support for the `convert<int>(...)` case, by allowing a generic to be applied to a prefix of its explicit arguments, and then inferring the remaining arguments from contextual information when that "partially applied" generic is applied to value-level arguments. Most of the changes are just plumbing: adding the notion of a partially applied generic and then supporting them during overload resolution. A single test case is included that covers the `convert`-style use case. It is likely that more testing is needed to cover failure modes of this feature.
Diffstat (limited to 'source/slang/slang-check-constraint.cpp')
-rw-r--r--source/slang/slang-check-constraint.cpp77
1 files changed, 71 insertions, 6 deletions
diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp
index 129d3ed0c..24cedd7d5 100644
--- a/source/slang/slang-check-constraint.cpp
+++ b/source/slang/slang-check-constraint.cpp
@@ -263,9 +263,10 @@ namespace Slang
return nullptr;
}
- SubstitutionSet SemanticsVisitor::TrySolveConstraintSystem(
- ConstraintSystem* system,
- DeclRef<GenericDecl> genericDeclRef)
+ SubstitutionSet SemanticsVisitor::trySolveConstraintSystem(
+ ConstraintSystem* system,
+ DeclRef<GenericDecl> genericDeclRef,
+ GenericSubstitution* substWithKnownGenericArgs)
{
// For now the "solver" is going to be ridiculously simplistic.
@@ -290,14 +291,60 @@ namespace Slang
return SubstitutionSet();
}
SubstitutionSet resultSubst = genericDeclRef.substitutions;
- // We will loop over the generic parameters, and for
- // each we will try to find a way to satisfy all
- // the constraints for that parameter
+
+ // Once have built up the full list of constraints we are trying to satisfy,
+ // we will attempt to solve for each parameter in a way that satisfies all
+ // the constraints that apply to that parameter.
+ //
+ // Note: this is a very limited kind of solver, in that it doesn't have a
+ // way to make use of constraints between two or more parameters.
+ //
+ // As we go, we will build up a list of argument values for a possible
+ // solution for how to assign the parameters in a way that satisfies all
+ // the constraints.
+ //
List<Val*> args;
+
+ // If the context is such that some of the arguments are already specified
+ // or known, we need to go ahead and use those arguments direclty (whether
+ // or not they are compatible with the constraints).
+ //
+ Count knownGenericArgCount = 0;
+ if (substWithKnownGenericArgs)
+ {
+ knownGenericArgCount = substWithKnownGenericArgs->getArgs().getCount();
+ for (auto arg : substWithKnownGenericArgs->getArgs())
+ {
+ args.add(arg);
+ }
+ }
+
+ // We will then iterate over the explicit parameters of the generic
+ // and try to solve for each.
+ //
+ Count paramCounter = 0;
for (auto m : getMembers(genericDeclRef))
{
if (auto typeParam = m.as<GenericTypeParamDecl>())
{
+ // If the parameter is one where we already know
+ // the argument value to use, we don't bother with
+ // trying to solve for it, and treat any constraints
+ // on such a parameter as implicitly solved-for.
+ //
+ Index paramIndex = paramCounter++;
+ if (paramIndex < knownGenericArgCount)
+ {
+ for (auto& c : system->constraints)
+ {
+ if (c.decl != typeParam.getDecl())
+ continue;
+
+ c.satisfied = true;
+ }
+ continue;
+ }
+
Type* type = nullptr;
for (auto& c : system->constraints)
{
@@ -334,6 +381,24 @@ namespace Slang
}
else if (auto valParam = m.as<GenericValueParamDecl>())
{
+ // If the parameter is one where we already know
+ // the argument value to use, we don't bother with
+ // trying to solve for it, and treat any constraints
+ // on such a parameter as implicitly solved-for.
+ //
+ Index paramIndex = paramCounter++;
+ if (paramIndex < knownGenericArgCount)
+ {
+ for (auto& c : system->constraints)
+ {
+ if (c.decl != typeParam.getDecl())
+ continue;
+
+ c.satisfied = true;
+ }
+ continue;
+ }
+
// TODO(tfoley): maybe support more than integers some day?
// TODO(tfoley): figure out how this needs to interact with
// compile-time integers that aren't just constants...