diff options
| author | Theresa Foley <10618364+tangent-vector@users.noreply.github.com> | 2022-09-20 12:37:33 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-20 12:37:33 -0700 |
| commit | 5ac7ba2c6d3405f1a59f4350c753ec990af8f6dc (patch) | |
| tree | 13aba4c8e57cd5cbe6e3859bea130a8091c0a13a /source/slang/slang-check-constraint.cpp | |
| parent | 8e44968be297c0fa0ab00510a5e5922630d8c401 (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.cpp | 77 |
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... |
