提交 82268d08 编写于 作者: J Julien Couvreur 提交者: Julien Couvreur

Prototype for "default": semantic model, constant value and more tests (#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
上级 cd510fd1
......@@ -920,6 +920,10 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression receiverOpt, b
case ConversionKind.NullLiteral:
return sourceConstantValue;
case ConversionKind.DefaultLiteral:
Debug.Assert(sourceConstantValue.IsDefaultLiteral);
return destination.GetDefaultValue();
case ConversionKind.ImplicitConstant:
return FoldConstantNumericConversion(syntax, sourceConstantValue, destination, diagnostics);
......
......@@ -553,7 +553,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
return BindDefaultExpression((DefaultExpressionSyntax)node, diagnostics);
case SyntaxKind.DefaultLiteral:
return new BoundDefaultOperator((DefaultLiteralSyntax)node);
return BindDefaultLiteral((DefaultLiteralSyntax)node);
case SyntaxKind.TypeOfExpression:
return BindTypeOf((TypeOfExpressionSyntax)node, diagnostics);
......@@ -773,6 +773,11 @@ private BoundExpression BindDeclarationVariables(TypeSymbol declType, VariableDe
}
}
private static BoundExpression BindDefaultLiteral(DefaultLiteralSyntax node)
{
return new BoundDefaultLiteral(node, ConstantValue.DefaultLiteral, type: null);
}
private BoundExpression BindTupleExpression(TupleExpressionSyntax node, DiagnosticBag diagnostics)
{
SeparatedSyntaxList<ArgumentSyntax> arguments = node.Arguments;
......@@ -1057,7 +1062,7 @@ internal static ConstantValue GetConstantSizeOf(TypeSymbol type)
private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, DiagnosticBag diagnostics)
{
TypeSymbol type = this.BindType(node.Type, diagnostics);
return new BoundDefaultOperator(node, type);
return new BoundDefaultLiteral(node, type);
}
/// <summary>
......@@ -1851,8 +1856,6 @@ private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression oper
return;
}
// PROTOTYPE(default) SHould handle default literal here?
if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure)
{
Debug.Assert(conversion.IsUserDefined);
......@@ -5039,7 +5042,6 @@ private bool IsUsingAliasInScope(string name)
return BadExpression(node, boundLeft);
}
// PROTOTYPE(default) unify with case above
// No member accesses on default
if (boundLeft.IsLiteralDefault())
{
......@@ -5216,7 +5218,7 @@ private bool IsUsingAliasInScope(string name)
private static void WarnOnAccessOfOffDefault(SyntaxNode node, BoundExpression boundLeft, DiagnosticBag diagnostics)
{
if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultOperator && boundLeft.ConstantValue == ConstantValue.Null)
if (boundLeft != null && boundLeft.Kind == BoundKind.DefaultLiteral && boundLeft.ConstantValue == ConstantValue.Null)
{
Error(diagnostics, ErrorCode.WRN_DotOnDefault, node, boundLeft.Type);
}
......
......@@ -409,6 +409,11 @@ private ImmutableArray<BoundExpression> BuildArgumentsForDynamicInvocation(Analy
// error CS1978: Cannot use an expression of type '__arglist' as an argument to a dynamically dispatched operation
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, "__arglist");
}
else if (arg.IsLiteralDefault())
{
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgDefault, arg.Syntax);
hasErrors = true;
}
else
{
// Lambdas,anonymous methods and method groups are the typeless expressions that
......
......@@ -2672,6 +2672,12 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
if (operand.ConstantValue == ConstantValue.DefaultLiteral)
{
Error(diagnostics, ErrorCode.ERR_DefaultNotValid, node, targetType);
return new BoundIsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true);
}
if (operand.ConstantValue == ConstantValue.Null ||
operand.Kind == BoundKind.MethodGroup ||
operand.Type.SpecialType == SpecialType.System_Void)
......@@ -3063,8 +3069,6 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa
return new BoundAsOperator(node, operand, typeExpression, Conversion.NullLiteral, resultType);
}
// PROTOTYPE(default) Something needed here for "as" operator
if (operand.Kind == BoundKind.MethodGroup)
{
Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType);
......
......@@ -724,6 +724,10 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r
{
diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location);
}
else if (ultimateReceiver.IsLiteralDefault())
{
diagnostics.Add(ErrorCode.ERR_DefaultNotValid, node.Location);
}
else if (ultimateReceiver.Kind == BoundKind.NamespaceExpression)
{
diagnostics.Add(ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax.Location, ultimateReceiver.Syntax, MessageID.IDS_SK_NAMESPACE.Localize());
......
......@@ -1005,8 +1005,11 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym
initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics);
if (!initializerOpt.HasAnyErrors)
{
Debug.Assert(initializerOpt.Kind == BoundKind.Conversion && ((BoundConversion)initializerOpt).Operand.IsLiteralNull(),
Debug.Assert(initializerOpt.Kind == BoundKind.Conversion &&
(((BoundConversion)initializerOpt).Operand.IsLiteralNull() ||
((BoundConversion)initializerOpt).Operand.IsLiteralDefault()),
"All other typeless expressions should have conversion errors");
// CONSIDER: this is a very confusing error message, but it's what Dev10 reports.
Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax);
}
......@@ -3197,7 +3200,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di
var interactiveInitializerMethod = this.ContainingMemberOrLambda as SynthesizedInteractiveInitializerMethod;
if (interactiveInitializerMethod != null)
{
arg = new BoundDefaultOperator(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType);
arg = new BoundDefaultLiteral(interactiveInitializerMethod.GetNonNullSyntaxNode(), interactiveInitializerMethod.ResultType);
}
}
......
......@@ -500,13 +500,19 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE
{
TypeSymbol collectionExprType = collectionExpr.Type;
if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull)
if (collectionExpr.ConstantValue != null)
{
// Spec seems to refer to null literals, but Dev10 reports anything known to be null.
Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type
diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location);
return false;
if (collectionExpr.ConstantValue.IsNull)
{
// Spec seems to refer to null literals, but Dev10 reports anything known to be null.
diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location);
return false;
}
else if (collectionExpr.ConstantValue.IsDefaultLiteral)
{
diagnostics.Add(ErrorCode.ERR_DefaultNotValid, _syntax.Expression.Location);
return false;
}
}
if ((object)collectionExprType == null) // There's no way to enumerate something without a type.
......
......@@ -810,8 +810,8 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi
}
break;
case BoundKind.DefaultOperator:
var defaultExpression = (BoundDefaultOperator)sourceExpression;
case BoundKind.DefaultLiteral:
var defaultExpression = (BoundDefaultLiteral)sourceExpression;
if ((object)defaultExpression.Type == null)
{
return Conversion.DefaultLiteral;
......@@ -973,7 +973,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou
{
var constantValue = source.ConstantValue;
if (constantValue == null)
if (constantValue == null || (object)source.Type == null)
{
return false;
}
......
......@@ -460,7 +460,7 @@ public override Symbol ExpressionSymbol
}
}
internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public override ConstantValue ConstantValue
{
......
......@@ -16,7 +16,7 @@ public static bool IsLiteralNull(this BoundExpression node)
public static bool IsLiteralDefault(this BoundExpression node)
{
return node.Kind == BoundKind.DefaultOperator && (object)node.Type == null;
return node.Kind == BoundKind.DefaultLiteral && (object)node.Type == null;
}
// returns true when expression has no side-effects and produces
......@@ -27,7 +27,7 @@ public static bool IsLiteralDefault(this BoundExpression node)
// after some folding/propagation/algebraic transformations.
public static bool IsDefaultValue(this BoundExpression node)
{
if (node.Kind == BoundKind.DefaultOperator)
if (node.Kind == BoundKind.DefaultLiteral)
{
return true;
}
......
......@@ -517,7 +517,7 @@
<Field Name="GetFieldFromHandle" Type="MethodSymbol" Null="allow"/>
</Node>
<Node Name="BoundDefaultOperator" Base="BoundExpression">
<Node Name="BoundDefaultLiteral" Base="BoundExpression">
<!-- Type is null in the case of a default literal, and non-null in the case of a fully-spelled out default operator. -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="allow"/>
<Field Name="ConstantValueOpt" Type="ConstantValue" Null="allow"/>
......
......@@ -48,8 +48,8 @@ public virtual R Visit(BoundNode node, A arg)
return VisitArrayAccess(node as BoundArrayAccess, arg);
case BoundKind.TypeOfOperator:
return VisitTypeOfOperator(node as BoundTypeOfOperator, arg);
case BoundKind.DefaultOperator:
return VisitDefaultOperator(node as BoundDefaultOperator, arg);
case BoundKind.DefaultLiteral:
return VisitDefaultLiteral(node as BoundDefaultLiteral, arg);
case BoundKind.IsOperator:
return VisitIsOperator(node as BoundIsOperator, arg);
case BoundKind.AsOperator:
......
......@@ -523,14 +523,14 @@ public static BoundBlock SynthesizedNoLocals(SyntaxNode syntax, params BoundStat
}
}
internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public BoundDefaultOperator(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false)
public BoundDefaultLiteral(SyntaxNode syntax, TypeSymbol type, bool hasErrors = false)
: this(syntax, type.GetDefaultValue(), type, hasErrors)
{
}
public BoundDefaultOperator(SyntaxNode syntax)
public BoundDefaultLiteral(SyntaxNode syntax)
: this(syntax, constantValueOpt: null, type: null, hasErrors: false)
{
}
......
......@@ -939,7 +939,7 @@ public override void Accept(OperationVisitor visitor)
}
}
internal partial class BoundDefaultOperator : IDefaultValueExpression
internal partial class BoundDefaultLiteral : IDefaultValueExpression
{
protected override OperationKind ExpressionKind => OperationKind.DefaultValueExpression;
......
......@@ -136,7 +136,7 @@ public override object Display
}
}
internal partial class BoundDefaultOperator
internal partial class BoundDefaultLiteral
{
public override object Display
{
......
......@@ -1348,6 +1348,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation..
/// </summary>
internal static string ERR_BadDynamicMethodArgDefault {
get {
return ResourceManager.GetString("ERR_BadDynamicMethodArgDefault", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type..
/// </summary>
......@@ -3175,6 +3184,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Use of default is not valid in this context.
/// </summary>
internal static string ERR_DefaultNotValid {
get {
return ResourceManager.GetString("ERR_DefaultNotValid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Argument of type &apos;{0}&apos; is not applicable for the DefaultParameterValue attribute.
/// </summary>
......@@ -4121,7 +4139,7 @@ internal class CSharpResources {
}
/// <summary>
/// Looks up a localized string similar to An expression tree lambda may not contain a coalescing operator with a null literal left-hand side.
/// Looks up a localized string similar to An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side.
/// </summary>
internal static string ERR_ExpressionTreeContainsBadCoalesce {
get {
......
......@@ -849,6 +849,9 @@
<data name="ERR_NullNotValid" xml:space="preserve">
<value>Use of null is not valid in this context</value>
</data>
<data name="ERR_DefaultNotValid" xml:space="preserve">
<value>Use of default is not valid in this context</value>
</data>
<data name="ERR_UseDefViolationThis" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields are assigned to</value>
</data>
......@@ -2184,7 +2187,7 @@ If such a class is used as a base class and if the deriving class defines a dest
<value>Cannot use local variable '{0}' before it is declared. The declaration of the local variable hides the field '{1}'.</value>
</data>
<data name="ERR_ExpressionTreeContainsBadCoalesce" xml:space="preserve">
<value>An expression tree lambda may not contain a coalescing operator with a null literal left-hand side</value>
<value>An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side</value>
</data>
<data name="ERR_IdentifierExpected" xml:space="preserve">
<value>Identifier expected</value>
......@@ -5023,4 +5026,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_PatternDynamicType" xml:space="preserve">
<value>It is not legal to use the type 'dynamic' in a pattern.</value>
</data>
<data name="ERR_BadDynamicMethodArgDefault" xml:space="preserve">
<value>Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation.</value>
</data>
</root>
\ No newline at end of file
......@@ -191,8 +191,8 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitAsExpression((BoundAsOperator)expression, used);
break;
case BoundKind.DefaultOperator:
EmitDefaultExpression((BoundDefaultOperator)expression, used);
case BoundKind.DefaultLiteral:
EmitDefaultExpression((BoundDefaultLiteral)expression, used);
break;
case BoundKind.TypeOfOperator:
......@@ -2637,7 +2637,7 @@ private void EmitDefaultValue(TypeSymbol type, bool used, SyntaxNode syntaxNode)
}
}
private void EmitDefaultExpression(BoundDefaultOperator expression, bool used)
private void EmitDefaultExpression(BoundDefaultLiteral expression, bool used)
{
Debug.Assert(expression.Type.SpecialType == SpecialType.System_Decimal ||
expression.Type.GetDefaultValue() == null, "constant should be set on this expression");
......
......@@ -1426,7 +1426,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
if (node.OperatorKind.IsChecked() && node.OperatorKind.Operator() == UnaryOperatorKind.UnaryMinus)
{
var origStack = StackDepth();
PushEvalStack(new BoundDefaultOperator(node.Syntax, node.Operand.Type), ExprContext.Value);
PushEvalStack(new BoundDefaultLiteral(node.Syntax, node.Operand.Type), ExprContext.Value);
BoundExpression operand = (BoundExpression)this.Visit(node.Operand);
return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, node.Type);
}
......
......@@ -1461,5 +1461,8 @@ internal enum ErrorCode
#endregion more stragglers for C# 7
ERR_Merge_conflict_marker_encountered = 8300,
ERR_BadDynamicMethodArgDefault = 9000,
ERR_DefaultNotValid = 9001,
}
}
\ No newline at end of file
......@@ -608,7 +608,7 @@ internal static bool WriteConsideredUse(TypeSymbol type, BoundExpression value)
}
return WriteConsideredUse(null, boundConversion.Operand);
}
case BoundKind.DefaultOperator:
case BoundKind.DefaultLiteral:
return false;
case BoundKind.ObjectCreationExpression:
var init = (BoundObjectCreationExpression)value;
......
......@@ -56,7 +56,7 @@ internal class FlowAnalysisPass
{
Debug.Assert(submissionResultType.SpecialType != SpecialType.System_Void);
var trailingExpression = new BoundDefaultOperator(method.GetNonNullSyntaxNode(), submissionResultType);
var trailingExpression = new BoundDefaultLiteral(method.GetNonNullSyntaxNode(), submissionResultType);
var newStatements = block.Statements.Add(new BoundReturnStatement(trailingExpression.Syntax, RefKind.None, trailingExpression));
block = new BoundBlock(block.Syntax, ImmutableArray<LocalSymbol>.Empty, newStatements) { WasCompilerGenerated = true };
#if DEBUG
......
......@@ -2497,7 +2497,7 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
return null;
}
public override BoundNode VisitDefaultOperator(BoundDefaultOperator node)
public override BoundNode VisitDefaultLiteral(BoundDefaultLiteral node)
{
return null;
}
......
......@@ -596,7 +596,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);
}
......
......@@ -84,7 +84,7 @@ public static bool NullableNeverHasValue(this BoundExpression expr)
}
// "default(int?)" never has a value.
if (expr.Kind == BoundKind.DefaultOperator)
if (expr.Kind == BoundKind.DefaultLiteral)
{
return true;
}
......
......@@ -231,7 +231,7 @@ private BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node)
case BoundKind.UnaryOperator:
return VisitUnaryOperator((BoundUnaryOperator)node);
case BoundKind.DefaultOperator:
case BoundKind.DefaultLiteral:
case BoundKind.HostObjectMemberReference:
case BoundKind.Literal:
case BoundKind.Local:
......
......@@ -639,7 +639,7 @@ private BoundNode IntroduceFrame(BoundNode node, LambdaFrame frame, Func<ArrayBu
if (frame.Constructor == null)
{
Debug.Assert(frame.TypeKind == TypeKind.Struct);
newFrame = new BoundDefaultOperator(syntax: syntax, type: frameType);
newFrame = new BoundDefaultLiteral(syntax: syntax, type: frameType);
}
else
{
......
......@@ -40,7 +40,7 @@ public override BoundNode VisitAsOperator(BoundAsOperator node)
if (constantValue != null)
{
Debug.Assert(constantValue.IsNull);
BoundExpression result = rewrittenType.IsNullableType() ? new BoundDefaultOperator(syntax, rewrittenType) : MakeLiteral(syntax, constantValue, rewrittenType);
BoundExpression result = rewrittenType.IsNullableType() ? new BoundDefaultLiteral(syntax, rewrittenType) : MakeLiteral(syntax, constantValue, rewrittenType);
if (rewrittenOperand.ConstantValue != null)
{
......
......@@ -1158,7 +1158,7 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
if (leftAlwaysNull && rightAlwaysNull)
{
// default(R?)
return new BoundDefaultOperator(syntax, null, type);
return new BoundDefaultLiteral(syntax, null, type);
}
// Optimization #2: If both sides are non-null then we can again eliminate the lifting entirely.
......@@ -1230,14 +1230,14 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
if (sideEffect.ConstantValue != null)
{
return new BoundDefaultOperator(syntax, null, type);
return new BoundDefaultLiteral(syntax, null, type);
}
return new BoundSequence(
syntax: syntax,
locals: ImmutableArray<LocalSymbol>.Empty,
sideEffects: ImmutableArray.Create<BoundExpression>(sideEffect),
value: new BoundDefaultOperator(syntax, null, type),
value: new BoundDefaultLiteral(syntax, null, type),
type: type);
}
......@@ -1302,7 +1302,7 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
BoundExpression consequence = MakeLiftedBinaryOperatorConsequence(syntax, kind, callX_GetValueOrDefault, callY_GetValueOrDefault, type, method);
// default(R?)
BoundExpression alternative = new BoundDefaultOperator(syntax, null, type);
BoundExpression alternative = new BoundDefaultLiteral(syntax, null, type);
// tempX.HasValue & tempY.HasValue ?
// new R?(tempX.GetValueOrDefault() OP tempY.GetValueOrDefault()) :
......@@ -1478,7 +1478,7 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
NamedTypeSymbol nullableBoolType = nullableType.Construct(boolType);
if (value == null)
{
return new BoundDefaultOperator(syntax, null, nullableBoolType);
return new BoundDefaultLiteral(syntax, null, nullableBoolType);
}
return new BoundObjectCreationExpression(
......@@ -1521,7 +1521,7 @@ private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value)
BoundExpression alwaysNull = leftAlwaysNull ? left : right;
BoundExpression notAlwaysNull = leftAlwaysNull ? right : left;
BoundExpression neverNull = NullableAlwaysHasValue(notAlwaysNull);
BoundExpression nullBool = new BoundDefaultOperator(syntax, null, alwaysNull.Type);
BoundExpression nullBool = new BoundDefaultLiteral(syntax, null, alwaysNull.Type);
if (neverNull != null)
{
......
......@@ -994,14 +994,14 @@ private BoundExpression GetDefaultParameterValue(SyntaxNode syntax, ParameterSym
else
{
// The argument to M([Optional] int x) becomes default(int)
defaultValue = new BoundDefaultOperator(syntax, parameterType);
defaultValue = new BoundDefaultLiteral(syntax, parameterType);
}
}
else if (defaultConstantValue.IsNull && parameterType.IsValueType)
{
// We have something like M(int? x = null) or M(S x = default(S)),
// so replace the argument with default(int?).
defaultValue = new BoundDefaultOperator(syntax, parameterType);
defaultValue = new BoundDefaultLiteral(syntax, parameterType);
}
else if (parameterType.IsNullableType())
{
......@@ -1057,20 +1057,20 @@ private BoundExpression GetDefaultParameterSpecial(SyntaxNode syntax, ParameterS
if (parameter.IsMarshalAsObject)
{
// default(object)
defaultValue = new BoundDefaultOperator(syntax, parameter.Type);
defaultValue = new BoundDefaultLiteral(syntax, parameter.Type);
}
else if (parameter.IsIUnknownConstant)
{
// new UnknownWrapper(default(object))
var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_UnknownWrapper__ctor);
var argument = new BoundDefaultOperator(syntax, parameter.Type);
var argument = new BoundDefaultLiteral(syntax, parameter.Type);
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument);
}
else if (parameter.IsIDispatchConstant)
{
// new DispatchWrapper(default(object))
var methodSymbol = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_DispatchWrapper__ctor);
var argument = new BoundDefaultOperator(syntax, parameter.Type);
var argument = new BoundDefaultLiteral(syntax, parameter.Type);
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, argument);
}
else
......
......@@ -175,7 +175,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
if (NullableNeverHasValue(rewrittenOperand))
{
return new BoundDefaultOperator(syntax, rewrittenType);
return new BoundDefaultLiteral(syntax, rewrittenType);
}
BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand);
......@@ -191,7 +191,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
case ConversionKind.DefaultLiteral:
if (!_inExpressionLambda || !explicitCastInCode)
{
return new BoundDefaultOperator(syntax, rewrittenType);
return new BoundDefaultLiteral(syntax, rewrittenType);
}
break;
......@@ -200,7 +200,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
case ConversionKind.ExplicitReference:
if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode))
{
return new BoundDefaultOperator(syntax, rewrittenType);
return new BoundDefaultLiteral(syntax, rewrittenType);
}
break;
......@@ -218,7 +218,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
case ConversionKind.ExplicitNumeric:
if (rewrittenOperand.IsDefaultValue() && (!_inExpressionLambda || !explicitCastInCode))
{
return new BoundDefaultOperator(syntax, rewrittenType);
return new BoundDefaultLiteral(syntax, rewrittenType);
}
if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal)
......@@ -278,7 +278,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
rewrittenOperand.IsDefaultValue() &&
(!_inExpressionLambda || !explicitCastInCode))
{
return new BoundDefaultOperator(syntax, rewrittenType);
return new BoundDefaultLiteral(syntax, rewrittenType);
}
if (rewrittenType.SpecialType == SpecialType.System_Decimal)
......@@ -680,7 +680,7 @@ private static bool NullableNeverHasValue(BoundExpression expression)
}
// default(int?) never has a value.
if (expression.Kind == BoundKind.DefaultOperator)
if (expression.Kind == BoundKind.DefaultLiteral)
{
return true;
}
......@@ -853,7 +853,7 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
conversion.UnderlyingConversions[0],
type.GetNullableUnderlyingType(),
@checked));
BoundExpression alternative = new BoundDefaultOperator(syntax, null, type);
BoundExpression alternative = new BoundDefaultLiteral(syntax, null, type);
BoundExpression conditionalExpression = RewriteConditionalOperator(
syntax: syntax,
rewrittenCondition: condition,
......@@ -881,7 +881,7 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
if (NullableNeverHasValue(operand))
{
return new BoundDefaultOperator(syntax, type);
return new BoundDefaultLiteral(syntax, type);
}
// If the converted expression is known to never be null then we can return
......@@ -910,7 +910,7 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
if (NullableNeverHasValue(operand))
{
return new BoundDefaultOperator(syntax, null, type);
return new BoundDefaultLiteral(syntax, null, type);
}
// Second, a trickier optimization. If the conversion is "(T?)(new S?(x))" then
......@@ -1090,7 +1090,7 @@ private BoundExpression MakeLiftedUserDefinedConversionConsequence(BoundCall cal
BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType);
// default(R?)
BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType);
BoundExpression alternative = new BoundDefaultLiteral(syntax, rewrittenType);
// temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?)
BoundExpression conditionalExpression = RewriteConditionalOperator(
......
......@@ -63,7 +63,7 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre
// replace "new S()" with a default struct ctor with "default(S)"
if (node.Constructor.IsDefaultValueTypeConstructor())
{
rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type);
rewrittenObjectCreation = new BoundDefaultLiteral(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type);
}
if (!temps.IsDefaultOrEmpty)
......@@ -185,7 +185,7 @@ private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typePara
if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method))
{
return new BoundDefaultOperator(syntax, null, type: typeParameter, hasErrors: true);
return new BoundDefaultLiteral(syntax, null, type: typeParameter, hasErrors: true);
}
Debug.Assert((object)method != null);
......
......@@ -199,7 +199,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault);
// default(R?)
BoundExpression alternative = new BoundDefaultOperator(syntax, null, type);
BoundExpression alternative = new BoundDefaultLiteral(syntax, null, type);
// temp.HasValue ?
// new R?(OP(temp.GetValueOrDefault())) :
......@@ -233,7 +233,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
{
if (NullableNeverHasValue(loweredOperand))
{
return new BoundDefaultOperator(syntax, null, type);
return new BoundDefaultLiteral(syntax, null, type);
}
// Second, another simple optimization. If we know that the operand is never null
......@@ -636,7 +636,7 @@ private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator
BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall);
// default(S?)
BoundExpression alternative = new BoundDefaultOperator(syntax, null, type);
BoundExpression alternative = new BoundDefaultLiteral(syntax, null, type);
// temp.HasValue ?
// new S?(op_Increment(temp.GetValueOrDefault())) :
......@@ -802,7 +802,7 @@ private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, Binar
// new decimal?(op_Inc(x.GetValueOrDefault()))
BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall);
// default(decimal?)
BoundExpression alternative = new BoundDefaultOperator(syntax, null, operand.Type);
BoundExpression alternative = new BoundDefaultLiteral(syntax, null, operand.Type);
// x.HasValue ? new decimal?(op_Inc(x.GetValueOrDefault())) : default(decimal?)
return RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type);
......
......@@ -549,7 +549,7 @@ private BoundExpression HoistRefInitialization(SynthesizedLocal local, BoundAssi
case BoundKind.ThisReference:
case BoundKind.BaseReference:
case BoundKind.DefaultOperator:
case BoundKind.DefaultLiteral:
return expr;
default:
......
......@@ -1176,7 +1176,7 @@ public BoundExpression Array(TypeSymbol elementType, BoundExpression length)
internal BoundExpression Default(TypeSymbol type)
{
return new BoundDefaultOperator(Syntax, type) { WasCompilerGenerated = true };
return new BoundDefaultLiteral(Syntax, type) { WasCompilerGenerated = true };
}
internal BoundStatement Try(
......
......@@ -429,7 +429,7 @@ private static bool IsValidDefaultValue(BoundExpression expression)
// Also when valuetype S has a parameterless constructor,
// new S() is clearly not a constant expression and should produce an error
return (expression.ConstantValue != null) ||
(expression.Kind == BoundKind.DefaultOperator) ||
(expression.Kind == BoundKind.DefaultLiteral) ||
(expression.Kind == BoundKind.ObjectCreationExpression &&
IsValidDefaultValue((BoundObjectCreationExpression)expression));
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using System.Linq;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
......@@ -80,16 +82,13 @@ static void M()
dynamic x3 = default;
ITest x5 = default;
T x6 = default;
System.Console.Write($""{x1} {x2} {x3} {x5} {x6}"");
}
}
interface ITest { }
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics(
// (7,14): warning CS0219: The variable 'x2' is assigned but its value is never used
// int? x2 = default;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x2").WithArguments("x2").WithLocation(7, 14)
);
comp.VerifyDiagnostics();
}
[Fact]
......@@ -242,6 +241,65 @@ static void Main()
CompileAndVerify(comp, expectedOutput: "0");
}
[Fact]
public void GenericCast()
{
string source = @"
class C
{
static void M<T>()
{
const T x = default(T);
const T y = (T)default;
const object z = (T)default;
System.Console.Write($""{x} {y} {z}"");
}
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics(
// (6,15): error CS0283: The type 'T' cannot be declared const
// const T x = default(T);
Diagnostic(ErrorCode.ERR_BadConstType, "T").WithArguments("T").WithLocation(6, 15),
// (7,15): error CS0283: The type 'T' cannot be declared const
// const T y = (T)default;
Diagnostic(ErrorCode.ERR_BadConstType, "T").WithArguments("T").WithLocation(7, 15),
// (8,26): error CS0134: 'z' is of type 'object'. A const field of a reference type other than string can only be initialized with null.
// const object z = (T)default;
Diagnostic(ErrorCode.ERR_NotNullConstRefField, "(T)default").WithArguments("z", "object").WithLocation(8, 26)
);
}
[Fact]
public void UserDefinedStruct()
{
string source = @"
struct S { }
class C
{
static void M()
{
const S x = default(S);
const S y = (S)default;
const object z = (S)default;
System.Console.Write($""{x} {y} {z}"");
}
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics(
// (7,15): error CS0283: The type 'S' cannot be declared const
// const S x = default(S);
Diagnostic(ErrorCode.ERR_BadConstType, "S").WithArguments("S").WithLocation(7, 15),
// (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),
// (9,26): error CS0134: 'z' is of type 'object'. A const field of a reference type other than string can only be initialized with null.
// const object z = (S)default;
Diagnostic(ErrorCode.ERR_NotNullConstRefField, "(S)default").WithArguments("z", "object").WithLocation(9, 26)
);
}
[Fact]
public void ImplicitlyTypedArray()
{
......@@ -258,7 +316,15 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "0");
// PROTOTYPE(default) Verify semantic model
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Equal("System.Int32", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
}
[Fact]
......@@ -281,6 +347,22 @@ static void Main()
);
}
[Fact]
public void ArrayConstruction()
{
string source = @"
class C
{
static void Main()
{
var t = new object[default];
}
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics();
}
[Fact]
public void Tuple()
{
......@@ -336,6 +418,15 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyEmitDiagnostics();
CompileAndVerify(comp, expectedOutput: "0 2");
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetDeclaredSymbol(def));
}
[Fact]
......@@ -354,6 +445,28 @@ static int M()
comp.VerifyDiagnostics();
}
[Fact]
public void YieldReturn()
{
string source = @"
using System.Collections;
using System.Collections.Generic;
class C
{
static IEnumerable<int> M()
{
yield return default;
}
static IEnumerable M2()
{
yield return default;
}
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics();
}
[Fact]
public void ReturnNullableType()
{
......@@ -370,7 +483,7 @@ class C
comp.VerifyDiagnostics();
}
[Fact(Skip = "PROTOTYPE(default)")]
[Fact]
public void ConstAndProperty()
{
string source = @"
......@@ -380,17 +493,16 @@ class C
static int P { get { return default; } }
static void Main()
{
System.Console.Write($""{x} {P}"");
System.Console.Write($""{x}-{P}"");
}
}
";
// PROTOTYPE(default) There is a problem with treating default literal as constant (should the constant value in the literal or in the conversion, or somewhere else?)
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyEmitDiagnostics();
CompileAndVerify(comp, expectedOutput: "0 0");
CompileAndVerify(comp, expectedOutput: "0-0");
}
[Fact(Skip = "PROTOTYPE(default)")]
[Fact]
public void DynamicInvocation()
{
string source = @"
......@@ -404,9 +516,280 @@ static void M1()
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics(
// (7,14): error CS9000: Cannot use a type-inferred default operator as an argument to a dynamically dispatched operation.
// d.M2(default);
Diagnostic(ErrorCode.ERR_BadDynamicMethodArgDefault, "default").WithLocation(7, 14)
);
}
[Fact]
public void DefaultEqualsDefault()
{
string source = @"
class C
{
static void Main()
{
System.Console.Write($""{default == default} {default != default}"");
}
}
";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (6,33): error CS0034: Operator '==' is ambiguous on operands of type 'default' and 'default'
// System.Console.Write($"{default == default} {default != default}");
Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default == default").WithArguments("==", "default", "default").WithLocation(6, 33),
// (6,54): error CS0034: Operator '!=' is ambiguous on operands of type 'default' and 'default'
// System.Console.Write($"{default == default} {default != default}");
Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default != default").WithArguments("!=", "default", "default").WithLocation(6, 54)
);
}
[Fact]
public void NormalInitializerType_Default()
{
var text = @"
class Program
{
unsafe static void Main()
{
fixed (int* p = default)
{
}
}
}
";
// Confusing, but matches Dev10.
CreateCompilationWithMscorlib(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.ExperimentalParseOptions)
.VerifyDiagnostics(
// (6,25): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression
// fixed (int* p = default)
Diagnostic(ErrorCode.ERR_FixedNotNeeded, "default").WithLocation(6, 25)
);
}
[Fact]
public void TestErrorDefaultLiteralCollection()
{
var text = @"
class C
{
static void Main()
{
foreach (int x in default) { }
foreach (int x in null) { }
}
}";
var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (6,27): error CS9001: Use of default is not valid in this context
// foreach (int x in default) { }
Diagnostic(ErrorCode.ERR_DefaultNotValid, "default").WithLocation(6, 27),
// (7,27): error CS0186: Use of null is not valid in this context
// foreach (int x in null) { }
Diagnostic(ErrorCode.ERR_NullNotValid, "null").WithLocation(7, 27)
);
}
[Fact]
public void QueryOnDefault()
{
string source =
@"static class C
{
static void Main()
{
var q = from x in default select x;
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.ExperimentalParseOptions);
compilation.VerifyDiagnostics(
// (5,35): error CS9001: Use of default is not valid in this context
// var q = from x in default select x;
Diagnostic(ErrorCode.ERR_DefaultNotValid, "select x").WithLocation(5, 35)
);
}
[Fact]
public void DefaultInConditionalExpression()
{
string source =
@"static class C
{
static void Main()
{
var x = default ? 4 : 5;
System.Console.Write(x);
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "5");
}
[Fact]
public void AlwaysNonNull()
{
string source =
@"static class C
{
static void Main()
{
System.Console.Write((int?)1 == default);
System.Console.Write(default == (int?)1);
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.ExperimentalParseOptions);
compilation.VerifyDiagnostics();
}
[Fact]
public void ThrowDefault()
{
var text = @"
class C
{
static void Main()
{
throw default;
}
}";
var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (6,15): error CS0155: The type caught or thrown must be derived from System.Exception
// throw default;
Diagnostic(ErrorCode.ERR_BadExceptionType, "default").WithLocation(6, 15)
);
}
[Fact]
public void DefaultInAsOperator()
{
var text = @"
class C
{
static void Main()
{
System.Console.Write(default as long);
}
}";
var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (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)
);
}
[Fact]
public void DefaultInIsPattern()
{
var text = @"
class C
{
static void Main()
{
System.Console.Write(default is long);
}
}";
var comp = CreateCompilationWithMscorlib(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (6,30): error CS9001: Use of default is not valid in this context
// System.Console.Write(default is long);
Diagnostic(ErrorCode.ERR_DefaultNotValid, "default is long").WithArguments("long").WithLocation(6, 30)
);
}
[Fact]
public void TypeVarCanBeDefault()
{
var source =
@"interface I { }
class A { }
class B<T1, T2, T3, T4, T5, T6, T7>
where T2 : class
where T3 : struct
where T4 : new()
where T5 : I
where T6 : A
where T7 : T1
{
static void M()
{
T1 t1 = default;
T2 t2 = default;
T3 t3 = default;
T4 t4 = default;
T5 t5 = default;
T6 t6 = default;
T7 t7 = default;
System.Console.Write($""{t1} {t2} {t3} {t4} {t5} {t6} {t7}"");
}
static T1 F1() { return default; }
static T2 F2() { return default; }
static T3 F3() { return default; }
static T4 F4() { return default; }
static T5 F5() { return default; }
static T6 F6() { return default; }
static T7 F7() { return default; }
}";
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics();
// PROTOTYPE(default) Crash
}
[Fact]
public void ExprTreeConvertedNullOnLHS()
{
var text =
@"using System;
using System.Linq.Expressions;
class Program
{
Expression<Func<object>> testExpr = () => default ?? ""hello"";
}";
var comp = CreateCompilationWithMscorlibAndSystemCore(text, parseOptions: TestOptions.ExperimentalParseOptions);
comp.VerifyDiagnostics(
// (6,47): error CS0845: An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side
// Expression<Func<object>> testExpr = () => default ?? "hello";
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, "default").WithLocation(6, 47)
);
}
[Fact]
public void NullableAndDefault()
{
var text =
@"class Program
{
static void Main()
{
int? x = default;
System.Console.Write(x.HasValue);
}
}";
var comp = CreateCompilationWithMscorlibAndSystemCore(text, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "False");
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).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(def).Symbol);
}
[Fact]
......@@ -450,7 +833,7 @@ static System.Func<int> M()
CompileAndVerify(comp, expectedOutput: "0");
}
[Fact(Skip = "PROTOTYPE(default)")]
[Fact]
public void Switch()
{
string source = @"
......@@ -458,14 +841,16 @@ class C
{
static void Main()
{
int x = 1;
M(0);
}
static void M(int x)
{
switch (x)
{
case default: // PROTOTYPE(default) default is not recognized as constant
default:
case default:
System.Console.Write(""default"");
break;
case 1:
System.Console.Write(""1"");
default:
break;
}
}
......@@ -474,7 +859,7 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "1");
CompileAndVerify(comp, expectedOutput: "default");
}
[Fact]
......@@ -597,7 +982,20 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source, parseOptions: TestOptions.ExperimentalParseOptions, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "0");
// PROTOTYPE(default) Verify semantic model. What is the type of default?
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var def = nodes.OfType<DefaultLiteralSyntax>().Single();
Assert.Null(model.GetTypeInfo(def).Type);
Assert.Null(model.GetSymbolInfo(def).Symbol);
Assert.Null(model.GetDeclaredSymbol(def));
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());
}
[Fact]
......
......@@ -1853,7 +1853,7 @@ S MakeS()
No, ObjectCreationExpression 'new S()' is not a non-moveable variable
No, Conversion 'default(S).i' is not a non-moveable variable
No, FieldAccess 'default(S).i' is not a non-moveable variable
No, DefaultOperator 'default(S)' is not a non-moveable variable
No, DefaultLiteral 'default(S)' is not a non-moveable variable
No, Conversion 'MakeS().i' is not a non-moveable variable
No, FieldAccess 'MakeS().i' is not a non-moveable variable
No, Call 'MakeS()' is not a non-moveable variable
......
......@@ -27,6 +27,7 @@ internal enum ConstantValueTypeDiscriminator : byte
String,
Decimal,
DateTime,
DefaultLiteral,
}
internal abstract partial class ConstantValue : IEquatable<ConstantValue>
......@@ -79,6 +80,7 @@ internal abstract partial class ConstantValue : IEquatable<ConstantValue>
public static ConstantValue Bad { get { return ConstantValueBad.Instance; } }
public static ConstantValue Null { get { return ConstantValueNull.Instance; } }
public static ConstantValue DefaultLiteral { get { return ConstantValueDefault.DefaultLiteralConstant; } }
public static ConstantValue Nothing { get { return Null; } }
// Null, Nothing and Unset are all ConstantValueNull. Null and Nothing are equivalent and represent the null and
// nothing constants in C# and VB. Unset indicates an uninitialized ConstantValue.
......@@ -655,6 +657,14 @@ public bool IsNull
}
}
public bool IsDefaultLiteral
{
get
{
return ReferenceEquals(this, DefaultLiteral);
}
}
public bool IsNothing
{
get
......
......@@ -269,6 +269,7 @@ private class ConstantValueDefault : ConstantValueDiscriminated
public static readonly ConstantValueDefault Decimal = new ConstantValueDecimalZero();
public static readonly ConstantValueDefault DateTime = new ConstantValueDefault(ConstantValueTypeDiscriminator.DateTime);
public static readonly ConstantValueDefault Boolean = new ConstantValueDefault(ConstantValueTypeDiscriminator.Boolean);
public static readonly ConstantValueDefault DefaultLiteralConstant = new ConstantValueDefault(ConstantValueTypeDiscriminator.DefaultLiteral);
protected ConstantValueDefault(ConstantValueTypeDiscriminator discriminator)
: base(discriminator)
......
......@@ -102,7 +102,7 @@ private static BoundExpression GetCustomTypeInfoPayloadId(SyntaxNode syntax, Met
{
if (!hasCustomTypeInfoPayload)
{
return new BoundDefaultOperator(syntax, guidConstructor.ContainingType);
return new BoundDefaultLiteral(syntax, guidConstructor.ContainingType);
}
var value = ConstantValue.Create(CustomTypeInfo.PayloadTypeId.ToString());
......
......@@ -3462,7 +3462,7 @@ static void M()
string error;
var testData = new CompilationTestData();
context.CompileExpression("F(() => null ?? new object())", out error, testData);
Assert.Equal(error, "error CS0845: An expression tree lambda may not contain a coalescing operator with a null literal left-hand side");
Assert.Equal(error, "error CS0845: An expression tree lambda may not contain a coalescing operator with a null or default literal left-hand side");
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册