Adding a new semantics attribute: must_specialize

Yes, you're right. The restriction you're talking about makes much more sense.

Note: using this attribute for something in the stdlib which may be called by a user is very dangerous. It might cause compile-time errors, depending on how it's used, or really hurt code-size.

Well, it really is both. Another way to think about it is, "generic callers must be explicitly marked must_specialize" and "if this is not the case, the compiler will fix it." It's mostly an implementation detail. I also thought it would be a clear way to articulate the idea that the restrictions that apply to functions marked as must_specialize also apply to their callers and their callers callers, etc. all the way up.

This is a really good question. The good news is that this should not affect ABI stability. Functions marked as must_specialize must be specialized, so a new function will be created. Additionally, after all the specializations of the must_specailize function have been created, the function will be deleted. It cannot be used anymore because that would violate its restrictions. This implicitly means that any functions marked with must_specialize must be internal/private functions. They cannot be exposed publically, and therefore, should not affect the ABI. Note: this rule applies to all functions marked as must_specialize, so, as described above, these restrictions will also be applied to any "generic" callers.

Looking back at my post, I didn't call out the "mustn't be public" rule explicitly. That's my bad, I should have stated that.

This is getting into some of the complicated problems discussed in the linked forum post. First, I want to be clear that I am only discussing function templates. Specializing, extending, and type-checking class templates are going to have a whole host of complications that we only began to discuss in that forum post. I think this is not the correct place to discuss those problems.

Second, let me clarify that this attribute will have nothing to do with the functions that we are able to import. At this point, the compiler will already have function declarations which may be called. The problem I'm trying to solve here is, how do we find the concrete types that are used to specialize those (already imported) declarations.

because it seems like we would need to also re-instantiate the Swift caller like a template in order to properly re-instantiate and re-type-check the new C++ template instance.

I think you are correct, and I think that's actually exactly what this attribute is doing. However, we don't need to re-type-check the Swift function that is calling the C++ template instance. Once we specialize the Swift function, then we will have all the concrete type substitutions. We can simply give Clang both the type substitutions and the template we want to instantiate and it will do the rest (create the new function and type check it). This is the same way we currently instantiate C++ function templates, it's just a matter of specializing the Swift functions first so that we know we'll have concrete template arguments that we can provide the C++ function template.

I think the problem you're getting at is the problem we spent so much time discussing in the other forum post: doing what I'm suggesting here means we lose the beauty of Swift generics. In other words, for C++ function templates, we won't have the ability to constrain generic arguments properly. I.e., if there was a C++ function template that added two template parameters and a generic Swift function which called the C++ function template, the Swift generic types would not be required to conform to AdditiveArithmetic. This means, if the substituted types weren't addable, then we'd get a compile-time error from Clang complaining about it. This is substantially worse because it introduces the "10 calls deep" instantiation errors we all know and hate.

Unfortunately, I think this is a bit of an inherent problem with the C++ language. As Dave and I discussed in the other forum post, we can probably "solve" this to a certain degree with class templates. But I'm not sure there's a way for us to fix this problem regarding function templates. Even if we did want to somehow automatically generate protocols to constrain C++ function template type arguments (which may actually be possible), I think that would be something that would require lots of work, and would probably better be done as an improvement down the road. What I'm proposing is a fairly simple solution, while maybe not ideal, I think it will get the job done quickly. We can always change it in the future.

@dabrahams what do you think?

1 Like