Skip to content

Commit

Permalink
Merge pull request #32704 from dotnet/features/unmanaged-constructed-…
Browse files Browse the repository at this point in the history
…types

Features/unmanaged constructed types
  • Loading branch information
RikkiGibson authored Jan 23, 2019
2 parents 6890282 + ffc2957 commit 7b704d6
Show file tree
Hide file tree
Showing 51 changed files with 1,416 additions and 291 deletions.
35 changes: 23 additions & 12 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1142,13 +1142,7 @@ private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, DiagnosticBag di
AliasSymbol alias;
TypeSymbol type = this.BindType(typeSyntax, diagnostics, out alias).TypeSymbol;

bool typeHasErrors = type.IsErrorType();

if (!typeHasErrors && type.IsManagedType)
{
diagnostics.Add(ErrorCode.ERR_ManagedAddr, node.Location, type);
typeHasErrors = true;
}
bool typeHasErrors = type.IsErrorType() || CheckManagedAddr(type, node, diagnostics);

BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, type, typeHasErrors);
ConstantValue constantValue = GetConstantSizeOf(type);
Expand All @@ -1157,6 +1151,24 @@ private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, DiagnosticBag di
this.GetSpecialType(SpecialType.System_Int32, diagnostics, node), hasErrors);
}

/// <returns>true if managed type-related errors were found, otherwise false.</returns>
private static bool CheckManagedAddr(TypeSymbol type, SyntaxNode node, DiagnosticBag diagnostics)
{
var managedKind = type.ManagedKind;
if (managedKind == ManagedKind.Managed)
{
diagnostics.Add(ErrorCode.ERR_ManagedAddr, node.Location, type);
return true;
}
else if (managedKind == ManagedKind.UnmanagedWithGenerics)
{
var supported = CheckFeatureAvailability(node, MessageID.IDS_FeatureUnmanagedConstructedTypes, diagnostics);
return !supported;
}

return false;
}

internal static ConstantValue GetConstantSizeOf(TypeSymbol type)
{
return ConstantValue.CreateSizeOf((type.GetEnumUnderlyingType() ?? type).SpecialType);
Expand Down Expand Up @@ -2882,9 +2894,9 @@ private BoundExpression BindImplicitStackAllocArrayCreationExpression(ImplicitSt
bestType = CreateErrorType();
}

if (!bestType.IsErrorType() && bestType.IsManagedType)
if (!bestType.IsErrorType())
{
Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, bestType);
CheckManagedAddr(bestType, node, diagnostics);
}

return BindStackAllocWithInitializer(
Expand Down Expand Up @@ -3235,10 +3247,9 @@ private BoundExpression BindStackAllocArrayCreationExpression(
var elementType = BindType(elementTypeSyntax, diagnostics);

TypeSymbol type = GetStackAllocType(node, elementType, diagnostics, out bool hasErrors);
if (!elementType.IsErrorType() && elementType.IsManagedType)
if (!elementType.IsErrorType())
{
Error(diagnostics, ErrorCode.ERR_ManagedAddr, elementTypeSyntax, elementType.TypeSymbol);
hasErrors = true;
hasErrors = hasErrors || CheckManagedAddr(elementType.TypeSymbol, elementTypeSyntax, diagnostics);
}

SyntaxList<ArrayRankSpecifierSyntax> rankSpecifiers = arrayTypeSyntax.RankSpecifiers;
Expand Down
5 changes: 2 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2117,10 +2117,9 @@ private BoundExpression BindAddressOfExpression(PrefixUnaryExpressionSyntax node
bool allowManagedAddressOf = Flags.Includes(BinderFlags.AllowManagedAddressOf);
if (!allowManagedAddressOf)
{
if (!hasErrors && isManagedType)
if (!hasErrors)
{
hasErrors = true;
Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, operandType);
hasErrors = CheckManagedAddr(operandType, node, diagnostics);
}

if (!hasErrors)
Expand Down
3 changes: 1 addition & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,9 +1209,8 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym
}
}

if (elementType.IsManagedType)
if (CheckManagedAddr(elementType, initializerSyntax, diagnostics))
{
Error(diagnostics, ErrorCode.ERR_ManagedAddr, initializerSyntax, elementType);
hasErrors = true;
}

Expand Down
25 changes: 14 additions & 11 deletions src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,9 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS
// Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
Error(diagnostics, ErrorCode.ERR_BadConstraintType, node);
}
else if (elementType.IsManagedType)
else
{
// "Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}')"
Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, elementType.TypeSymbol);
CheckManagedAddr(elementType.TypeSymbol, node, diagnostics);
}

return TypeSymbolWithAnnotations.Create(new PointerTypeSymbol(elementType));
Expand Down Expand Up @@ -2233,6 +2232,17 @@ internal static bool CheckFeatureAvailability(SyntaxTree tree, MessageID feature
return false;
}

internal static CSDiagnosticInfo GetLanguageVersionDiagnosticInfo(LanguageVersion availableVersion, MessageID feature)
{
LanguageVersion requiredVersion = feature.RequiredVersion();
if (requiredVersion > availableVersion)
{
return new CSDiagnosticInfo(availableVersion.GetErrorCode(), feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion));
}

return null;
}

private static CSDiagnosticInfo GetFeatureAvailabilityDiagnosticInfo(SyntaxTree tree, MessageID feature)
{
CSharpParseOptions options = (CSharpParseOptions)tree.Options;
Expand All @@ -2248,14 +2258,7 @@ private static CSDiagnosticInfo GetFeatureAvailabilityDiagnosticInfo(SyntaxTree
return new CSDiagnosticInfo(ErrorCode.ERR_FeatureIsExperimental, feature.Localize(), requiredFeature);
}

LanguageVersion availableVersion = options.LanguageVersion;
LanguageVersion requiredVersion = feature.RequiredVersion();
if (requiredVersion > availableVersion)
{
return new CSDiagnosticInfo(availableVersion.GetErrorCode(), feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion));
}

return null;
return GetLanguageVersionDiagnosticInfo(options.LanguageVersion, feature);
}
}
}
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Imports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ private void Validate()
{
var typeSymbol = (TypeSymbol)@using.NamespaceOrType;
var location = @using.UsingDirective?.Name.Location ?? NoLocation.Singleton;
typeSymbol.CheckAllConstraints(conversions, location, semanticDiagnostics);
typeSymbol.CheckAllConstraints(_compilation, conversions, location, semanticDiagnostics);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3188,7 +3188,7 @@ private MemberResolutionResult<TMember> IsApplicable<TMember>(
var parameterTypes = leastOverriddenMember.GetParameterTypes();
for (int i = 0; i < parameterTypes.Length; i++)
{
if (!parameterTypes[i].TypeSymbol.CheckAllConstraints(Conversions))
if (!parameterTypes[i].TypeSymbol.CheckAllConstraints(Compilation, Conversions))
{
return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ConstructedParameterFailedConstraintsCheck(i));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ private bool HadConstructedParameterFailedConstraintCheck(
// formal parameter type.

TypeSymbol formalParameterType = method.ParameterTypes[result.Result.BadParameter].TypeSymbol;
formalParameterType.CheckAllConstraints(conversions, location, diagnostics);
formalParameterType.CheckAllConstraints((CSharpCompilation)compilation, conversions, location, diagnostics);

return true;
}
Expand Down
9 changes: 9 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@
<data name="IDS_FeatureAsyncStreams" xml:space="preserve">
<value>async streams</value>
</data>
<data name="IDS_FeatureUnmanagedConstructedTypes" xml:space="preserve">
<value>unmanaged constructed types</value>
</data>
<data name="IDS_FeatureDefaultLiteral" xml:space="preserve">
<value>default literal</value>
</data>
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ internal enum MessageID
IDS_Disposable = MessageBase + 12753,
IDS_FeatureUsingDeclarations = MessageBase + 12754,
IDS_FeatureStaticLocalFunctions = MessageBase + 12755,
IDS_FeatureUnmanagedConstructedTypes = MessageBase + 12756
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -252,6 +253,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureRecursivePatterns:
case MessageID.IDS_FeatureUsingDeclarations:
case MessageID.IDS_FeatureStaticLocalFunctions:
case MessageID.IDS_FeatureUnmanagedConstructedTypes: // semantic check
return LanguageVersion.CSharp8;

// C# 7.3 features.
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ internal void CheckConstraints(DiagnosticBag diagnostics)
{
var corLibrary = this.ContainingAssembly.CorLibrary;
var conversions = new TypeConversions(corLibrary);
target.CheckAllConstraints(conversions, _locations[0], diagnostics);
target.CheckAllConstraints(DeclaringCompilation, conversions, _locations[0], diagnostics);
}
}

Expand Down
8 changes: 1 addition & 7 deletions src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,7 @@ public override bool IsValueType
}
}

internal sealed override bool IsManagedType
{
get
{
return true;
}
}
internal sealed override ManagedKind ManagedKind => ManagedKind.Managed;

public sealed override bool IsRefLikeType
{
Expand Down
84 changes: 49 additions & 35 deletions src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet<Symbol> p
/// IsManagedType is simple for most named types:
/// enums are not managed;
/// non-enum, non-struct named types are managed;
/// generic types and their nested types are managed;
/// type parameters are managed;
/// type parameters are managed unless an 'unmanaged' constraint is present;
/// all special types have spec'd values (basically, (non-string) primitives) are not managed;
///
/// Only structs are complicated, because the definition is recursive. A struct type is managed
Expand All @@ -106,30 +105,40 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet<Symbol> p
/// be managed even if it had no fields. e.g. struct S { S s; } is not managed, but struct S { S s; object o; }
/// is because we can point to object.
/// </summary>
internal static bool IsManagedType(NamedTypeSymbol type)
internal static ManagedKind GetManagedKind(NamedTypeSymbol type)
{
// If this is a type with an obvious answer, return quickly.
switch (IsManagedTypeHelper(type))
var (isManaged, hasGenerics) = IsManagedTypeHelper(type);
var definitelyManaged = isManaged == ThreeState.True;
if (isManaged == ThreeState.Unknown)
{
case ThreeState.True:
return true;
case ThreeState.False:
return false;
// Otherwise, we have to build and inspect the closure of depended-upon types.
var hs = PooledHashSet<Symbol>.GetInstance();
var result = DependsOnDefinitelyManagedType(type, hs);
definitelyManaged = result.definitelyManaged;
hasGenerics = hasGenerics || result.hasGenerics;
hs.Free();
}

// Otherwise, we have to build and inspect the closure of depended-upon types.
var hs = PooledHashSet<Symbol>.GetInstance();
bool result = DependsOnDefinitelyManagedType(type, hs);
hs.Free();
return result;

if (definitelyManaged)
{
return ManagedKind.Managed;
}
else if (hasGenerics)
{
return ManagedKind.UnmanagedWithGenerics;
}
else
{
return ManagedKind.Unmanaged;
}
}

private static bool DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet<Symbol> partialClosure)
private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet<Symbol> partialClosure)
{
Debug.Assert((object)type != null);

// NOTE: unlike in StructDependsClosure, we don't have to check for expanding cycles,
// because as soon as we see something with non-zero arity we kick out (generic => managed).
var hasGenerics = false;
if (partialClosure.Add(type))
{
foreach (var member in type.GetInstanceFieldsAndEvents())
Expand Down Expand Up @@ -170,45 +179,56 @@ private static bool DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet
{
if (fieldType.IsManagedType)
{
return true;
return (true, hasGenerics);
}
}
else
{
// NOTE: don't use IsManagedType on a NamedTypeSymbol - that could lead
var result = IsManagedTypeHelper(fieldNamedType);
hasGenerics = hasGenerics || result.hasGenerics;
// NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead
// to infinite recursion.
switch (IsManagedTypeHelper(fieldNamedType))
switch (result.isManaged)
{
case ThreeState.True:
return true;
return (true, hasGenerics);

case ThreeState.False:
continue;

case ThreeState.Unknown:
if (DependsOnDefinitelyManagedType(fieldNamedType, partialClosure))
if (!fieldNamedType.OriginalDefinition.KnownCircularStruct)
{
return true;
var (definitelyManaged, childHasGenerics) = DependsOnDefinitelyManagedType(fieldNamedType, partialClosure);
hasGenerics = hasGenerics || childHasGenerics;
if (definitelyManaged)
{
return (true, hasGenerics);
}
}
continue;
}
}
}
}

return false;
return (false, hasGenerics);
}

/// <summary>
/// Returns a boolean value if we can determine whether the type is managed
/// without looking at its fields and Unset otherwise.
/// </summary>
private static ThreeState IsManagedTypeHelper(NamedTypeSymbol type)
private static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(NamedTypeSymbol type)
{
// To match dev10, we treat enums as their underlying types.
if (type.IsEnumType())
{
type = type.GetEnumUnderlyingType();
}

bool hasGenerics = type.TupleUnderlyingTypeOrSelf().GetArity() > 0;

// Short-circuit common cases.
switch (type.SpecialType)
{
Expand All @@ -231,26 +251,20 @@ private static ThreeState IsManagedTypeHelper(NamedTypeSymbol type)
case SpecialType.System_TypedReference:
case SpecialType.System_ArgIterator:
case SpecialType.System_RuntimeArgumentHandle:
return ThreeState.False;
return (ThreeState.False, hasGenerics);
case SpecialType.None:
default:
// CONSIDER: could provide cases for other common special types.
break; // Proceed with additional checks.
}

if (type.AllTypeArgumentCount() > 0)
{
return ThreeState.True;
}

switch (type.TypeKind)
{
case TypeKind.Enum:
return ThreeState.False;
return (ThreeState.False, hasGenerics);
case TypeKind.Struct:
return ThreeState.Unknown;
return (ThreeState.Unknown, hasGenerics);
default:
return ThreeState.True;
return (ThreeState.True, hasGenerics);
}
}

Expand Down
Loading

0 comments on commit 7b704d6

Please sign in to comment.