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

Two bug fixes - disallow "new (int, int)()" and induce a failure if ValueTuple is not a struct. #14161

Merged
merged 13 commits into from
Oct 4, 2016
31 changes: 20 additions & 11 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3405,20 +3405,17 @@ private BoundExpression BindClassCreationExpression(ObjectCreationExpressionSynt
// new C(__arglist()) is legal
BindArgumentsAndNames(node.ArgumentList, diagnostics, analyzedArguments, allowArglist: true);

// No point in performing overload resolution if the type is static. Just return a bad expression containing
// the arguments.
// No point in performing overload resolution if the type is static or a tuple literal.
// Just return a bad expression containing the arguments.
if (type.IsStatic)
{
diagnostics.Add(ErrorCode.ERR_InstantiatingStaticClass, node.Location, type);

var children = ArrayBuilder<BoundNode>.GetInstance();
children.AddRange(BuildArgumentsForErrorRecovery(analyzedArguments));
if (boundInitializerOpt != null)
{
children.Add(boundInitializerOpt);
}

return new BoundBadExpression(node, LookupResultKind.NotCreatable, ImmutableArray.Create<Symbol>(type), children.ToImmutableAndFree(), type);
return MakeBadExpressionForObjectCreation(node, type, boundInitializerOpt, analyzedArguments);
}
else if (node.Type.Kind() == SyntaxKind.TupleType)
{
Debug.Assert(node.HasErrors, "new <tuple type> should be a syntax error");
return MakeBadExpressionForObjectCreation(node, type, boundInitializerOpt, analyzedArguments);
}

return BindClassCreationExpression(node, typeName, node.Type, type, analyzedArguments, diagnostics, boundInitializerOpt);
Expand All @@ -3429,6 +3426,18 @@ private BoundExpression BindClassCreationExpression(ObjectCreationExpressionSynt
}
}

private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpressionSyntax node, NamedTypeSymbol type, BoundExpression boundInitializerOpt, AnalyzedArguments analyzedArguments)
{
var children = ArrayBuilder<BoundNode>.GetInstance();
children.AddRange(BuildArgumentsForErrorRecovery(analyzedArguments));
if (boundInitializerOpt != null)
{
children.Add(boundInitializerOpt);
}

return new BoundBadExpression(node, LookupResultKind.NotCreatable, ImmutableArray.Create<Symbol>(type), children.ToImmutableAndFree(), type);
}

private BoundExpression BindInitializerExpressionOrValue(
ExpressionSyntax syntax,
TypeSymbol type,
Expand Down
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4930,6 +4930,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_TupleLiteralNameMismatch_Title" xml:space="preserve">
<value>The tuple element name is ignored because a different name is specified by the assignment target.</value>
</data>
<data name="ERR_PredefinedValueTupleTypeMustBeStruct" xml:space="preserve">
<value>Predefined type '{0}' must be a struct.</value>
</data>
<data name="ERR_NewWithTupleTypeSyntax" xml:space="preserve">
<value>'new' cannot be used with tuple type. Use a tuple literal expression instead.</value>
</data>
<data name="ERR_DeconstructionVarFormDisallowsSpecificType" xml:space="preserve">
<value>Deconstruction 'var (...)' form disallows a specific type for 'var'.</value>
</data>
Expand Down
20 changes: 19 additions & 1 deletion src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal abstract class PEModuleBuilder : PEModuleBuilder<CSharpCompilation, Sou
// TODO: Need to estimate amount of elements for this map and pass that value to the constructor.
protected readonly ConcurrentDictionary<Symbol, Cci.IModuleReference> AssemblyOrModuleSymbolToModuleRefMap = new ConcurrentDictionary<Symbol, Cci.IModuleReference>();
private readonly ConcurrentDictionary<Symbol, object> _genericInstanceMap = new ConcurrentDictionary<Symbol, object>();
private readonly ConcurrentSet<ErrorTypeSymbol> _reportedErrorTypesMap = new ConcurrentSet<ErrorTypeSymbol>();
private readonly ConcurrentSet<TypeSymbol> _reportedErrorTypesMap = new ConcurrentSet<TypeSymbol>();
Copy link
Member

@gafter gafter Sep 29, 2016

Choose a reason for hiding this comment

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

I think this set should use an identity comparer. #Resolved

Copy link
Member Author

@VSadov VSadov Sep 29, 2016

Choose a reason for hiding this comment

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

Will change to identity.
Note that this is an optimization for the number of errors spitted out in extremely rare and badly broken cases. I do not think it makes a lot of difference either way. #Resolved


private readonly NoPia.EmbeddedTypesManager _embeddedTypesManagerOpt;
public override NoPia.EmbeddedTypesManager EmbeddedTypesManagerOpt
Expand Down Expand Up @@ -790,6 +790,7 @@ internal Cci.INamedTypeReference Translate(
{
Debug.Assert(!needDeclaration);
namedTypeSymbol = namedTypeSymbol.TupleUnderlyingType;
CheckTupleUnderlying(namedTypeSymbol, syntaxNodeOpt, diagnostics);
}

// Substitute error types with a special singleton object.
Expand Down Expand Up @@ -890,6 +891,23 @@ internal Cci.INamedTypeReference Translate(
return namedTypeSymbol;
}

private void CheckTupleUnderlying(NamedTypeSymbol namedTypeSymbol, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
{
// check that underlying type of a ValueTuple is indeed a value type (or error)
// this should never happen, in theory,
// but if it does happen we should make it a failure.
// NOTE: declaredBase could be null for interfaces
var declaredBase = namedTypeSymbol.BaseTypeNoUseSiteDiagnostics;
if (declaredBase?.SpecialType != SpecialType.System_ValueType && declaredBase?.IsErrorType() != true)
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 4, 2016

Choose a reason for hiding this comment

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

!= true [](start = 104, length = 8)

Is there a test that backs this change? #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.

ValueTupleNotStruct2i


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

Copy link
Member Author

Choose a reason for hiding this comment

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

It covers the case where ValueTuple is an interface. (and it was indeed causing a crash)


In reply to: 81813233 [](ancestors = 81813233,81812665)

{
// Try to decrease noise by not complaining about the same type over and over again.
if (_reportedErrorTypesMap.Add(namedTypeSymbol))
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_PredefinedValueTupleTypeMustBeStruct, namedTypeSymbol.MetadataName), syntaxNodeOpt == null ? NoLocation.Singleton : syntaxNodeOpt.Location));
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 3, 2016

Choose a reason for hiding this comment

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

diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_PredefinedValueTupleTypeMustBeStruct, namedTypeSymbol.MetadataName), syntaxNodeOpt == null ? NoLocation.Singleton : syntaxNodeOpt.Location)); [](start = 20, length = 209)

Do we have a test for a nested case. #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.

nested as in ... example?


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

Copy link
Contributor

@AlekseyTs AlekseyTs Oct 3, 2016

Choose a reason for hiding this comment

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

A tuple of cardinality >= 8 is used and VT`8 is not a structure, or "short" VT is not a structure, or both are not structures. #Resolved

}
}
}

public static bool IsGenericType(NamedTypeSymbol toCheck)
{
while ((object)toCheck != null)
Expand Down
4 changes: 3 additions & 1 deletion src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1428,8 +1428,10 @@ internal enum ErrorCode

ERR_PredefinedValueTupleTypeNotFound = 8179,
ERR_SemiOrLBraceOrArrowExpected = 8180,
ERR_NewWithTupleTypeSyntax = 8181,
ERR_PredefinedValueTupleTypeMustBeStruct = 8182,

// Available = 8181-8195
// Available = 8183-8195

#region diagnostics for out var
ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196,
Expand Down
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10676,7 +10676,13 @@ private ExpressionSyntax ParseArrayOrObjectCreationExpression()
SyntaxFactory.MissingToken(SyntaxKind.CloseParenToken));
}

return _syntaxFactory.ObjectCreationExpression(@new, type, argumentList, initializer);
var objectCreation = _syntaxFactory.ObjectCreationExpression(@new, type, argumentList, initializer);
if (type.Kind == SyntaxKind.TupleType)
Copy link
Member

@gafter gafter Sep 30, 2016

Choose a reason for hiding this comment

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

Might be nicer to put the error on the tuple type. #ByDesign

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 tried to figure what is at fault here - the "new" or the tuple literal. And ended up deciding to flag the whole thing since it is the combination that is invalid.


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

{
objectCreation = this.AddError(objectCreation, type, ErrorCode.ERR_NewWithTupleTypeSyntax);
}

return objectCreation;
}
}

Expand Down
23 changes: 3 additions & 20 deletions src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -663,22 +663,6 @@ internal override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiagnostics
return _underlyingType.InterfacesNoUseSiteDiagnostics(basesBeingResolved);
}

public override bool IsReferenceType
{
get
{
return _underlyingType.IsErrorType() ? false : _underlyingType.IsReferenceType;
}
}

public override bool IsValueType
{
get
{
return _underlyingType.IsErrorType() || _underlyingType.IsValueType;
}
}

internal sealed override bool IsManagedType
{
get
Expand Down Expand Up @@ -1097,10 +1081,9 @@ public override TypeKind TypeKind
{
get
{
// only classes and structs can have instance fields as tuple requires.
// we need to have some support for classes, but most common case will be struct
// in broken scenarios (ErrorType, Enum, Delegate, whatever..) we will just default to struct.
return _underlyingType.TypeKind == TypeKind.Class ? TypeKind.Class : TypeKind.Struct;
// From the language perspective tuple is a value type
// composed of its underlying elements
return TypeKind.Struct;
}
}

Expand Down
Loading