-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Conversation
@dotnet/roslyn-compiler for review (post-RTM change). |
@gafter Could you do the first review, when you get some time? |
Yes |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
?
@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); | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 ...
?
There was a problem hiding this comment.
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) | ||
); |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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)
?
There was a problem hiding this comment.
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;
?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated line.
@jcouv This needs a specification, please. |
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) | ||
); |
There was a problem hiding this comment.
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(); | ||
} |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
Thanks for the test suggestions. I pushed another batch. |
LGTM |
Thanks for the review and feedback. I think this completes the implementation of the prototype. |
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
Continuing prototyping for #13602
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.