Skip to content

Track nullability of nullable value types#31499

Merged
cston merged 30 commits intodotnet:masterfrom
cston:NVT
Dec 11, 2018
Merged

Track nullability of nullable value types#31499
cston merged 30 commits intodotnet:masterfrom
cston:NVT

Conversation

@cston
Copy link
Contributor

@cston cston commented Dec 3, 2018

No description provided.

@cston cston requested a review from a team as a code owner December 3, 2018 18:45
Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Nit: this whole loop can just be converted to a foreach, we don't use i anywhere except accessing overridingParameters. #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately i is used for overriddenParameters as well as overridingParameters.


In reply to: 238440431 [](ancestors = 238440431)

Copy link
Member

Choose a reason for hiding this comment

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

Ah dang, and I thought I had gone through thoroughly here to avoid looking blind.


In reply to: 238500830 [](ancestors = 238500830,238440431)

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

overridingParameters[i] [](start = 158, length = 23)

If you don't want to update the whole loop, please at least do this instance. #ByDesign

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

This feels like it's the wrong location. The chance for the null reference happens on the invocation of .Value, right? Unless this is reporting on the implicit conversion, in which case I feel like this isn't descriptive enough, and I would still have expected a warning on .Value. #Closed

Copy link
Member

Choose a reason for hiding this comment

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

This still feels wrong.


In reply to: 238442273 [](ancestors = 238442273)

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Yeah, I think this example is more illustrative of wanting to have a clearer error message here. As a user, my first thought is "The nullabilities here don't differ, why are you warning me?" #Resolved

Copy link
Member

Choose a reason for hiding this comment

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

Actually, by my reading of the spec, this is an invalid warning.

Given a user-defined conversion operator that converts from a non-nullable value type S to a non-nullable value type T, a lifted conversion operator exists that converts from S? to T?.
https://github.com/dotnet/csharplang/blob/54df8973faabe32335fa087f9d32fd959366ff83/spec/conversions.md#lifted-conversion-operators


In reply to: 238443507 [](ancestors = 238443507)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, thanks.


In reply to: 238445282 [](ancestors = 238445282,238443507)

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Perhaps add a dereference of y after this? #Closed

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Is this substantially different from NullableT_06, other than also having the negative case? If not, consider consolidating. #Closed

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Shouldn't we have a warning here for something along the lines of "this will never be false"? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Logged #31516.


In reply to: 238456621 [](ancestors = 238456621)

Copy link
Member

@333fred 333fred Dec 3, 2018

Choose a reason for hiding this comment

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

Again, there should not be a arning here. Top-level nullabilities match. #Closed

@333fred
Copy link
Member

333fred commented Dec 3, 2018

Done review pass (commit 3) #Resolved

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 4, 2018

        builder.Add(getId(ErrorCode.WRN_ConvertingNullableToNonNullable));

Should WRN_NullableValueTypeMayBeNull be added to this list? #Closed


Refers to: src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs:23 in 5f14743. [](commit_id = 5f14743255e09c9143be2d7e10dd2eb49e25610a, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 4, 2018

                return TypeSymbolWithAnnotations.Create(resolvedType, customModifiers: customModifiers);

Possibly dropping "NotNullable" annotation? Perhaps annotations should be passed explicitly, or an assert should be added to verify that state is not going to be lost here. #Closed


Refers to: src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs:1203 in 5f14743. [](commit_id = 5f14743255e09c9143be2d7e10dd2eb49e25610a, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 4, 2018

                return TypeSymbolWithAnnotations.Create(typeSymbol, customModifiers: customModifiers);

Here is possibly another place where state might be lost. #Closed


Refers to: src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs:1213 in 5f14743. [](commit_id = 5f14743255e09c9143be2d7e10dd2eb49e25610a, deletion_comment = False)

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

case NullableAnnotation.NotAnnotated: [](start = 16, length = 37)

Would it make sense to add an assert to TypeSymbolWithAnnotations's constructor that Unknown and NotAnnotated are never combined with a Nullable<T>? #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) [](start = 37, length = 65)

We have a IsNullableType helper. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

return containingSlot < 0 ? -1 : GetNullableOfTValueSlot(operand.Type, containingSlot); [](start = 27, length = 88)

I would like to better understand why do we need to be able to get a slots for conversions. Could you provide some details? Offline would be fine. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As we discussed offline, in the following, need to track slots for a, a.Value, a.Value.B and a.Value.B.Value, and when evaluating ((A)a).B, we need to recognize the nullability of (A)a (not nullable) and the slot (a.Value).

struct A { B? B; }
struct B { }
class Program
{
    static void F(A? a)
    {
        if (a?.B != null)
            _ = ((A)a).B.Value;
    }
}

See NullableT_19.


In reply to: 238815266 [](ancestors = 238815266)

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

GetTypeOrReturnType [](start = 47, length = 19)

It looks like we also have VariableType helper in the base class. Would it be more appropriate to use it here? #Closed

Copy link
Member

@333fred 333fred Dec 4, 2018

Choose a reason for hiding this comment

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

Are we expecting to get warnings here, or is this the actual final behavior? If the former, consider logging a bug. #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There shouldn't be any warnings here. (There were warnings in an earlier iteration of this change.)


In reply to: 238833118 [](ancestors = 238833118)

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM (commit 8)

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

return containingSlot < 0 ? -1 : GetNullableOfTValueSlot(operand.Type, containingSlot); [](start = 28, length = 87)

Can we get here for a conversion from Nullable<Type1> to Type2? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converting from Nullable<T1> to T2 would be either a Boxing conversion or an ExplicitUserDefined conversion.


In reply to: 238852151 [](ancestors = 238852151)

Copy link
Contributor

Choose a reason for hiding this comment

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

Converting from Nullable to T2 would be either a Boxing conversion or an ExplicitUserDefined conversion.

Are you sure? What about int? -> long and the like?


In reply to: 239155510 [](ancestors = 239155510,238852151)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 4, 2018

                    // getOperandSlots will only return slots for locations that are reference types.

Is this comment still accurate with respect to nullable value types? #Closed


Refers to: src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs:1549 in 5de5cae. [](commit_id = 5de5caef98a5df5dcce9aedc904cf0e83a6b8107, deletion_comment = False)

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

parameterType.Equals(underlyingOperandType = operandType.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) [](start = 28, length = 119)

It feels like this condition is too strict. For example, it is valid for a lifted conversion to have long as the parameter type and int? as the operand type. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The operand -> conversion "from" type conversion above should represent the conversion from int? to long?.


In reply to: 238872741 [](ancestors = 238872741)

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have a test for a scenario like this? Also, the fact that we are "redefining" what operandType represents throughout this sequence, makes it very hard to reason about the code. Consider introducing locals with specific names.


In reply to: 239170622 [](ancestors = 239170622,238872741)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added tests.


In reply to: 239174936 [](ancestors = 239174936,239170622,238872741)

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

operandType.IsValueType ? compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(operandType)) : operandType.TypeSymbol, [](start = 32, length = 155)

Unless I am misinterpreting something, I am not sure why do we need to wrap parameterType to nullable. What observable effect would that have on the analysis? #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

operandAnnotation.IsAnyNullable() ? NullableAnnotation.Nullable : NullableAnnotation.NotNullable); [](start = 32, length = 98)

It is not clear why would operandAnnotation matter, the lifted conversion method is always invoked with unwrapped (not nullable) value. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

operandAnnotation.IsAnyNullable() ? NullableAnnotation.Nullable : NullableAnnotation.NotNullable [](start = 32, length = 96)

It feels like operandAnnotation shouldn't matter if methodOpt.ReturnType is nullable value type, or a reference type. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Dec 4, 2018

Choose a reason for hiding this comment

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

operandType.IsNullableType() [](start = 43, length = 28)

I think we should keep the IsNullableType check. Malformed annotations in metadata can have nullable annotation of a non-nullable value type. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 4, 2018

            case ConversionKind.Unboxing:

It feels like we should have a special handling for unboxing into a nullable value type. #Closed


Refers to: src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs:3412 in 5de5cae. [](commit_id = 5de5caef98a5df5dcce9aedc904cf0e83a6b8107, deletion_comment = False)

@cston cston merged commit 1f5f718 into dotnet:master Dec 11, 2018
@cston cston deleted the NVT branch December 11, 2018 01:08
@cston
Copy link
Contributor Author

cston commented Dec 11, 2018

Thanks for the review feedback!

@cston
Copy link
Contributor Author

cston commented Dec 11, 2018

cc @dotnet/roslyn-compiler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants