-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Compiler error when extending a typealias of a partially specialized generic type #68212
Comments
I would like to solve this problem, It will be my first open source contribution. |
Sounds good. Thanks. |
@dochoi-bot Does the following command make any difference in the terminal?
If not, you may have interrupted the build script before it finished building the standard library. You can build it by rerunning your build script invocation, or by running |
Thanks to you, I solved it:) |
@dochoi-bot are you still working on this issue? |
@saehejkang I tried it, but it's still unresolved. |
yes please, I would like to work on it @AnthonyLatsis could you assign me to this issue? I was looking into this issue and am kind of stumped on where to begin. I was able to reproduce the issue and make updates to the |
@AnthonyLatsis spending a good amount of time on this issue and I am pretty lost with how to tackle this. Any hints/good starting points to start understanding what needs to be done? I feel I need to make some sort of change in this file here |
You’re not wrong, but besides adjusting the logic in this place, just as |
I read the blog by Slava wrote and it was really helpful!!! Understanding how the compiler builds the I just wanted to walk through a piece of code to better familiarize myself with what is happening. // Check if you can take the extended type and get it as the the generic type Field<Tag, Value>
if (auto *unboundGeneric = extendedType->getAs<UnboundGenericType>()) {
// Check if the typealias IntField is allowed with the generic that was declared
if (auto *aliasDecl = dyn_cast<TypeAliasDecl>(unboundGeneric->getDecl())) {
// get the underlying type of the typealias, which would be Field<Value, Int>
auto underlyingType = aliasDecl->getUnderlyingType();
} The next piece is a little tricky but I think I understand it (correct me if I am wrong). if (auto extendedNominal = underlyingType->getAnyNominal()) {
return TypeChecker::isPassThroughTypealias(
aliasDecl, extendedNominal)
? extendedType
: extendedNominal->getDeclaredType();
} We are setting the
The blog I read talked about what it means to be a extension Field where Value == Int {
func adding(_ value: Int) -> Self {
Field(tag: tag, value: self.value + value)
}
} Do we now have to add a check for the |
If you’ve dealt with generic parameters and generic |
The use of the syntactic sugar That is why this piece of code compiles. We are simply not using the syntactic sugar of the extension Field where Value == Int {
func adding(_ value: Int) -> Self {
Field(tag: tag, value: self.value + value)
}
} |
Criteria for a
if (!nominal) return false; // typealias defined
typealias IntField<Tag> = Field<Tag,Int>
// it refers to the nominal type below
struct Field<Tag,Value> {
let tag: Tag
let value: Value
}
if (nominal->isGeneric() != typealias->isGeneric())
return false; This is true for both the
if (static_cast<bool>(nominalSig) != static_cast<bool>(typealiasSig))
return false; This is true because if they are both generic, then a generic signature exists
if (!nominalSig) return true; Just checking one of the values to see it is not generic
auto nominalGenericParams = nominalSig.getGenericParams();
auto typealiasGenericParams = typealiasSig.getGenericParams();
if (nominalGenericParams.size() != typealiasGenericParams.size())
return false;
.......... This is a check for
if (!typealias->isGeneric()) return true; Another simple check to see if the variable is generic
(bound_generic_struct_type decl="main.
(generic_type_param_type depth=0 index=0 decl="main.(file).IntField.Tag
(struct_type decl="Swift.(file).Int"))
(bound_generic_struct_type decl="main.(file).Field
(generic_type_param_type depth=0 index=0 decl="main.(file).Field.Tag
(generic_type_param_type depth=0 index=1 decl="main.(file).Field.Value If the check ever gets this far, simply compare the two types if they are the same. |
So should I be adding a new 😰 |
Check if the if(aliasDecl->getUnboundGenericType()->isEqual(extendedType)) {
return extendedType;
} Make this check inside the lines of |
That’s a more informed thought, which is good. A new function is a good idea in itself. Why only
Not sure if this is a misconception or just colloquial language. There is a nominal type declaration (
Not arguments — parameters. Arguments are values that are substituted into parameters.
Do you understand how this differs from
This sounds rather ambiguous. Are you sure you understand what this means? Would you be able to explain it with Swift examples?
Have you tried reasoning about the scope of this condition? It works for this particular case, but what is a general example of Swift code that satisfies it the aforementioned context? |
Swift Example struct Field<Tag,Value> {
let tag: Tag
let value: Value
} typealias IntField<Tag> = Field<Tag,Int>
The The In this Swift example, both have
Using the Swift example above, there are two
Going deeper, there is a check for each
This is what is being compared ^ at a deeper level.
The scope of this check does not seem right because the |
This is correct, but the
Again, that you understand what is happening here is good, but how does this map to source code? What is a Swift example where the size matches, but the element-wise comparison fails?
Ditto, what is a Swift example where this is true/false? Just reading these The current goal is to formulate the criteria relative to Swift source code, without using internal terms or mentioning implementation details. |
Swift Example for nominal is not generic but typealias isstruct Field {
let tag: Int
let value: Int
}
typealias IntField<A, B> = Field
extension IntField {
func adding(_ value: Int) -> Self {
Field(tag: tag, value: self.value + value)
}
} The Swift Example for genericSignatureCheckThis check is kind the same as the Swift Example for size matches but element wise comparison failsstruct Field<A, B> {
let tag: Int
let value: Int
}
typealias IntField<Tag, Value> = Field<Tag, Int>
extension IntField {
func adding(_ value: Int) -> Self {
Field(tag: tag, value: self.value + value)
}
} The These are the params being compared below and they are different.
Swift Example for comparing underlying type with self interface typeGonna be honest, I can't think of an example that gets to this final check and returns true. Why do I feel like this is where the check needs to get updated to account for solving this issue 🤔 . struct Field<A, B> {
let tag: Int
let value: Int
}
typealias IntField<A, B> = Field<A, B>
extension IntField {
func adding(_ value: Int) -> Self {
Field(tag: tag, value: self.value + value)
}
} Even with the Swift example above, the
|
This is not true.
This is also not true.
Are you sure they are different? Then how come
Let’s get back to this one once we have a clear understanding of the rest. |
I worded this thought incorrectly. I was speaking about the two checks made and what THOSE evalutate to.
I can see now with the example, how one of them can have a generic signature and the other can't have one either.
Completely realized I posted an example of it passing. Even after taking some time thinking about it, I can't think of an example where the sizes are the same but they don't pass. I can only think of examples where the sizes are not the same and that is why it fails. |
Ah, I wasn’t clear enough. What I meant was a Swift example where |
I can't think of an example where both If they pass the first check, then we know that both are either generic or both are not generic. Now with the check for the genericSignature, we know that both will either have one or both will not have one. To me, it seems like if any of them are generic, then a genericSignature exists. Are the genericParams being built based off the genericSignature? I read about it somewhere in here |
If by generic you mean it has a generic parameter list, then indeed there is no such valid example. Hint: Calling struct Outer<X> {
struct Inner {}
}
No, a generic parameter list is either parsed from source or derived from the generic parameter list of an associated declaration. Protocols are an exception: the generic parameter list of a protocol is derived from the protocol itself and has a fixed format — |
I believe it is |
So, does this light a bulb in regards to a Swift example? |
The example you showed above with a The example below does not pass the TLDR, I still don't see how struct S<X> {
struct Inner {};
};
typealias Alias<T> = S<T>.Inner
extension Alias {}; Thank you for being patient with me |
Sorry for the delay.
That may be because the following two propositions contradict each other. Which one is right?
|
I think that the That is why I don't see any other way to get past the struct S<X> {
struct Inner {};
};
typealias Alias<T> = S<T>.Inner
extension Alias {}; In theory, the example above will cause both of the lines below to evaluate to
|
Ah, sorry, I ought to have been more clear. The goal is just to come up with an example of a type alias that points to a nominal type declaration where these conditions evaluate to false and true respectively, not one that necessarily reaches both
Why would |
It would not evaluate to |
Alright. You’ve argued that |
This makes sense to me.
I see how if neither have a generic param list then they both are not generic, thus the check above evaluates to |
It depends. As we found out earlier, a nominal type declaration without a generic parameter list is not necessarily bereft of a generic signature. |
I am going to take a pause on this. If anyone else comes along and wants to work on this please unassign me and give it to them. |
I would like to work on this if you don't mind. @saehejkang @AnthonyLatsis I was able to reproduce this issue and see the signature with the -debug-generic-signatures flag. Why is the check isPassThroughTypealias check needed here after all? After commenting the complete "Hack to allow extending a generic typealias", extending the generic typealias throws an error in the code as expected. I believe when the hack is resolved to a reasonable check, we can also fix this issue with ease. In order to tackle this better I am required to debug the code using LLDB. I have not yet figured out how to run my example swift file and start the LLDB. If you could refer me to any tuturial, would be appreciated. This is how I ran my swift file: |
Thanks for starting the CI for me @AnthonyLatsis. It comes down to two failing tests:
(1) This outputs two new error messages in line 427, which is setup to fail anyway. I did not go further in analyzing this one. (2) ultimately Foo was typealiased to line IntFoo 25: declaring x to be of type Int. In line 33 x is defined to be "test" which is a String. Furthermore I setup LLDB and I will start playing around with it as well. |
Investigating further into the specialize.swift test, I figured there is the where clause. The where clause sets which of the two arguments is to be inferred. I am wondering if this where clause resolution is made by the type through alias. I would like to extract/split the type through alias function in order to keep the where clause functionality working while also fixing this bug. Am I still on track? |
Digging deeper I have stumbled upon this issue which was linked in decl/ext/specialize.swift: #47452 Back to requirement_inference (1) |
Description
When creating a typealias that partially specializes a generic type, the compiler generates an error when attempting to extend that type via the typealias.
Steps to reproduce
Create a Generic type with two type parameters.
Define a typealias which specializes one of the type parameters as such :
typealias IntField<Tag> = Field<Tag,Int>
Define an extension based on the typealias:
Expected behavior
I would expect this code to compile.
Instead the compiler complained: "Binary operator '+' cannot be applied to operands of type 'Value' and 'Int'"
If instead of extending the typealias, I extend the type directly it compiles fine as such:
Environment
The text was updated successfully, but these errors were encountered: