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

Inline classes #103

Merged
merged 4 commits into from
Jul 13, 2018
Merged

Inline classes #103

merged 4 commits into from
Jul 13, 2018

Conversation

zarechenskiy
Copy link
Contributor

@zarechenskiy zarechenskiy commented Apr 4, 2018

Discussion of this proposal is held in this issue.

@zarechenskiy zarechenskiy mentioned this pull request Apr 4, 2018
- String enum for Kotlin/JS (see [WebIDL enums](https://www.w3.org/TR/WebIDL-1/#idl-enums))
- Units of measurement
- Result type (aka Try monad) [KT-18608](https://youtrack.jetbrains.com/issue/KT-18608)
- Inline property delegates
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that this and the next item need clarification/some examples


- Inline class must have a public primary constructor with a single value parameter
- Inline class must have a single read-only (`val`) property as an underlying value, which is defined in primary constructor
- Underlying value cannot be of the same type that is containing inline class
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say, having undefined (recursively defined), e.g. generics with an upper bound equal to the class should be prohibited as well.
Or like this

inline class Id<T>(val x: T)
inline class A(val w: Id<A>)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with restriction about recursively defined generics, thanks!

About the example, it's OK if we'll map Id<T> always to Any, I'll add a note about this

- Underlying value cannot be of the same type that is containing inline class
- Inline class cannot have `init` block
- Inline class must be final
- Inline class can implement only interfaces
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what happens with non-abstract interface methods.
Do we need to generate DefaultImpls in the case?

| **Reference type** | Underlying type | Underlying type | Wrapper type |
| **Nullable reference type** | Underlying type | Wrapper type | Wrapper type |
| **Primitive type** | Underlying type | Wrapper type | Wrapper type |
| **Nullable primitive type** | Underlying type | Wrapper type | Wrapper type |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, "wrapper" term is a bit ambiguous since underlying type is a wrapper too :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I think I'll replace it to a "Boxed type"

fun test(vararg foos: Foo) { ... } // should be an error
```

## Java interoperability
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also add some short description/examples of how these wrappers look like and where the methods' bodies are


Compiler will generate original `equals` method that is delegated to the typed version.

By default, compiler will automatically generate `equals`, `hashCode` and `toString` same as for data classes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, are data inline classes allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, they can be allowed... I'm not sure it makes sense, I'll add a note

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related - is there a reason sealed classes can't support inlining as long as every subclass is also inline?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hzsweers Since there's no instance, how would you do is checks at runtime for use in a when or boolean expression?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would assume that they would work the same way is checks would work in normal use (I didn't see that listed as a listed caveat and assumed that meant it was supported, though admittedly I don't see how)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect is check to not be supported on inline classes since they are identity-less, as said here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LouisCAD Could you please elaborate? Type check should work the same as it works for primitives.
<expr> is InlineClassType
There are two options:

  • type of expr is statically known to be an inline class type, then type check is trivial
  • type of expr is statically unknown, so if it's actually of an inline class type then it should be boxed, therefore we do type check for boxed type

Copy link
Contributor

@LouisCAD LouisCAD Apr 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zarechenskiy Does that mean that anytime you put an inline class value to a reference that is higher in the class hierarchy (like Any), it is boxed, therefore, no longer inline for this usage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LouisCAD Yes, same as for primitives

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can discuss this problem in the issue. We had some design about what you are thinking :)
But it has too much restrictions, you can check opaque types in Scala, for example


| Underlying Type \ Declaration Site | Not-null | Nullable | Generic |
| --------------------------------------------- | --------------- | ---------------- | ---------------- |
| **Reference type** | Underlying type | Underlying type | Wrapper type |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we decided that fun foo(x: List<Name>) here it compiles to List<Name> from Java POV?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll add an example

@@ -0,0 +1,163 @@
# Inline classes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a section about our arrays struggles

The following restrictions are related to the usages of inline classes:

- Referential equality (`===`) is prohibited for inline classes
- vararg of inline class type is prohibited
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why vararg is not allowed?

Copy link
Contributor

@gildor gildor Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general would be good to provide some basic comments about particular limitations/restrictons

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's not clear what type should it have. I'll add a section about arrays and then cover this question

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @zarechenskiy, can you expose some clarification about List<T>, Array<T> and vararg T?

Currently, inline classes must satisfy the following requirements:

- Inline class must have a public primary constructor with a single value parameter
- Inline class must have a single read-only (`val`) property as an underlying value, which is defined in primary constructor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it impossible to use var? Is it technical limitation or design decision?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it's both :)
Short answer is that because inline classes are identityless, they don't have state

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline class Name(var s: String)

val name = Name("Alan")
name.s = "Bob"

Is name read-only or not?
How estimate the impact of change the Name.s property in a shared boxed type?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any problems with single-property class which violates other statements?

inline class Something(t: T) {
    init {
        // some init, checks
    }
    val value: U = transform(t)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Miha-x64 This is the same as if we have private primary constructor + public secondary constructor, therefore we are going to prohibit such cases for now

Currently, there is no performant way to create wrapper for a value of a corresponding type. The only way is to create a usual class,
but the use of such classes would require additional heap allocations, which can be critical for many use cases.

We propose to support identityless inline classes that would allow to introduce wrappers for a values without additional overhead related

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "a values"


- Native types like `size_t` for Kotlin/Native
- Inline enum classes
- Int enum for [Android IntDef](https://developer.android.com/reference/android/support/annotation/IntDef.html)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this suppose to work?
Of course, you can create one inline class for each int, but it's not clear how this help with enum case.
Some kind enums based on inline classes or sealed would be really helpful tho
Are inline enums part of this proposal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe inline enum classes deserve another proposal, but I'll put here a bit information about them

- Inline class must have a public primary constructor with a single value parameter
- Inline class must have a single read-only (`val`) property as an underlying value, which is defined in primary constructor
- Underlying value cannot be of the same type that is containing inline class
- Inline class cannot have `init` block
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would init block be prohibited? Isn't it where checks for the single value parameter would take place?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't really check anything if at runtime the class isn't actually instantiated. Remember, it's inline after all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of Java interop. We can't ensure execution of init block when value comes from Java. Consider this case:

// file: sample.kt
inline class Positive(val u: Int) {
    init { assert(0 < u) }
}

fun foo(p: Positive) {} // in JVM bytecode `p` is just `int`

// Java
...
SampleKt.foo(-10) // it's OK from Java POV

Also, this is the reason for public primary constructor requirement.
I'll write about this in the proposal

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zarechenskiy Why not rule out giving the underlying value of an inline class in Java at compile time? Java would only be allowed to receive inline classes values as the underlying type, but only Kotlin would be allowed to invoke the inline "constructor".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this question should be discussed in the issue.
I see here several options:

  • we relax this requirement and have inconsistent behaviour
  • we introduce some kind of assertions as for nullable values that come from Java
  • use conversions for values that come from Java

Each of these options have cons, but it's worth to discuss it anyway

}
```

Compiler will generate original `equals` method that is delegated to the typed version.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also makes sense to generate component1 function.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider harmful expose the private value for the UInt example, or the component1 must have the same visibility of the value attribute

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please elaborate? Do you have a use case? Maybe it's a point to allow inline data classes

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right, auto component1 is useless.
Vice versa, it may be used by hand, for example:

inline class IntPair(private val value: Long) {
    operator fun component1() = value ushr 32
    operator fun component2() = value and 0xFFFFFFFF
}

}


inline class InlinedDelegate<T>(var node: T) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline class must have a single read-only (val) property as an underlying value, which is defined in primary constructor

There is an inconsistency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that this is an open question. We can support mutable inline classes if they will be used only with delegated properties. Currently we're looking for use cases

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @zarechenskiy,
having a delegate with a backing field is interesting, however rules exceptions are harder to explain and understand.

class Test {
void test() {
String name = SampleKt.simple();
List<Name> ls = Samplekt.generic(); // from Java POV it's List<Name>, not List<String>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Kotlin code Name is a parameter type.
In Java code Name is a return type.
Name is a parameter or a return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, indeed, Name and List<Name> should be used in return type position in Kotlin, thanks!

@@ -128,31 +213,79 @@ Since boxing doesn't have side effects as is, it's possible to reuse various opt
### Type mapping on JVM

Depending on the underlying type of inline class and its declaration site, inline class type can be mapped to the underlying type or to the
wrapper type.

boxed type.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotate each parameter using an explicit annotation with a boxed type can really improve code readability.
This code should be injected by compiler in the same manner of @Nullable annotation.

Copy link

@artem-zinnatullin artem-zinnatullin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome 👍 👍 👍

I have a draft of same KEEP, will do couple more rounds of review, so far looks fantastic!

```
- Inline class cannot have `init` block
- Inline class must be final
- Inline class can implement only interfaces

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explicitly clarify that inline class cannot extend other classes?

- Inline class cannot have backing fields
- Hence, it follows that inline class can have only simple computable properties (no lateinit/delegated properties)
- Inline class cannot have inner classes
- Inline class must be a toplevel class

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, that's pretty limiting, why is that?

Use-case: inside a class I want to use an inline class as an implementation detail, but I don't want other classes to know about it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this is too limiting, thanks!
It's definitely can be a nested member, I'll think more about it


The following restrictions are related to the usages of inline classes:

- Referential equality (`===`) is prohibited for inline classes

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why? Shouldn't it just do referential equality check on underlying object?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artem-zinnatullin That would be dangerous. Let's say two different inline classes both wrap an int which happens to have a value of 0. It would be considered equals reference while not representing the same thing at all?
Allowing reference equality would defeat the purpose of inline classes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍

I was thinking about it from Java perspective where I'll still be able to do reference comparison, I think it's fine to prohibit === from Kotlin side unless someone comes up with a good use-case.

fun test(vararg foos: Foo) { ... } // should be an error
```

## Java interoperability

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we need JS and Native interoperability sections as well :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we'll have it :)
For now, I'm going to put more information about common and expect/actual inline classes

Again, method `foo` have type `int` from Java POV, so we can indirectly create values of type `Positive`
even with the presence of private constructor.

To make behaviour more predictable and consistent with Java, we demand public primary constructor and restrict `init` blocks.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not allow init blocks and private constructors by enforcing boxed type when used from Java? This would work similarly to inline functions that are inline when invoked in Kotlin, but not when called from Java or the debugger.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this approach is nice, some kind of "closed world" for inline classes.
But note that inline functions cannot be virtual, while inline class types can be used everywhere. To enforce boxing for Java we'll have to duplicate methods, plus there are some issues with arrays (if we'll allow them, we'll have to box each element of an array on the border)

I'd say that we should think more about this approach, at least to understand our design space

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zarechenskiy It should not duplicate methods, but rather, add a method that takes the boxed type that is visible in Java, which unboxes the value and call the static method that takes the unboxed value.
In case the defined method is also inline, the method with the boxed type would contain all the logic directly, since no method for the unboxed type would be generated (since they would all be inlined at call places).

inline class A<T : A<T>>(val x: T) // error
```
- Inline class cannot have `init` block
- Inline class must be final
Copy link
Contributor

@LouisCAD LouisCAD Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would open inline classes not be allowed?
Couldn't they be extended by other inline classes that would only be additional API wrappers that could add more restrictions and more functions?
Such restrictions added to an inline class extending another, open one could be:

  • Narrowing down the underlying value to a child type of the one defined in the parent inline class
  • Adding additional checks/logic to init blocks (if they are allowed)
  • Overriding open methods from the parent inline class

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How runtime should resolve which subtype of inline class it deals with?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Miha-x64 The inline classes have a boxed (non inline) type for cases where you use generics or super types like Any, so either it can be done statically, without autoboxing, or it is boxed, so behaves like a regular class, until it reaches an auto-unboxing place again.

To make an analogy to something existing, it would work the same as primitive number types, who have their boxed types extending the Number class.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LouisCAD to assign Int value to a Number variable, you must box it, which eliminates the whole purpose of inline classes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Miha-x64 Does boxing an Int somewhere in your program eliminates the whole purpose of primitive, non boxed integers in your whole program? I don't think so, and the same applies for inline classes.
If you always use them in ways that force autoboxing, then, yes, its pointless, but you'll usually not write autoboxing code everywhere.


The following restrictions are related to the usages of inline classes:

- Referential equality (`===`) is prohibited for inline classes

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍

I was thinking about it from Java perspective where I'll still be able to do reference comparison, I think it's fine to prohibit === from Kotlin side unless someone comes up with a good use-case.


Compiler will generate original `equals` method that is delegated to the typed version.

By default, compiler will automatically generate `equals`, `hashCode` and `toString` same as for data classes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an interesting conflict with this section about equals/hashCode and implementing interfaces.

Let me quote:

Compiler will generate original equals method that is delegated to the typed version.

and

Inline class can implement only interfaces

Which means that comparing two different inline classes that use same underlying type and implement same interface can result in wrong behavior because of comparing underlying values which can be indeed equal.

Example:

interface Bytable {
    fun toBytes(): ByteArray
}

inline class UserId(private val id: String): Bytable {
    override fun toBytes(): ByteArray = "userId:$id".toByteArray()
}

inline class OrderId(private val id: String) : Bytable {
    override fun toBytes(): ByteArray = "orderId:$id".toByteArray()
}

fun f(bytable1: Bytable, bytable2: Bytable) {
    assert(bytable1 == bytable2)
}

fun test() {
    f(bytable1 = UserId("123"), bytable2 = OrderId("123"))
}

fun main(args: Array<String>) {
    test()
}

If you change inline classes to data classes, test will fail.
But I think it'll pass with inline classes which is interesting.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm intrigued as to how exactly unsigned types will work under this proposal.

I can see how one could do something like this:

val u = UInt(42)

but how then would you access the value property if it's private or is the idea that u itself would be treated as the value?

Also, if you wanted to create a UInt that was greater than Int.MAX_VALUE what value would you present to the primary constructor? Presumably one would not be expected to work out the appropriate negative Int value corresponding to this so would Int literals be extended to permit higher values up to UInt.MAX_VALUE?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think number literals for unsigned values are part of this proposal, but I guess if they are implemented they need to allow for higher values than there signed parts. How else are you gonna get values for ULong. For UInt you could use a function converting a long. That being said, maybe you are doing something wrong if you have to hardcode numbers that big.

Inline classes are indirectly inherited from `Any`, i.e. they can be assigned to a value of type `Any`, but only through boxing.

Methods from `Any` (`toString`, `hashCode`, `equals`) can be useful for a user-defined inline classes and therefore should be customizable.
Methods `toString` and `hashCode` can be overridden as usual methods from `Any`. For method `equals` we're going to introduce new operator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, how will overridding toString(), hashCode() and equals() work?

inline class UserId(private val id: String) {
    override fun toString() = "UserId($id)"
}

fun test(userId: UserId) {
    System.out.println(userId) // ?
}

It'll require a wrapper class which is what inline class is trying to remove

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artem-zinnatullin It'll be a static method that'll be called at invocation places, so still no additional allocation. If you put the inline class instance in a more generic type, it'll automatically be boxed (like Int and other primitives are), so it'll call the virtual version of toString() and alike methods.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would System.out.println() or any other runtime-linked use-site know about any of these methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artem-zinnatullin userId will be boxed into an instance of UserId wrapper class, then this object instance is being passed to println

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aha, I guess it falls into nullable/platform-type case.

Still don't like this whole wrapping story, gotta think about it more.

Currently, inline classes must satisfy the following requirements:

- Inline class must have a public primary constructor with a single value parameter
- Inline class must have a single read-only (`val`) property as an underlying value, which is defined in primary constructor
Copy link

@ricmf ricmf Apr 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for the single-value restriction?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ricmf It can only work on a single reference. If you have more than one property, you need two references, and that only works with a real enclosing instance (that is allocated, not inline). However, you can perfectly make an inline class enclosing a Pair if it makes sense.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is possible to use multiple values, I am thinking about it.
It should work like JPA Embeddable class , JVM escape analysis should avoid extra allocation, probably this is out of scope and we should wait for Valhalla.

The problem become tricky in presence of annotations.

```kotlin
inline class Name(val s: String)

fun foo(n: Name?) { ... } // `Name?` here is mapped to String
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be String?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, here we know that null can be associated only with the type Name?, because underlying type of the inline class Name is a non-null reference type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to what we have for IntArray/LongArray/...: IntArray? is just an array of primitive types

@zarechenskiy zarechenskiy merged commit 6a050ca into Kotlin:master Jul 13, 2018
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

Successfully merging this pull request may close these issues.

None yet