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

Prototype for "default": semantic model, constant value and more tests #16425

Merged
merged 6 commits into from
Feb 14, 2017

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Jan 11, 2017

Continuing prototyping for #13602

  • Adding some tests for semantic model
  • Allow target-typed literal to be seen as a constant
  • Adding tests corresponding to scenarios where null gets special treatment (throw null, null == null, null as Some Type, ...)

As a refresher, the first part of this prototype work was in PR #13603

CC @dotnet/roslyn-compiler for review. This is for features/default branch. No rush.

@jcouv
Copy link
Member Author

jcouv commented Jan 27, 2017

@dotnet/roslyn-compiler for review (post-RTM change).

@jcouv jcouv assigned jcouv and gafter and unassigned jcouv Jan 27, 2017
@jcouv
Copy link
Member Author

jcouv commented Feb 1, 2017

@gafter Could you do the first review, when you get some time?

@gafter
Copy link
Member

gafter commented Feb 2, 2017

Yes

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.

The set of changes in this PR look good to me. However, it appears to be adding to an existing prototype that I haven't reviewed.

@@ -553,7 +553,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
return BindDefaultExpression((DefaultExpressionSyntax)node, diagnostics);

case SyntaxKind.DefaultLiteral:
return new BoundDefaultOperator((DefaultLiteralSyntax)node);
return new BoundDefaultOperator((DefaultLiteralSyntax)node, ConstantValue.DefaultLiteral, type: null);
Copy link
Member

Choose a reason for hiding this comment

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

Please extract into method BindDefaultLiteral for consistency (all other expression forms delegate to a separate bind method).

Copy link
Member

Choose a reason for hiding this comment

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

Also, BoundDefaultOperator should probably be named BoundDefaultLiteral.

@@ -4834,7 +4832,6 @@ private bool IsUsingAliasInScope(string name)
return BadExpression(node, boundLeft);
}

// PROTOTYPE(default) unify with case above
// No member accesses on default
if (boundLeft.IsLiteralDefault())
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if these two cases (default and unbound lambda) can be combined by testingfor (object)leftType == null and in that case report the diagnostic using boundLeft.Display.

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 addressed your other feedback in latest commit, but I couldn't quite see how to factor as you suggested here.

@@ -975,7 +975,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou
{
var constantValue = source.ConstantValue;

if (constantValue == null)
if (constantValue == null || constantValue.IsDefaultLiteral)
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps change constantValue.IsDefaultLiteral to (object)source.Type == null?

@gafter gafter removed their assignment Feb 3, 2017
@jcouv
Copy link
Member Author

jcouv commented Feb 8, 2017

@cston I was hoping you could do the second review, since you had reviewed the first part of this prototype work. Thanks

private static BoundExpression BindDefaultLiteral(DefaultLiteralSyntax node)
{
return new BoundDefaultLiteral(node, ConstantValue.DefaultLiteral, type: null);
}
Copy link
Member

Choose a reason for hiding this comment

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

Is (T)default a constant? Is const object o = (T)default; legal?

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 will check. I have a test for int x = (short)default; (legal), but not with a generic cast.
My expectation is that (T)default should behave the same as default(T).

Copy link
Contributor

Choose a reason for hiding this comment

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

What about scenarios when it is of type user-defined struct? Is it supposed to have constant value then?

@@ -588,7 +588,7 @@ public override BoundNode VisitNameOfOperator(BoundNameOfOperator node)

public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node)
{
if (_inExpressionLambda && node.LeftOperand.IsLiteralNull())
if (_inExpressionLambda && (node.LeftOperand.IsLiteralNull() || node.LeftOperand.IsLiteralDefault()))
{
Error(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, node.LeftOperand);
Copy link
Member

Choose a reason for hiding this comment

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

Is there a test for default?

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. All the tests are in TargetTypedDefaultTests.cs, which is updated as part of this PR (although GitHub doesn't expand it by default).

var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Copy link
Member

Choose a reason for hiding this comment

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

Duplicated line.

comp.VerifyDiagnostics(
// (6,27): error CS0446: Foreach cannot operate on a 'default'. Did you intend to invoke the 'default'?
// foreach (int x in default) { }
Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "default").WithArguments("default").WithLocation(6, 27),
Copy link
Member

Choose a reason for hiding this comment

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

Did you intend to invoke the 'default'? seems odd.

compilation.VerifyDiagnostics(
// (5,35): error CS0186: Use of null is not valid in this context
// var q = from x in default select x;
Diagnostic(ErrorCode.ERR_NullNotValid, "select x").WithLocation(5, 35)
Copy link
Member

Choose a reason for hiding this comment

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

Should the message be Use of default is not valid ...?

Copy link
Member

Choose a reason for hiding this comment

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

Should this be ERR_DefaultNotValid?

// (6,30): error CS0077: The as operator must be used with a reference type or nullable type ('long' is a non-nullable value type)
// System.Console.Write(default as long);
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "default as long").WithArguments("long").WithLocation(6, 30)
);
Copy link
Member

Choose a reason for hiding this comment

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

Are there tests for default as C and default is C?

Copy link
Member

Choose a reason for hiding this comment

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

Are we testing nameof(default)?

Copy link
Member

Choose a reason for hiding this comment

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

Test for yield return default;?

Copy link
Member Author

Choose a reason for hiding this comment

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

InvocationOnDefault has nameof(default) (illegal). DefaultInAsOperator has default as long (illegal).
I'll add tests for the other suggestions.

var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Copy link
Member

Choose a reason for hiding this comment

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

Duplicated line.

@gafter
Copy link
Member

gafter commented Feb 9, 2017

@jcouv This needs a specification, please.

@jcouv jcouv added the 4 - In Review A fix for the issue is submitted for review. label Feb 11, 2017
@jcouv
Copy link
Member Author

jcouv commented Feb 13, 2017

Filed issue #17111 for writing a spec.

// (8,15): error CS0283: The type 'S' cannot be declared const
// const S y = (S)default;
Diagnostic(ErrorCode.ERR_BadConstType, "S").WithArguments("S").WithLocation(8, 15)
);
Copy link
Member

Choose a reason for hiding this comment

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

Are const object y = (T)default; or const object y = (S)default; allowed?

";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics();
}
Copy link
Member

Choose a reason for hiding this comment

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

Consider testing IEnumerable as well.

}

[Fact]
public void CS0403ERR_TypeVarCanBeDefault()
Copy link
Member

Choose a reason for hiding this comment

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

The name of the method refers to an error, although no errors are reported.

var conversion = nodes.OfType<CastExpressionSyntax>().Single();
var conversionTypeInfo = model.GetTypeInfo(conversion);
Assert.Equal("System.Int16", conversionTypeInfo.Type.ToTestDisplayString());
Assert.Equal("System.Int32", conversionTypeInfo.ConvertedType.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 new object[default] supported?

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, see IndexingIntoArray which has x[default] (legal).

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, sorry, I misunderstood. I tested access, but not array construction. I'll add.

@jcouv
Copy link
Member Author

jcouv commented Feb 13, 2017

Thanks for the test suggestions. I pushed another batch.

@cston
Copy link
Member

cston commented Feb 14, 2017

LGTM

@jcouv
Copy link
Member Author

jcouv commented Feb 14, 2017

Thanks for the review and feedback. I think this completes the implementation of the prototype.
I'll make sure to follow-up with a spec document.

@jcouv jcouv merged commit 582255b into dotnet:features/default Feb 14, 2017
@jcouv jcouv deleted the more-default branch February 14, 2017 18:30
jcouv added a commit to jcouv/roslyn that referenced this pull request Mar 11, 2017
dotnet#16425)

Conflicts:
	src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
	src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
	src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
	src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4 - In Review A fix for the issue is submitted for review. Area-Compilers cla-already-signed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants