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

Check GetConstantValue and GetConversion on target-typed default #17205

Merged
merged 11 commits into from
Mar 3, 2017

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Feb 17, 2017

I noticed two more semantic model APIs that need coverage.

GetConstantValue should behave as it does on default(...).
In the current implementation, when the type of the default literal is inferred, the bound node remains the same (typeless and without a constant value) and it is wrapped with a bound conversion. I'd like to get your feedback on that.

Relates to umbrella item for "default" (#13602)
@dotnet/roslyn-compiler for review.

@jcouv jcouv added this to the 2.1 milestone Feb 17, 2017
@jcouv jcouv self-assigned this Feb 17, 2017
@jcouv
Copy link
Member Author

jcouv commented Feb 21, 2017

@AlekseyTs Small semantic model change for "default" literal.
Another design would be to update the type in the bound node at the time the type the type is inferred. Let me know if you think that'd be better.

@@ -439,6 +439,7 @@ public object Value
{
case ConstantValueTypeDiscriminator.Bad: return null;
case ConstantValueTypeDiscriminator.Null: return null;
case ConstantValueTypeDiscriminator.DefaultLiteral: return null;
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 21, 2017

Choose a reason for hiding this comment

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

case ConstantValueTypeDiscriminator.DefaultLiteral: return null; [](start = 20, length = 64)

case ConstantValueTypeDiscriminator.DefaultLiteral: return null; [](start = 20, length = 64)

It feels unexpected that we ended up with a special discriminator for this feature. This is pretty much the same as Nothing literal in VB and we managed to support it without special discriminator. #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Right. The value should be what ConstantValue.Default returns.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm having some more trouble with constant values. The trouble is that expressions like default(int?) and default(int*) don't have constant values (see GetDefaultValue).

That means that default in int? x = default; shouldn't have a constant value. Does that seem right?

Also, I explored binding default as a BoundConversion with a null literal conversion (instead of creating a new conversion kind) and a BoundLiteralExpression as operand. But I think that literals should have a constant value. Is that correct?

Copy link
Contributor

@AlekseyTs AlekseyTs Feb 22, 2017

Choose a reason for hiding this comment

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

I'm having some more trouble with constant values.

I don't think of default as a literal. It represents a default value of a type. Each type has a default value, but not all default values are constants from the language point of view. Let's talk offline in more details. #Closed

@@ -980,6 +980,11 @@ internal override Optional<object> GetConstantValueWorker(CSharpSyntaxNode node,

if (boundExpr == null) return default(Optional<object>);

if (boundExpr.Kind == BoundKind.DefaultLiteral)
Copy link
Contributor

Choose a reason for hiding this comment

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

if (boundExpr.Kind == BoundKind.DefaultLiteral) [](start = 12, length = 47)

It is not clear why can't we make this decision based on the syntax.

var model = comp.GetSemanticModel(tree);
var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DefaultLiteralSyntax>().Single();

Assert.Equal(0, model.GetConstantValue(def).Value);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 21, 2017

Choose a reason for hiding this comment

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

model.GetConstantValue(def).Value [](start = 28, length = 33)

model.GetConstantValue(def).Value [](start = 28, length = 33)

What type is returned by SemanticModel for the "def" node here? That type and the type of the constant value should agree. #Closed

@@ -325,6 +354,10 @@ static void Main()
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Equal(0, model.GetConstantValue(def).Value);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 21, 2017

Choose a reason for hiding this comment

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

Assert.Equal(0, model.GetConstantValue(def).Value); [](start = 12, length = 51)

Assert.Equal(0, model.GetConstantValue(def).Value); [](start = 12, length = 51)

What type is returned by SemanticModel for the "def" node here? That type and the type of the constant value should agree. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like they agree.


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

@@ -427,6 +460,7 @@ static void Main()
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetDeclaredSymbol(def));
Assert.Equal(0, model.GetConstantValue(def).Value);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 21, 2017

Choose a reason for hiding this comment

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

Assert.Equal(0, model.GetConstantValue(def).Value); [](start = 12, length = 51)

Assert.Equal(0, model.GetConstantValue(def).Value); [](start = 12, length = 51)

What type is returned by SemanticModel for the "def" node here? That type and the type of the constant value should agree. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like they agree.


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

Copy link
Member

Choose a reason for hiding this comment

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

I don't think I fully agree with that. The type of the constant value should agree with the converted type of def.

Copy link
Member Author

Choose a reason for hiding this comment

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

For comparison, for null, the Type is null, the ConvertedType is the inferred type and the constant is null (which agrees with ConvertedType):

        public void AssignNullToString()
        {
            string source = @"
class C
{
    static void Main()
    {
        string x = null;
        System.Console.Write(x);
    }
}
";
            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
            comp.VerifyDiagnostics();

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().Single();

            Assert.Null(model.GetConstantValue(def).Value);
            Assert.Equal("System.String", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
            Assert.Null(model.GetTypeInfo(def).Type);
        }

Copy link
Contributor

@AlekseyTs AlekseyTs Feb 22, 2017

Choose a reason for hiding this comment

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

The null literal is special, it will only "match" reference types and will match any reference type. So, its constant value is null regardless of the target type. As in these scenarios, for example:

        [Fact]
        public void AssignNullToInt()
        {
            string source = @"
class C
{
    static void Main()
    {
        int x = null;
        System.Console.Write(x);
    }
}
";
            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().Single();

            Assert.Equal("null", def.ToString());
            Assert.True(model.GetConstantValue(def).HasValue);
            Assert.Null(model.GetConstantValue(def).Value);
            Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
            Assert.Null(model.GetTypeInfo(def).Type);
        }

        [Fact]
        public void AssignNullToNullableInt()
        {
            string source = @"
class C
{
    static void Main()
    {
        int? x = null;
        System.Console.Write(x);
    }
}
";
            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().Single();

            Assert.Equal("null", def.ToString());
            Assert.True(model.GetConstantValue(def).HasValue);
            Assert.Null(model.GetConstantValue(def).Value);
            Assert.Equal("System.Int32?", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
            Assert.Null(model.GetTypeInfo(def).Type);
        }

Clearly null value doesn't match the ConvertedType in cases like this, its the value of the null literal regardless of the target type.

However, if we look at literals of value types:

        [Fact]
        public void AssignIntToLong()
        {
            string source = @"
class C
{
    static void Main()
    {
        long x = 1;
        System.Console.Write(x);
    }
}
";
            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
            comp.VerifyDiagnostics();

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().Single();

            Assert.Equal(1, model.GetConstantValue(def).Value);
            Assert.Equal(typeof(int), model.GetConstantValue(def).Value.GetType());
            Assert.Equal("System.Int64", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
            Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString());
        }

We can observe that the constant value matches the Type, not the ConvertedType.

I understand that default is special in the way that it doesn't have natural type (similar to null). At the same time it doesn't have a "natural" constant value that is going to be valid for any target type in an non-error scenario. In fact, for some target types there could be no valid constant value. Given that, it would feel right to me If the Type (not Converted type) and the ConstantValue of the node were both based on the target type. But I could be convinced that the Type should always bee null. I would still like to avoid any special cases in SemanticModel to get this behavior. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 21, 2017

@jcouv

Another design would be to update the type in the bound node at the time the type the type is inferred. Let me know if you think that'd be better.

It is hard to say at the moment. It feels like it would be ideal to avoid adding special handling into SemanticModel because it is very likely that we will have to do that for different APIs so that they work in concert. #Closed

@gafter
Copy link
Member

gafter commented Feb 22, 2017

This is analogous to the way null is handled: E1 = null is bound to

assignmentOperator
├─left
│ └─eventAccess
│   ├─receiverOpt
│   │ └─thisReference
│   │   └─type: Program
│   ├─eventSymbol: event System.EventHandler Program.E1
│   ├─isUsableAsField: True
│   ├─resultKind: Viable
│   └─type: System.EventHandler
├─right
│ └─conversion
│   ├─operand
│   │ └─literal
│   │   ├─constantValueOpt: ConstantValueNull(null: Null)
│   │   └─type
│   ├─conversion: ImplicitReference
│   ├─isBaseConversion: False
│   ├─@checked: False
│   ├─explicitCastInCode: False
│   ├─constantValueOpt: ConstantValueNull(null: Null)
│   └─type: System.EventHandler
├─refKind: None
└─type: System.EventHandler

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 22, 2017

@gafter

This is analogous to the way null is handled: E1 = null is bound to ...

Could you please elaborate what source location or message this is related to, if any.
#Closed

@gafter
Copy link
Member

gafter commented Feb 22, 2017

This is related to the suggestion that GetTypeInfo on default should return a value whose Type property describes the type of the resulting constant value. I think that should be true of the ConvertedType property of the returned value from GetTypeInfo, not the Type property.

@jcouv
Copy link
Member Author

jcouv commented Feb 23, 2017

@AlekseyTs @gafter I've re-purposed and renamed the "null literal" conversion as we discussed.

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

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

Looks good, presuming this is the right approach to take.

@@ -643,7 +643,8 @@ private BoundExpression VisitConversion(BoundConversion node)
var e1 = Convert(Visit(node.Operand), node.Operand.Type, intermediate, node.Checked, false);
return Convert(e1, intermediate, node.Type, node.Checked, false);
}
case ConversionKind.NullLiteral:
case ConversionKind.DefaultOrNullLiteral:
// TODO REVIEW
Copy link
Member

Choose a reason for hiding this comment

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

// PROTOTYPE(default): REVIEW

var nodes = tree.GetCompilationUnitRoot().DescendantNodes();

var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString());
Copy link
Member

Choose a reason for hiding this comment

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

Is this similar to how we handle the null literal?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not exactly. See discussion above.

When we create a null literal conversion, we create a BoundConversion which wraps the BoundLiteralExpression, but that literal node isn't changed. It will have ConstantValue.Null and no Type. The Type is stored in the conversion node, but not in the literal node.

For default, when we create the conversion, we update the operand to have a ConstantValue (if one exists for that type) and a Type.

Here's what we get for null:

        public void Null()
        {
            string source = @"
class C
{
    static void Main()
    {
        C x = null;
        System.Console.Write(x);
    }
}
";
            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
            comp.VerifyDiagnostics();

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var nodes = tree.GetCompilationUnitRoot().DescendantNodes();

            var def = nodes.OfType<LiteralExpressionSyntax>().Single();
            Assert.Null(model.GetTypeInfo(def).Type);
            Assert.Equal("C", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
            Assert.Null(model.GetSymbolInfo(def).Symbol);
            Assert.Null(model.GetConstantValue(def).Value);
        }

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 23, 2017

    DefaultLiteral,

Do we still use this? #Closed


Refers to: src/Compilers/Core/Portable/ConstantValue.cs:30 in 7eb2e3f. [](commit_id = 7eb2e3f, deletion_comment = False)

{
if (conversion.Kind == ConversionKind.DefaultOrNullLiteral && source.Kind == BoundKind.DefaultLiteral)
{
return destination.GetDefaultValue();
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

GetDefaultValue [](start = 39, length = 15)

Is this method new to this branch? Could you provide a link to its implementation? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the existing method used for making a ConstantValue for default() operator.
Code is here http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/Symbols/TypeSymbolExtensions.cs,264ab804a03fe275

@@ -216,7 +216,7 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
case ConversionKind.Identity:
case ConversionKind.ImplicitReference:
case ConversionKind.Unboxing:
case ConversionKind.NullLiteral:
case ConversionKind.DefaultOrNullLiteral:
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

case ConversionKind.DefaultOrNullLiteral: [](start = 20, length = 41)

Do we have a test that would go through here with default? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Apparently this line affects no tests whatsoever. I'll see if I can hit it, or I'll file an issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

This method called only from BindDeclarationPattern (and more specifically when there is a problem with the type in the declaration pattern), so I don't think this line is needed for either null or default.

@@ -2991,7 +2991,7 @@ private TypeSymbol StackMergeType(BoundExpression expr)
var conversionKind = conversion.ConversionKind;
if (conversionKind.IsImplicitConversion() &&
conversionKind != ConversionKind.MethodGroup &&
conversionKind != ConversionKind.NullLiteral)
conversionKind != ConversionKind.DefaultOrNullLiteral)
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

conversionKind != ConversionKind.DefaultOrNullLiteral) [](start = 24, length = 54)

Do we have a test that covers this path for default? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't manage to reach this with null or default either. The logic above in StackMergeType exits early except for array, interface or delegate types. But the local rewriter strips the bound conversion on an array, interface and delegate types (because they have a constant value).

@@ -456,7 +456,7 @@ private static BoundExpression DemoteEnumOperand(BoundExpression operand)
var conversion = (BoundConversion)operand;
if (!conversion.ConversionKind.IsUserDefinedConversion() &&
conversion.ConversionKind.IsImplicitConversion() &&
conversion.ConversionKind != ConversionKind.NullLiteral &&
conversion.ConversionKind != ConversionKind.DefaultOrNullLiteral &&
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

conversion.ConversionKind != ConversionKind.DefaultOrNullLiteral && [](start = 20, length = 67)

Do we have a test that covers this code path with default? #Resolved

Assert.Equal("System.Int32", model.GetTypeInfo(def).Type.ToTestDisplayString());
Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Equal("0", model.GetConstantValue(def).Value.ToString());
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

Assert.Equal("0", model.GetConstantValue(def).Value.ToString()); [](start = 12, length = 64)

It would be good to test conversion as well. #Closed

Assert.Equal("S", model.GetTypeInfo(def).Type.ToTestDisplayString());
Assert.Equal("S", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.False(model.GetConstantValue(def).HasValue);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

    Assert.False(model.GetConstantValue(def).HasValue); [](start = 3, length = 60)

It would be good to test conversion as well. #Closed

Assert.Equal("T", model.GetTypeInfo(def).Type.ToTestDisplayString());
Assert.Equal("T", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.False(model.GetConstantValue(def).HasValue);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

Assert.False(model.GetConstantValue(def).HasValue); [](start = 12, length = 51)

It would be good to test conversion as well. #Closed

@@ -787,7 +859,7 @@ static void Main()
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Equal("System.Int32?", model.GetTypeInfo(def).Type.ToTestDisplayString());
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 23, 2017

Choose a reason for hiding this comment

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

Assert.Equal("System.Int32?", model.GetTypeInfo(def).Type.ToTestDisplayString()); [](start = 12, length = 81)

It would be good to test conversion and constant value as well. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like constant value is tested.


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

@@ -988,7 +1064,7 @@ static void Main()
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();

var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Equal("System.Int16", model.GetTypeInfo(def).Type.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetDeclaredSymbol(def));
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 24, 2017

Choose a reason for hiding this comment

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

Assert.Null(model.GetDeclaredSymbol(def)); [](start = 12, length = 42)

It would be good to test conversion and constant value for this scenario because it is using explicit cast. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 24, 2017

        Assert.Equal("System.Int32", conversionTypeInfo.ConvertedType.ToTestDisplayString());

It would be good to test constant value as well. #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs:1074 in d980c02. [](commit_id = d980c02, deletion_comment = False)

Assert.Equal("System.Int16", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Equal((short)0, model.GetConstantValue(def).Value);
Assert.True(model.GetConversion(def).IsIdentity);
Copy link
Member Author

Choose a reason for hiding this comment

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

@AlekseyTs This one surprised me. This behavior comes from GetTypeInfoForNode when type and converted type match.

Assert.Equal("System.Int16", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Equal((short)0, model.GetConstantValue(def).Value);
Assert.True(model.GetConversion(def).IsIdentity);
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 27, 2017

Choose a reason for hiding this comment

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

Assert.True(model.GetConversion(def).IsIdentity); [](start = 12, length = 49)

Is this consistent with behavior when explicit cast is applied to a null literal? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, in terms of conversions, the behavior is similar with null (there is an identity conversion on the literal).
The main difference with null as we already discussed is that we give default a type.

        [Fact]
        public void ExplicitCastOnNull()
        {
            string source = @"
class Base { }
class Derived : Base 
{
    static void Main()
    {
        Base x = (Derived)null;
        System.Console.Write(x);
    }
}
";

            var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
            comp.VerifyDiagnostics();

            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
            var nodes = tree.GetCompilationUnitRoot().DescendantNodes();

            var def = nodes.OfType<LiteralExpressionSyntax>().Single();
            Assert.Equal(null, model.GetTypeInfo(def).Type);
            Assert.Null(model.GetSymbolInfo(def).Symbol);
            Assert.Null(model.GetDeclaredSymbol(def));
            Assert.Equal(null, model.GetTypeInfo(def).ConvertedType);
            Assert.Null(model.GetSymbolInfo(def).Symbol);
            Assert.True(model.GetConstantValue(def).HasValue);
            Assert.Equal(null, model.GetConstantValue(def).Value);
            Assert.True(model.GetConversion(def).IsIdentity);

            var conversionSyntax = nodes.OfType<CastExpressionSyntax>().Single();
            var conversionTypeInfo = model.GetTypeInfo(conversionSyntax);
            Assert.Equal("Derived", conversionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("Base", conversionTypeInfo.ConvertedType.ToTestDisplayString());
            Assert.True(model.GetConstantValue(conversionSyntax).HasValue);
            Assert.Equal(null, model.GetConstantValue(conversionSyntax).Value);
            Conversion conversion = model.GetConversion(conversionSyntax);
            Assert.False(conversion.IsNumeric);
            Assert.True(conversion.IsImplicit);
        }

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for confirming this.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM

@jcouv
Copy link
Member Author

jcouv commented Feb 28, 2017

@gafter Could you take another look and approve the change if looks good?

@jcouv
Copy link
Member Author

jcouv commented Mar 1, 2017

@gafter Still good to go?

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

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

LGTM

@jcouv jcouv merged commit 533aa4b into dotnet:features/default Mar 3, 2017
@jcouv jcouv deleted the default-constvalue branch March 3, 2017 00:20
jcouv added a commit to jcouv/roslyn that referenced this pull request Mar 11, 2017
…net#17205)

Conflicts:
	src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
	src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs
	src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs
	src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
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