-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
cmd/compile: package-scope and function-scope types get the same name #38893
Comments
@ianlancetaylor I need to warn you that you are treading into some messiness. Consider this:
In the above example, there are two different types named myint64 which both refer to a int64, but closure1 can only take in the first type of myint64 and closure2 can only take in the second. That's when they both refer to the same underlying type. Now consider what can happen if they refer to different underlying types:
It gets worse. Each inner scope can produce types declarations that have the same name (but different underlying Kind) than the declaration in the outer scope. You can have a closure in a closure in a closure and each inner closure can define myint64 with a different underlying Kind than the outer closure did, like so:
GoLang's type system obviously has some holes. I think the issue is that the type of a variable (ex. myint64) doesn't actually tell you the Kind that the variable points to. Like you can have four different variables all of type myint64 and they can all have different Kinds. Maybe put the Kind in the type of the variable. Like instead of %T printing Either that or throw a deprecation warning at compile time if people declare a new type with the same name as an existing type. But allowing the inclusion of
When it prints the % it can print the entire |
@ianlancetaylor This example below should not compile. It's too confusing. If two declared types have the same package, name, and Kind, they should be interchangeable or the code should not compile.
|
I don't see why you say that Go's type system has holes. Don't confuse the name of the type as printed by %T or as used by the reflect package with the actual type. As the reflect documentation already says, there are cases where different types can have the same name (see the Your example in #38893 (comment) works as the language spec describes. Each |
I misspoke. It's a little unintuitive is all. Now before I continue, I'd like to take a little digression. Take this very mundane Go code snippet:
Coming from other programming languages like Kotlin and Scala, I expected that code to express the same intent as this code:
That being said, after learning about type declarations, I now see that this Go code means something fundamentally different. What it means is that the type With that in mind, let's take a look at this example code:
Now as a beginner this is a little unintuitive, but given that Go was created by the creator of C, it makes some sense. Notice that when If your code compiles and you have one type declaration on line 10 and another one with the same name on line 20, everything between line 10 and line 20 with that name gets the first type and everything after line 20 gets the second type, regardless of the presence of loops, closures, goto, etc, and regardless of whether the two type declarations refer to the same Kind or different Kinds. Oh, and then the compiler says "Cannot use type MyType as type MyType", and when you try to cast your variable to MyType you can't because your variable actually needs to be cast to the MyType on line 10, not the MyType on line 20, which isn't obvious from the error message. At the very least type declarations should give their enclosing scope (maybe more - in Scala types have a whole path). The types are static so it shouldn't be super hard to get that information. Like if there is one MyType in package scope and another in function foo, the first could be Also, if the whole path with enclosing scope could show in the output of reflect.Type.String() and in the compiler error message, that would make things less unintuitive. Less "which MyType is it talking about?" and no "I can't cast this because it's shadowed by some other MyType in another scope." Like if you could explicitly cast Also, if it were my programming language I would require types to have unique fully qualified names. Like even in Scala I don't like when I do a type path or type projection for the type of a class inside another class (ex. |
Correction:
Yeah, this is horrible, but at least you can access singletons within singletons and types within types in Scala (as long as they're not shadowed). In Go if a type was defined and used inside of a function and that function has been returned from, that type is totally inaccessible regardless of where in the function it was defined or whether it was declared exported or unexported. I have no expertise whatsoever, but I like the idea of being able to access an exported, declared type via a unique path like: package.function.$closure.innerFunction.$scope.Type, where $closure is an unnamed closure, $scope is an unnamed scope, and Type is the declared type that I am referring to. If all the declared types had unique paths, that would be more intuitive. Then I could think of the types like being in a tree. |
Getting back to @ianlancetaylor's original question:
I don't see a need to. I think we can wait until we have a report of a real-world use case where this would be useful. |
@ianlancetaylor , would it be okay to bump this to Unplanned? |
I think so, yes. |
It's true that I think this should be in Backlog, not Unplanned, and I will change it accordingly. |
I don't know if |
I think this issue is blocked waiting on the proposed user-visible changes. For example: Why are unique strings needed? Why can't types.Type values be disambiguated directly with How should the strings be further disambiguated? How stable do the disambiguators need to be? What changes need to be made to the reflect API? These will need to go through the proposal approval process. |
In my case it's because I need to relate a
I just came across #55924 which seems relevant - having some way of accessing the link name for any type might just do the trick. Though that's assuming that the way the link name is constructed is documented and could be calculated deterministically from the AST of a package. For some more context on my specific issue, given a |
This programs shows that we can get two different types with the same name. This is permitted by the reflect package, but it seems unnecessarily confusing. Should the compiler generate different names for these types?
Output:
The text was updated successfully, but these errors were encountered: