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

Specify and implement function subtyping. #57

Open
seanbaxter opened this issue Sep 18, 2024 · 4 comments
Open

Specify and implement function subtyping. #57

seanbaxter opened this issue Sep 18, 2024 · 4 comments

Comments

@seanbaxter
Copy link
Collaborator

seanbaxter commented Sep 18, 2024

#feature on safety

template<typename T>
void call(T);

int main() {
  void(*pf)(int^/static) = addr call<int^/static>;
}
@seanbaxter
Copy link
Collaborator Author

The fix for this requires a kind of function subtyping. The call function isn't aware of the static lifetime constraint on T. It would need to specify /(T : static)(T) for that. However the caller knows the constraint. The function from the caller's perspective is more constrained than the function from the callee's perspective, making it a subtype.

When specializing class templates the lifetime arguments of the template arguments are factored out and appended as lifetime arguments of the specialization. A similar thing has to be done here. That should make the type of the rhs the same as the type of the lhs.

@seanbaxter seanbaxter changed the title Compiler: fix lifetime canonicalization bug Specify and implement funtion subtyping. Sep 19, 2024
@seanbaxter seanbaxter changed the title Specify and implement funtion subtyping. Specify and implement function subtyping. Sep 19, 2024
@seanbaxter
Copy link
Collaborator Author

seanbaxter commented Sep 19, 2024

Class template specializations where template parameters are lifetime binders (at least in the T+ variety) are themselves lifetime binders. This allows us to hoist out the lifetime arguments of the template arguments and make them lifetime arguments of the specialization.

Currently, function types are not lifetime binders. This probably has to change to support subtyping. I don't know about stripping of lifetime arguments in this case.

template<typename T>
void call(T);

int main() {
  void(*pf)(int^/static) = addr call<int^/static>;
}

cannot convert lvalue int^/SCC-0(int^/SCC-0) to int^/static(*)(int^/static)

If the rhs can be converted to the lhs type by subtyping, can you still strip the lifetime arguments off the rhs? That would imply you can also strip them off the lhs.

I see three possible ways to treat this subtyping:

  1. Out of the box, call<int^/static> doesn't get any /static lifetime argument. It's just discarded. The result type is int^/SCC-0(int^/SCC-0) where SCC-0 is not a static SCC. The subtyping occurs during standard conversion to the lhs pointer. The lhs is more constrained than the rhs so standard conversion may apply. This is the equivalent adding a /(SCC-0 : static) to the function's type. The downside is that it is confusing to just discard the /static. It could be converted to a function pointer with some non-static lifetime argument.
  2. Out of the box, call<int^/static> is decorated with the /static lifetime argument. This is like how class specializations work. I'm not liking it.
  3. Out of the box, call<int^/static> is reparameterized with the /(SCC-0 : static) constraint as described in option 1. The advantage is that it prevents conversion to a function pointer type with unrelated lifetime arguments.

Option 3 seems most desirable. Placeholder lifetimes in the template arguments would create no additional constraints. But I don't know yet how to deal with non-static lifetimes, such as named lifetimes. If I used the actual named lifetime for defining a function type, incorporating the hash of its lifetime_param_t*, how would canonicalization work? That would seemingly take us back to the start of the problem: we've erased the specific lifetime argument and made a type that's more generic than the one requested by the user.

@seanbaxter
Copy link
Collaborator Author

#feature on safety

template<typename T>
T call(T);

int^(*pf)(int^) = call<int^>;

build_sep_19_2024-1 implements lifetime normalization on function type declarators, which allows this assignment to work. This should open the door for real subtyping during standard conversions between function types.

@seanbaxter
Copy link
Collaborator Author

See:
#61

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant