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

Unify nint and IntPtr #60913

Merged
merged 15 commits into from
May 18, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,7 @@ private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgum
var type = typeWithAnnotations.Type;
if (type.IsDynamic()
|| (typeWithAnnotations.NullableAnnotation.IsAnnotated() && !type.IsValueType)
|| type.IsNativeIntegerType
|| type.IsNativeIntegerWrapperType
|| (type.IsTupleType && !type.TupleElementNames.IsDefault))
{
diagnostics.Add(ErrorCode.ERR_AttrDependentTypeNotAllowed, errorLocation, topLevelType.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat));
Expand Down
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1394,7 +1394,7 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution(

if (kind == UnaryOperatorKind.UnaryMinus &&
(object)operand.Type != null &&
(operand.Type.SpecialType == SpecialType.System_UInt64 || (operand.Type.SpecialType == SpecialType.System_UIntPtr && operand.Type.IsNativeIntegerType)))
(operand.Type.SpecialType == SpecialType.System_UInt64 || isNuint(operand.Type)))
{
resultKind = LookupResultKind.OverloadResolutionFailure;
}
Expand Down Expand Up @@ -1422,6 +1422,12 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution(

result.Free();
return possiblyBest;

static bool isNuint(TypeSymbol type)
{
return type.SpecialType == SpecialType.System_UIntPtr
&& type.IsNativeIntegerType;
}
}

private static object FoldDecimalBinaryOperators(BinaryOperatorKind kind, ConstantValue valueLeft, ConstantValue valueRight)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ private void GetAllBuiltInOperators(BinaryOperatorKind kind, bool isChecked, Bou
}
else
{
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableNativeIntegerType() && !right.Type.IsNativeIntegerOrNullableNativeIntegerType());
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableThereof() && !right.Type.IsNativeIntegerOrNullableThereof());

// SPEC 7.3.4: For predefined enum and delegate operators, the only operators
// considered are those defined by an enum or delegate type that is the binding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ private void GetAllBuiltInOperators(UnaryOperatorKind kind, bool isChecked, Boun
// specification to match the previous implementation.

var operators = ArrayBuilder<UnaryOperatorSignature>.GetInstance();
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableNativeIntegerType());
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableThereof());

GetEnumOperations(kind, operand, operators);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3033,7 +3033,7 @@ private static bool IsSignedIntegralType(TypeSymbol type)
case SpecialType.System_Int16:
case SpecialType.System_Int32:
case SpecialType.System_Int64:
case SpecialType.System_IntPtr:
case SpecialType.System_IntPtr when type.IsNativeIntegerType:
return true;

default:
Expand All @@ -3054,7 +3054,7 @@ private static bool IsUnsignedIntegralType(TypeSymbol type)
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
case SpecialType.System_UIntPtr:
case SpecialType.System_UIntPtr when type.IsNativeIntegerType:
return true;

default:
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr

if (returnType.HasType)
{
if (returnType.Type.ContainsNativeInteger())
if (compilation.ShouldEmitNativeIntegerAttributes(returnType.Type))
{
compilation.EnsureNativeIntegerAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3998,6 +3998,11 @@ internal bool EmitNullablePublicOnly
}
}

internal bool ShouldEmitNativeIntegerAttributes()
{
return !Assembly.RuntimeSupportsNumericIntPtr;
}

internal bool ShouldEmitNullableAttributes(Symbol symbol)
{
RoslynDebug.Assert(symbol is object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand All @@ -17,5 +18,10 @@ internal static bool IsFeatureEnabled(this SyntaxNode? syntax, MessageID feature
{
return ((CSharpParseOptions?)syntax?.SyntaxTree.Options)?.IsFeatureEnabled(feature) == true;
}

internal static bool ShouldEmitNativeIntegerAttributes(this CSharpCompilation compilation, TypeSymbol type)
{
return compilation.ShouldEmitNativeIntegerAttributes() && type.ContainsNativeIntegerWrapperType();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ private void CreateEmbeddedAttributesIfNeeded(BindingDiagnosticBag diagnostics)

if ((needsAttributes & EmbeddableAttributes.NativeIntegerAttribute) != 0)
{
Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());
CreateAttributeIfNeeded(
ref _lazyNativeIntegerAttribute,
diagnostics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1667,7 +1667,8 @@ internal SynthesizedAttributeData SynthesizePreserveBaseOverridesAttribute()
internal SynthesizedAttributeData SynthesizeNativeIntegerAttribute(Symbol symbol, TypeSymbol type)
{
Debug.Assert((object)type != null);
Debug.Assert(type.ContainsNativeInteger());
Debug.Assert(type.ContainsNativeIntegerWrapperType());
Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());

if ((object)Compilation.SourceModule != symbol.ContainingModule)
{
Expand Down Expand Up @@ -1702,6 +1703,8 @@ internal SynthesizedAttributeData SynthesizeNativeIntegerAttribute(Symbol symbol

internal virtual SynthesizedAttributeData SynthesizeNativeIntegerAttribute(WellKnownMember member, ImmutableArray<TypedConstant> arguments)
{
Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());

// For modules, this attribute should be present. Only assemblies generate and embed this type.
// https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
return Compilation.TrySynthesizeAttribute(member, arguments, isOptionalUse: true);
Expand Down Expand Up @@ -1776,6 +1779,7 @@ internal void EnsureNullableContextAttributeExists()

internal void EnsureNativeIntegerAttributeExists()
{
Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());
EnsureEmbeddableAttributeExists(EmbeddableAttributes.NativeIntegerAttribute);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,15 @@ private void EnsureAttributesExist(TypeCompilationState compilationState)

ParameterHelpers.EnsureIsReadOnlyAttributeExists(moduleBuilder, Parameters);

if (ReturnType.ContainsNativeInteger())
if (moduleBuilder.Compilation.ShouldEmitNativeIntegerAttributes())
{
moduleBuilder.EnsureNativeIntegerAttributeExists();
}
if (ReturnType.ContainsNativeIntegerWrapperType())
{
moduleBuilder.EnsureNativeIntegerAttributeExists();
}

ParameterHelpers.EnsureNativeIntegerAttributeExists(moduleBuilder, Parameters);
ParameterHelpers.EnsureNativeIntegerAttributeExists(moduleBuilder, Parameters);
}

if (compilationState.Compilation.ShouldEmitNullableAttributes(this))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,13 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
moduleBuilder.EnsureIsUnmanagedAttributeExists();
}

if (hasReturnTypeOrParameter(localFunction, t => t.ContainsNativeInteger()) ||
typeParameters.Any(t => t.ConstraintTypesNoUseSiteDiagnostics.Any(t => t.ContainsNativeInteger())))
if (_compilation.ShouldEmitNativeIntegerAttributes())
{
moduleBuilder.EnsureNativeIntegerAttributeExists();
if (hasReturnTypeOrParameter(localFunction, t => t.ContainsNativeIntegerWrapperType()) ||
typeParameters.Any(t => t.ConstraintTypesNoUseSiteDiagnostics.Any(t => t.ContainsNativeIntegerWrapperType())))
{
moduleBuilder.EnsureNativeIntegerAttributeExists();
}
}

if (_factory.CompilationState.Compilation.ShouldEmitNullableAttributes(localFunction))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private BoundExpression MakeConversionNodeCore(
rewrittenType: rewrittenType);

case ConversionKind.IntPtr:
return RewriteIntPtrConversion(oldNodeOpt, syntax, rewrittenOperand, conversion, @checked,
return RewriteIntPtrConversion(syntax, rewrittenOperand, conversion, @checked,
explicitCastInCode, constantValueOpt, rewrittenType);

case ConversionKind.ImplicitNullable:
Expand Down Expand Up @@ -1288,7 +1288,6 @@ private BoundExpression RewriteLiftedUserDefinedConversion(
}

private BoundExpression RewriteIntPtrConversion(
BoundConversion? oldNode,
SyntaxNode syntax,
BoundExpression rewrittenOperand,
Conversion conversion,
Expand All @@ -1300,6 +1299,7 @@ private BoundExpression RewriteIntPtrConversion(
Debug.Assert(rewrittenOperand != null);
Debug.Assert((object)rewrittenType != null);
Debug.Assert(rewrittenOperand.Type is { });
Debug.Assert(!_compilation.Assembly.RuntimeSupportsNumericIntPtr);
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved

TypeSymbol source = rewrittenOperand.Type;
TypeSymbol target = rewrittenType;
Expand Down Expand Up @@ -1602,6 +1602,7 @@ private Conversion TryMakeConversion(SyntaxNode syntax, Conversion conversion, T
}
case ConversionKind.IntPtr:
{
Debug.Assert(!_compilation.Assembly.RuntimeSupportsNumericIntPtr);
SpecialMember member = GetIntPtrConversionMethod(fromType, toType);
MethodSymbol method;
if (!TryGetSpecialTypeMethod(syntax, member, out method))
Expand Down
19 changes: 18 additions & 1 deletion src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,24 @@ internal bool RuntimeSupportsStaticAbstractMembersInInterfaces
get => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__VirtualStaticsInInterfaces);
}

private bool RuntimeSupportsFeature(SpecialMember feature)
/// <summary>
/// Whether the target runtime supports numeric IntPtr types.
/// This test hook should be removed once TargetFramework.Net70 is added.
/// Tracked by https://github.com/dotnet/roslyn/issues/61235
/// </summary>
internal virtual bool RuntimeSupportsNumericIntPtr
Copy link
Member

@cston cston May 16, 2022

Choose a reason for hiding this comment

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

virtual

Where is this base implementation used? It looks like the only AssemblySymbol implementation that does not override this is RetargetingAssemblySymbol. If so, consider making this abstract and adding an appropriate override for that case. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

There's also MissingAssemblySymbol.
FWIW, I'm following the model from original DIM PR, trying to stick to an established pattern as much as possible (since we're going to have to do this regularly).

{
get
{
// CorLibrary should never be null, but that invariant is broken in some cases for MissingAssemblySymbol.
// Tracked by https://github.com/dotnet/roslyn/issues/61262
return CorLibrary?.RuntimeSupportsNumericIntPtr == true;
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2022

Choose a reason for hiding this comment

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

RuntimeSupportsNumericIntPtr

Could this access and the one below go into an infinite loop when the current assembly is the CorLibrary? #Resolved

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 don't think so. If CorLibrary is available, then we'll call MetadataOrSourceAssemblySymbol.RuntimeSupportsNumericIntPtr. If it's missing then we'll call MissingCorLibrarySymbol.RuntimeSupportsNumericIntPtr.

FWIW, I followed the same test hook pattern from DIM: #17927

}

set => CorLibrary.RuntimeSupportsNumericIntPtr = value;
}

protected bool RuntimeSupportsFeature(SpecialMember feature)
{
Debug.Assert((SpecialType)SpecialMembers.GetDescriptor(feature).DeclaringTypeId == SpecialType.System_Runtime_CompilerServices_RuntimeFeature);
return GetSpecialType(SpecialType.System_Runtime_CompilerServices_RuntimeFeature) is { TypeKind: TypeKind.Class, IsStatic: true } &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ internal void EnsureNullableContextAttributeExists(BindingDiagnosticBag? diagnos

internal void EnsureNativeIntegerAttributeExists(BindingDiagnosticBag? diagnostics, Location location, bool modifyCompilation)
{
Debug.Assert(ShouldEmitNativeIntegerAttributes());
EnsureEmbeddableAttributeExists(EmbeddableAttributes.NativeIntegerAttribute, diagnostics, location, modifyCompilation);
}

Expand Down Expand Up @@ -593,6 +594,7 @@ internal bool CheckIfAttributeShouldBeEmbedded(EmbeddableAttributes attribute, B

case EmbeddableAttributes.NativeIntegerAttribute:
// If the type exists, we'll check both constructors, regardless of which one(s) we'll eventually need.
Debug.Assert(ShouldEmitNativeIntegerAttributes());
return CheckIfAttributeShouldBeEmbedded(
diagnosticsOpt,
locationOpt,
Expand Down Expand Up @@ -983,6 +985,7 @@ internal static class NativeIntegerTransformsEncoder
{
internal static void Encode(ArrayBuilder<bool> builder, TypeSymbol type)
{
Debug.Assert(type.ContainingAssembly?.RuntimeSupportsNumericIntPtr != true);
type.VisitType((typeSymbol, builder, isNested) => AddFlags(typeSymbol, builder), builder);
}

Expand All @@ -992,7 +995,7 @@ private static bool AddFlags(TypeSymbol type, ArrayBuilder<bool> builder)
{
case SpecialType.System_IntPtr:
case SpecialType.System_UIntPtr:
builder.Add(type.IsNativeIntegerType);
builder.Add(type.IsNativeIntegerWrapperType);
break;
}
// Continue walking types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
internal struct NativeIntegerTypeDecoder
{
internal static TypeSymbol TransformType(TypeSymbol type, EntityHandle handle, PEModuleSymbol containingModule)
internal static TypeSymbol TransformType(TypeSymbol type, EntityHandle handle, PEModuleSymbol containingModule, TypeSymbol? containingType)
{
// Note: We avoid any cycles when loading members of System.Runtime.CompilerServices.RuntimeFeature
if (containingType?.SpecialType == SpecialType.System_Runtime_CompilerServices_RuntimeFeature
|| type.ContainingAssembly?.RuntimeSupportsNumericIntPtr == true)
{
return type;
}

return containingModule.Module.HasNativeIntegerAttribute(handle, out var transformFlags) ?
TransformType(type, transformFlags) :
type;
Expand Down Expand Up @@ -111,7 +118,7 @@ private NativeIntegerTypeDecoder(ImmutableArray<bool> transformFlags)
{
throw new UnsupportedSignatureContent();
}
return (_transformFlags[_index++], type.IsNativeIntegerType) switch
return (_transformFlags[_index++], type.IsNativeIntegerWrapperType) switch
{
(false, true) => type.NativeIntegerUnderlyingType,
(true, false) => type.AsNativeInteger(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ internal PEEventSymbol(

const int targetSymbolCustomModifierCount = 0;
var typeSymbol = DynamicTypeDecoder.TransformType(originalEventType, targetSymbolCustomModifierCount, handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, handle, moduleSymbol, _containingType);

// We start without annotation (they will be decoded below)
var type = TypeWithAnnotations.Create(typeSymbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ private void EnsureSignatureIsLoaded()
ImmutableArray<CustomModifier> customModifiersArray = CSharpCustomModifier.Convert(customModifiers);

typeSymbol = DynamicTypeDecoder.TransformType(typeSymbol, customModifiersArray.Length, _handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, _handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, _handle, moduleSymbol, _containingType);

// We start without annotations
var type = TypeWithAnnotations.Create(typeSymbol, customModifiers: customModifiersArray);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ private NamedTypeSymbol GetDeclaredBaseType(bool skipTransformsIfNecessary)

var moduleSymbol = ContainingPEModule;
TypeSymbol decodedType = DynamicTypeDecoder.TransformType(baseType, 0, _handle, moduleSymbol);
decodedType = NativeIntegerTypeDecoder.TransformType(decodedType, _handle, moduleSymbol);
decodedType = NativeIntegerTypeDecoder.TransformType(decodedType, _handle, moduleSymbol, this);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by this. Are we using the current type as the containing type of the base type?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, base type would make more sense

Copy link
Member

Choose a reason for hiding this comment

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

You're right, base type would make more sense

Should it be the containing type perhaps null, instead? In short, do we need to protect against a cycle when transforming a nested type?

Copy link
Member Author

Choose a reason for hiding this comment

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

Chuck corrected both of us. It should be the containing type ;-)

decodedType = TupleTypeDecoder.DecodeTupleTypesIfApplicable(decodedType, _handle, moduleSymbol);
baseType = (NamedTypeSymbol)NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(decodedType), _handle, moduleSymbol, accessSymbol: this, nullableContext: this).Type;
}
Expand Down Expand Up @@ -539,7 +539,7 @@ private ImmutableArray<NamedTypeSymbol> MakeDeclaredInterfaces()
EntityHandle interfaceHandle = moduleSymbol.Module.MetadataReader.GetInterfaceImplementation(interfaceImpl).Interface;
TypeSymbol typeSymbol = tokenDecoder.GetTypeOfToken(interfaceHandle);

typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, interfaceImpl, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, interfaceImpl, moduleSymbol, ContainingType);
typeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeSymbol, interfaceImpl, moduleSymbol);
typeSymbol = NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(typeSymbol), interfaceImpl, moduleSymbol, accessSymbol: this, nullableContext: this).Type;

Expand Down Expand Up @@ -2376,6 +2376,10 @@ internal override int MetadataArity
internal override NamedTypeSymbol AsNativeInteger()
{
Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr);
if (ContainingAssembly.RuntimeSupportsNumericIntPtr)
{
return this;
}

return ContainingAssembly.GetNativeIntegerType(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ private PEParameterSymbol(
}

var typeSymbol = DynamicTypeDecoder.TransformType(typeWithAnnotations.Type, countOfCustomModifiers, handle, moduleSymbol, refKind);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, handle, moduleSymbol, containingSymbol.ContainingType);
typeWithAnnotations = typeWithAnnotations.WithTypeAndModifiers(typeSymbol, typeWithAnnotations.CustomModifiers);
// Decode nullable before tuple types to avoid converting between
// NamedTypeSymbol and TupleTypeSymbol unnecessarily.
Expand Down
Loading