-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Compiler error when extending a typealias of a partially specialized generic type #68212
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
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)
}
} |
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) |
@xavgru12 Are you still interested in pursuing this? I doubt that simply circumventing |
@AnthonyLatsis Thanks for your reply. I am still interested in resolving this. I removed the line of code in order to check which consequences it has. Do any of my comments make sense? Your comment „we do not support extensions of most other generic type aliases“ seems to be an important point. Can you guide me into the correct direction? |
To give you an idea of why we currently cannot properly extend just any generic type alias: struct Foo<T> {}
typealias A<T> = Foo<(T, T)>
extension A {} We need a generic extension to represent this, which we do not support: extension <U> Foo where T == (U, U) {}
|
@AnthonyLatsis #1 #2 I need to fix this issue in main, even if it was found in 5.8.1, right? #3 Earlier you said that modifying the function isPassThroughTypealias is too #4 extending the definition This
is nominal since Tag and Value do not have a specific type like Int. Whenever in typealias a type like Int is inferred, I noticed the difference in the Generic signature: typealias: my possible next steps would be of this kind: Other than the size check from line 3018, are there any other checks inside isPassThroughTypealias that I need to think about? I am waiting for feedback so I do not walk into the wrong direction. |
@AnthonyLatsis I do not think my @ works if I edit my post. Anyways to make it more compact: I am trying to compile the code in debug. ./utils/build-script --debug Is there any way to remove optimizations for lib/Sema or even only in that one file without messing with cmake? |
I am almost positive our tests exercise all the code paths in
Right. We cannot back-deploy a fix for this. Either way we primarily want to land a fix on main.
The point was to take one step at a time.
A nominal in this context is a nominal type declaration ( Are you following docs/HowToGuides/GettingStarted.md? What is the error? You cannot configure the build variant per library without messing with the generated build rules. |
@AnthonyLatsis thanks for the reply and for bearing with me
I think we need to separate means of getting there from what actually solves the issue as this confuses me a lot of the time
Instead of release with debug info I wanted to build with optimizations off. I thought --debug-swift does this job, yet it does not compile so I continue using release with debug info. I saw inside the build folder a compile_commands.json is generated. If I turn off optimizations for this source file, that might already solve that. However I assume that this file gets overwritten with every cmake configure and I am unsure if and when cmake configure is called from the build script. Back to solving the issue: My understanding is we need to extend the checks so this case is returns true and therefore extendedType is returned instead of nominal->getDeclaredType() I need to check if the type inferred by typealias goes from generic to non generic (Int). I see that there is a method called isGeneric(). I just need the right two objects to call isGeneric() on. If so, return true and that is it. |
--debug-swift compile error is this one at #78815 |
I stepped through isTypeThroughAlias
one difference that NumGenericParams went from 2 to 1. This is exactly what I expected since the inferred type Int is non generic. I need to further explore the structure CanonicalSignatureOrASTContext. I need to learn how to print values and nested values using only the lldb shell. My goal is to compare the typealias with the struct, check if the inferred argument changed from generic to non generic so I return true and therefore return the extendedType. |
Yes.
The generic signature here is a property of the declaration.
Many of our classes, including |
I printed the typealias with this:
The nominal has the two generic parameters:
The typealias has the Int which is inferred and the other generic parameter:
I would like to iterate over the parameters of nominal and typealias and check if the inferred parameter changed from generic to non generic. I am struggling to find a way to iterate over the parameters. Is there any method/function which does what I am looking for? |
You can downcast the type objects to |
Thank you for your help!
This checks if the type changed from generic to struct and if so, it immediately returns true. For that I would need to iterate over the arguments and check if the type changed to something unsupported. I would like to use std::is_same<decltype(obj1), decltype(obj2)>::value for that. Let me know if I am still on track. Also: (lldb) p std::is_same<int, int>::value |
I fixed the failing unit test. The github issue says that the test is supposed to fail anyway, but for another reason with another error message. I also added the unit test for the now supported case. |
@AnthonyLatsis Could you have a look? |
@AnthonyLatsis |
Uh oh!
There was an error while loading. Please reload this page.
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: