Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support where clause and type equality constraint. #4986

Merged
merged 11 commits into from
Sep 5, 2024

Conversation

csyonghe
Copy link
Collaborator

@csyonghe csyonghe commented Sep 3, 2024

Closes #4890.

This PR adds syntax for where clause and extended the generic syntax so that it can work more nicely with codebases that wish to share with C++.

Generics can now be defined as:

__generic<typename T, int N>
struct Vector {}

Which is the same as struct Vector<T, let N:int> or struct Vector<T, int N>.

Generic constraints can now be defined as trailing where clauses:

struct S<T> where T:IInteger {}

In addition, this PR brings support for type equality constraints:

interface IFoo { associatedtype A; }

struct S<T>
   where T : IFoo
   where T.A == int   // T.A must be int
{
     int a = T.A(4); // allowed, because T.A is known to be equal to int.
}

@csyonghe csyonghe added the pr: non-breaking PRs without breaking changes label Sep 3, 2024
@csyonghe csyonghe changed the title Support where clause. Support where clause and type equality constraint. Sep 3, 2024
tangent-vector
tangent-vector previously approved these changes Sep 5, 2024
Copy link
Contributor

@tangent-vector tangent-vector left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks really good.
As discussed elsewhere, one big category of work that keeps this from being truly feature-complete is that we don't yet have a step to canonicalize the constraints on a generic declaration, which will both be important to producing consistent mangled names, and also required to detect whether or not two generic function signatures are equivalent (and should be considered redefinitions/redeclarations), or not (in which case they should be treated as distinct overloads).

// If this decl is defined in a where clause, store the source location of the where token.
SourceLoc whereTokenLoc = SourceLoc();

bool isEqualityConstraint = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It really feels like the kind of constraint should be captured as distinct subclasses of GenericTypeConstraintDecl. In the simplest case we'd just have TypeConformanceConstraintDecl and TypeEqualityConstraintDecl.

A more refined version of things (down the line) might be to have a distinct hierarchy to represent relations on types (e.g., TypeConformanceRelation, TypeEqualityRelation, SubtypeRelation, etc.), and then have a GenericConstraintDecl simply store the relation (or other proposition) it requires to be true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about having separate subclasses for GenericTypeConstraintDecl but that will lead a much bigger change.

Having a single GenericTypeConstraintDecl with a getType() that returns the relation type seems easier to do.

source/slang/slang-ast-iterator.h Outdated Show resolved Hide resolved
Comment on lines +906 to +907
inline bool isTypeEqualityWitness(Val* witness)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have the model proposed above where we use a distinct hierarchy for type relations, then those relations (and other propositions) would be the type of values that satisfy them. In that case, the isTypeEqualityWitness(w) query would amount to as<TypeEqualityRelation>(w->getType()). That change would enable this query to also return the type-equality relationship that w is witnessing, which clients of this query probably want to know.

source/slang/slang-check-impl.h Outdated Show resolved Hide resolved
Comment on lines +294 to +297

// A placeholder witness for the fact that two types are equal.
INST(TypeEqualityWitness, TypeEqualityWitness, 2, HOISTABLE)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we get around to being more formal with our type system, we may find that we need more nuance here and a type equality witness isn't just a no-op "placeholder."

It really comes down to whether we are talking about identical types, or just equivalent types. If it is possible in our system to have two types T and U where each is a subtype of the other, but they are not identical, then those types are equivalent, and a witness to their equivalence would basically amount to a pair of witnesses: one for each of the subtype relations involved. Those subtype witnesses would in turn but usable for converting a value of type T into one of type U, and vice versa, which might not otherwise be possible for the IR codegen to synthesize.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because inheritance isn't really a thing right now in the language, such case won't arise. When we have general support of inheritance, then equivalence vs equality becomes relevant and we will need more formality about it.

source/slang/slang-lower-to-ir.cpp Outdated Show resolved Hide resolved
@csyonghe csyonghe merged commit d655302 into shader-slang:master Sep 5, 2024
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr: non-breaking PRs without breaking changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support where clause for defining generic constraints.
2 participants