diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index f62b13299f9063d3bb8142c52c497d93246cdc61..b415e4d33f503f5038a9b19744c0186ba448d986 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1465,11 +1465,11 @@ private static CSDiagnosticInfo CreateReflectionTypeNotFoundError(Type type) HostObjectType.Name, useCLSCompliantNameArityEncoding: true); - symbol = new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo( + symbol = new MissingMetadataTypeSymbol.TopLevel( new MissingAssemblySymbol(AssemblyIdentity.FromAssemblyDefinition(HostObjectType.GetTypeInfo().Assembly)).Modules[0], ref mdName, - CreateReflectionTypeNotFoundError(HostObjectType), - SpecialType.None); + SpecialType.None, + CreateReflectionTypeNotFoundError(HostObjectType)); } Interlocked.CompareExchange(ref _lazyHostObjectTypeSymbol, symbol, null); diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index 8775e90ac5be8d35188d45aef06db1e8119b9097..43f6a8fee0cbef1cc149ada221822688f9e564df 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -373,13 +373,13 @@ internal virtual NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetectio internal ErrorTypeSymbol CreateCycleInTypeForwarderErrorTypeSymbol(ref MetadataTypeName emittedName) { DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_CycleInTypeForwarder, emittedName.FullName, this.Name); - return new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(this.Modules[0], ref emittedName, diagnosticInfo); + return new MissingMetadataTypeSymbol.TopLevel(this.Modules[0], ref emittedName, diagnosticInfo); } internal ErrorTypeSymbol CreateMultipleForwardingErrorTypeSymbol(ref MetadataTypeName emittedName, ModuleSymbol forwardingModule, AssemblySymbol destination1, AssemblySymbol destination2) { var diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, forwardingModule, this, emittedName.FullName, destination1, destination2); - return new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(forwardingModule, ref emittedName, diagnosticInfo); + return new MissingMetadataTypeSymbol.TopLevel(forwardingModule, ref emittedName, diagnosticInfo); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 886034e8149145d651765856c8f2dd7665d13ffe..16156165e395ca799b920118e74c6663be5314e8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -166,7 +166,7 @@ internal NamedTypeSymbol GetWellKnownType(WellKnownType type) errorInfo = new CSDiagnosticInfo(ErrorCode.ERR_PredefinedValueTupleTypeAmbiguous3, emittedName.FullName, conflicts.Item1, conflicts.Item2); } - result = new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(this.Assembly.Modules[0], ref emittedName, errorInfo, type); + result = new MissingMetadataTypeSymbol.TopLevel(this.Assembly.Modules[0], ref emittedName, type, errorInfo); } else { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 7a760a351c74b498390d2821cc6f854c2c7cab09..390da481d81909d730bf6de2c3ba9bd7b5c4b77e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -2320,19 +2320,6 @@ private sealed class PENamedTypeSymbolNonGeneric : PENamedTypeSymbol protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable; - // PROTOTYPE: Temporary approach for AsNativeInt(). - private PENamedTypeSymbolNonGeneric(PENamedTypeSymbolNonGeneric other, bool isNativeInt) : - base(other) - { - Debug.Assert(isNativeInt != other.IsNativeInt); - - _underlying = other; - IsNativeInt = isNativeInt; - - Debug.Assert(this.Equals(other)); - Debug.Assert(other.Equals(this)); - } - public override int Arity { get @@ -2358,36 +2345,20 @@ internal override int MetadataArity } } - private readonly PENamedTypeSymbolNonGeneric _underlying; - internal override bool IsNativeInt { get; } - internal override NamedTypeSymbol AsNativeInt(bool asNativeInt) { Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr); - if (IsNativeInt == asNativeInt) - { - return this; - } - - return asNativeInt ? new PENamedTypeSymbolNonGeneric(this, isNativeInt: true) : _underlying; + return asNativeInt ? + (NamedTypeSymbol)new NativeIntegerTypeSymbol(this) : // PROTOTYPE: Consider caching instance, perhaps on containing assembly. + this; } - // PROTOTYPE: Temporary approach for AsNativeInt(). internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOnlyDictionary isValueTypeOverrideOpt = null) { - if ((object)t2 == null) return false; - if ((object)t2 == this) return true; - - var other = t2 as PENamedTypeSymbolNonGeneric; - if ((object)other != null && - (this.IsNativeInt || other.IsNativeInt) && - this.SpecialType == other.SpecialType) - { - return true; - } - - return base.Equals(t2, comparison, isValueTypeOverrideOpt); + return t2 is NativeIntegerTypeSymbol nativeInteger ? + nativeInteger.Equals(this, comparison, isValueTypeOverrideOpt) : + base.Equals(t2, comparison, isValueTypeOverrideOpt); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs index 7df18086f14340125f801521747cc9fb7a0ff1c2..7b9c80f7f1b559916731dae92efea57e029b3279 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs @@ -4,14 +4,11 @@ #nullable enable +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using System; -using System.Collections.Generic; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -126,60 +123,75 @@ internal override DiagnosticInfo ErrorInfo /// /// Represents not nested missing type. /// - internal class TopLevel : MissingMetadataTypeSymbol + internal sealed class TopLevel : MissingMetadataTypeSymbol { private readonly string _namespaceName; private readonly ModuleSymbol _containingModule; + private readonly bool _isNativeInt; + private DiagnosticInfo? _lazyErrorInfo; private NamespaceSymbol? _lazyContainingNamespace; /// /// Either , , or -1 if not initialized. /// - private int _lazyTypeId = -1; + private int _lazyTypeId; - public TopLevel(ModuleSymbol module, string @namespace, string name, int arity, bool mangleName, TupleExtraData? tupleData = null) - : base(name, arity, mangleName, tupleData) + public TopLevel(ModuleSymbol module, string @namespace, string name, int arity, bool mangleName) + : this(module, @namespace, name, arity, mangleName, errorInfo: null, isNativeInt: false, containingNamespace: null, typeId: -1, tupleData: null) { - RoslynDebug.Assert((object)module != null); - RoslynDebug.Assert(@namespace != null); - - _namespaceName = @namespace; - _containingModule = module; } - public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName) - : this(module, ref fullName, -1) + public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, DiagnosticInfo? errorInfo = null) + : this(module, ref fullName, -1, errorInfo) { } - public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, SpecialType specialType) - : this(module, ref fullName, (int)specialType) + public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, SpecialType specialType, DiagnosticInfo? errorInfo = null) + : this(module, ref fullName, (int)specialType, errorInfo) { } - public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, WellKnownType wellKnownType) - : this(module, ref fullName, (int)wellKnownType) + public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, WellKnownType wellKnownType, DiagnosticInfo? errorInfo = null) + : this(module, ref fullName, (int)wellKnownType, errorInfo) { } - private TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, int typeId) - : this(module, ref fullName, fullName.ForcedArity == -1 || fullName.ForcedArity == fullName.InferredArity) + private TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, int typeId, DiagnosticInfo? errorInfo) + : this(module, ref fullName, fullName.ForcedArity == -1 || fullName.ForcedArity == fullName.InferredArity, errorInfo, typeId) { - Debug.Assert(typeId == -1 || typeId == (int)SpecialType.None || Arity == 0 || MangleName); - _lazyTypeId = typeId; } - private TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, bool mangleName) + private TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, bool mangleName, DiagnosticInfo? errorInfo, int typeId) : this(module, fullName.NamespaceName, mangleName ? fullName.UnmangledTypeName : fullName.TypeName, mangleName ? fullName.InferredArity : fullName.ForcedArity, - mangleName) + mangleName, + isNativeInt: false, + errorInfo, + containingNamespace: null, + typeId, + tupleData: null) { } + private TopLevel(ModuleSymbol module, string @namespace, string name, int arity, bool mangleName, bool isNativeInt, DiagnosticInfo? errorInfo, NamespaceSymbol? containingNamespace, int typeId, TupleExtraData? tupleData) + : base(name, arity, mangleName, tupleData) + { + RoslynDebug.Assert((object)module != null); + RoslynDebug.Assert(@namespace != null); + RoslynDebug.Assert(typeId == -1 || typeId == (int)SpecialType.None || arity == 0 || mangleName); + + _namespaceName = @namespace; + _containingModule = module; + _isNativeInt = isNativeInt; + _lazyErrorInfo = errorInfo; + _lazyContainingNamespace = containingNamespace; + _lazyTypeId = typeId; + } + protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) { - return new TopLevel(_containingModule, _namespaceName, Name, Arity, MangleName, newData); + return new TopLevel(_containingModule, _namespaceName, name, arity, mangleName, _isNativeInt, _lazyErrorInfo, _lazyContainingNamespace, _lazyTypeId, newData); } /// @@ -292,12 +304,14 @@ internal override DiagnosticInfo ErrorInfo { get { - if (this.TypeId != (int)SpecialType.None) + if (_lazyErrorInfo == null) { - return new CSDiagnosticInfo(ErrorCode.ERR_PredefinedTypeNotFound, MetadataHelpers.BuildQualifiedName(_namespaceName, MetadataName)); + var errorInfo = this.TypeId != (int)SpecialType.None ? + new CSDiagnosticInfo(ErrorCode.ERR_PredefinedTypeNotFound, MetadataHelpers.BuildQualifiedName(_namespaceName, MetadataName)) : + base.ErrorInfo; + Interlocked.CompareExchange(ref _lazyErrorInfo, errorInfo, null); } - - return base.ErrorInfo; + return _lazyErrorInfo; } } @@ -312,6 +326,22 @@ public override int GetHashCode() return Hash.Combine(MetadataName, Hash.Combine(_containingModule, Hash.Combine(_namespaceName, arity))); } + internal override NamedTypeSymbol AsNativeInt(bool asNativeInt) + { + Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr); + + var other = asNativeInt == _isNativeInt ? + this : + new TopLevel(_containingModule, _namespaceName, name, arity, mangleName, isNativeInt: asNativeInt, _lazyErrorInfo, _lazyContainingNamespace, _lazyTypeId, TupleData); + + Debug.Assert(other.Equals(this)); + Debug.Assert(other.SpecialType == this.SpecialType); + + return other; + } + + internal override bool IsNativeInt => _isNativeInt; + internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOnlyDictionary? isValueTypeOverrideOpt = null) { if (ReferenceEquals(this, t2)) @@ -338,44 +368,10 @@ internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOn } } - internal class TopLevelWithCustomErrorInfo : TopLevel - { - private readonly DiagnosticInfo _errorInfo; - - public TopLevelWithCustomErrorInfo(ModuleSymbol module, ref MetadataTypeName emittedName, DiagnosticInfo errorInfo) - : base(module, ref emittedName) - { - RoslynDebug.Assert(errorInfo != null); - _errorInfo = errorInfo; - } - - public TopLevelWithCustomErrorInfo(ModuleSymbol module, ref MetadataTypeName emittedName, DiagnosticInfo errorInfo, SpecialType typeId) - : base(module, ref emittedName, typeId) - { - RoslynDebug.Assert(errorInfo != null); - _errorInfo = errorInfo; - } - - public TopLevelWithCustomErrorInfo(ModuleSymbol module, ref MetadataTypeName emittedName, DiagnosticInfo errorInfo, WellKnownType typeId) - : base(module, ref emittedName, typeId) - { - RoslynDebug.Assert(errorInfo != null); - _errorInfo = errorInfo; - } - - internal override DiagnosticInfo ErrorInfo - { - get - { - return _errorInfo; - } - } - } - /// /// Represents nested missing type. /// - internal class Nested : MissingMetadataTypeSymbol + internal sealed class Nested : MissingMetadataTypeSymbol { private readonly NamedTypeSymbol _containingType; diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 40591f954bd18327f517ce5ab1904f8fcf65e113..b5d47d5dec7d756529dc9af4a7cd6ee6f859f74f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -1510,9 +1510,18 @@ internal bool IsTupleTypeOfCardinality(out int tupleCardinality) } // PROTOTYPE: Should be abstract. - internal virtual NamedTypeSymbol AsNativeInt(bool asNativeInt) => this; + /// + /// Returns an instance of a symbol that has set to + /// if the underlying symbol represents System.IntPtr or System.UIntPtr. + /// For other symbols, throws . + /// + internal virtual NamedTypeSymbol AsNativeInt(bool asNativeInt) => throw ExceptionUtilities.Unreachable; // PROTOTYPE: Should be abstract. + /// + /// Returns true if the symbol represents a native integer + /// (corresponding to the language tokens nint or nuint). + /// internal virtual bool IsNativeInt => false; protected override ISymbol CreateISymbol() diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs new file mode 100644 index 0000000000000000000000000000000000000000..6ba556838ab8638048a52f5f15f99e217a953dd6 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class NativeIntegerTypeSymbol : WrappedNamedTypeSymbol + { + internal NativeIntegerTypeSymbol(NamedTypeSymbol underlying) : base(underlying, tupleData: null) + { + Debug.Assert(underlying.TupleData is null); + Debug.Assert(!underlying.IsNativeInt); + Debug.Assert(underlying.SpecialType == SpecialType.System_IntPtr || underlying.SpecialType == SpecialType.System_UIntPtr); + Debug.Assert(this.Equals(underlying)); + Debug.Assert(underlying.Equals(this)); + } + + public override ImmutableArray TypeParameters => ImmutableArray.Empty; + + public override NamedTypeSymbol ConstructedFrom => this; + + public override Symbol ContainingSymbol => _underlyingType.ContainingSymbol; + + internal override ImmutableArray TypeArgumentsWithAnnotationsNoUseSiteDiagnostics => ImmutableArray.Empty; + + internal override bool IsComImport => _underlyingType.IsComImport; + + internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics => _underlyingType.BaseTypeNoUseSiteDiagnostics; + + public override SpecialType SpecialType => _underlyingType.SpecialType; + + public override IEnumerable MemberNames => Array.Empty(); + + /// + /// There are no members on that should be exposed directly on nint: + /// constructors for nint other than the default parameterless constructor are not supported; + /// operators are handled explicitly as built-in operators and conversions; + /// 0 should be used instead of Zero; + /// sizeof(nint) should be used instead of Size; + /// + and - should be used instead of Add() and Subtract(); + /// ToInt32(), ToInt64(), ToPointer() should be used from System.IntPtr only; + /// overridden methods Equals(), GetHashCode(), and ToString() are referenced from System.Object. + /// The one remaining member is which we could expose if needed. + /// + public override ImmutableArray GetMembers() => ImmutableArray.Empty; + + public override ImmutableArray GetMembers(string name) => ImmutableArray.Empty; + + public override ImmutableArray GetTypeMembers() => ImmutableArray.Empty; + + public override ImmutableArray GetTypeMembers(string name) => ImmutableArray.Empty; + + public override ImmutableArray GetTypeMembers(string name, int arity) => ImmutableArray.Empty; + + internal override NamedTypeSymbol GetDeclaredBaseType(ConsList basesBeingResolved) => _underlyingType.GetDeclaredBaseType(basesBeingResolved); + + internal override ImmutableArray GetDeclaredInterfaces(ConsList basesBeingResolved) => _underlyingType.GetDeclaredInterfaces(basesBeingResolved); + + internal override ImmutableArray GetEarlyAttributeDecodingMembers() => throw ExceptionUtilities.Unreachable; + + internal override ImmutableArray GetEarlyAttributeDecodingMembers(string name) => throw ExceptionUtilities.Unreachable; + + internal override IEnumerable GetFieldsToEmit() => throw ExceptionUtilities.Unreachable; + + internal override ImmutableArray GetInterfacesToEmit() => throw ExceptionUtilities.Unreachable; + + // PROTOTYPE: Include certain interfaces defined on the underlying type, with substitution + // of [U]IntPtr (for instance, IEquatable rather than IEquatable). + internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList basesBeingResolved = null) => ImmutableArray.Empty; + + protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable; + + public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable; + + internal override bool IsNativeInt => true; + + internal override NamedTypeSymbol AsNativeInt(bool asNativeInt) => asNativeInt ? this : _underlyingType; + + internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOnlyDictionary isValueTypeOverrideOpt = null) => _underlyingType.Equals(t2, comparison, isValueTypeOverrideOpt); + + public override int GetHashCode() => _underlyingType.GetHashCode(); + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 9b6cb0131102a0462731fb8fe33260b07b23929c..cd4bd88ce5660bc47e6b69fb8a7006929d659210 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -10,7 +10,6 @@ using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -1367,5 +1366,21 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } #endregion + + internal override NamedTypeSymbol AsNativeInt(bool asNativeInt) + { + Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr); + + return asNativeInt ? + (NamedTypeSymbol)new NativeIntegerTypeSymbol(this) : // PROTOTYPE: Consider caching instance, perhaps on containing assembly. + this; + } + + internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOnlyDictionary isValueTypeOverrideOpt = null) + { + return t2 is NativeIntegerTypeSymbol nativeInteger ? + nativeInteger.Equals(this, comparison, isValueTypeOverrideOpt) : + base.Equals(t2, comparison, isValueTypeOverrideOpt); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NativeInteger.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NativeInteger.cs index d64b85dafc7051975101d6276e655967ad04fad9..0f8600049c8fbfa6b5c7a545429d5492dfdc7edb 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NativeInteger.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NativeInteger.cs @@ -265,6 +265,22 @@ static void F(A a) comp = CreateCompilation(source1, new[] { ref0 }, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); + + var type = comp.GetTypeByMetadataName("B"); + Assert.Equal("void B.F1(A a)", type.GetMember("F1").ToDisplayString(FormatWithSpecialTypes)); + Assert.Equal("void B.F2(A a)", type.GetMember("F2").ToDisplayString(FormatWithSpecialTypes)); + Assert.Equal("void B.F3(A a)", type.GetMember("F3").ToDisplayString(FormatWithSpecialTypes)); + + var expected = +@"B + void F1(A a) + [NativeInteger({ False, True, True })] A a + void F2(A a) + [NativeInteger({ False, True })] A a + void F3(A a) + [NativeInteger({ False, False, True, True })] A a +"; + AssertNativeIntegerAttributes(type.ContainingModule, expected); } [Fact] @@ -897,7 +913,7 @@ private static CustomAttribute GetAttributeByConstructorName(MetadataReader read private static void AssertAttributes(MetadataReader reader, CustomAttributeHandleCollection handles, params string[] expectedNames) { var actualNames = handles.Select(h => GetAttributeConstructorName(reader, h)).ToArray(); - AssertEx.SetEqual(actualNames, expectedNames); + AssertEx.Equal(actualNames, expectedNames); } private static void AssertNoNativeIntegerAttribute(ImmutableArray attributes) @@ -913,7 +929,7 @@ private static void AssertNativeIntegerAttribute(ImmutableArray attributes, params string[] expectedNames) { var actualNames = attributes.Select(a => a.AttributeClass.ToTestDisplayString()).ToArray(); - AssertEx.SetEqual(actualNames, expectedNames); + AssertEx.Equal(actualNames, expectedNames); } private static void AssertNoNativeIntegerAttributes(CSharpCompilation comp) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs index d4d94e928ff520d5276ebd5f23a29a0de7b97aad..90c4d88659f1dc50acfd25f1d53af91e6e37f8de 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntTests.cs @@ -15,6 +15,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class NativeIntTests : CSharpTestBase { + private static readonly SymbolDisplayFormat FormatWithSpecialTypes = SymbolDisplayFormat.TestFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + [Fact] public void LanguageVersion() { @@ -40,12 +42,252 @@ public void LanguageVersion() comp.VerifyDiagnostics(); } + /// + /// System.IntPtr and System.UIntPtr definitions from metadata. + /// + [Fact] + public void TypeDefinitions_FromMetadata() + { + var source = +@"interface I +{ + void F1(System.IntPtr x, nint y); + void F2(System.UIntPtr x, nuint y); +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + var type = comp.GetTypeByMetadataName("System.IntPtr"); + VerifyType(type, signed: true, isNativeInt: false); + VerifyType(type.GetPublicSymbol(), signed: true, isNativeInt: false); + + type = comp.GetTypeByMetadataName("System.UIntPtr"); + VerifyType(type, signed: false, isNativeInt: false); + VerifyType(type.GetPublicSymbol(), signed: false, isNativeInt: false); + + var method = comp.GetMember("I.F1"); + Assert.Equal("void I.F1(System.IntPtr x, nint y)", method.ToDisplayString(FormatWithSpecialTypes)); + VerifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: true); + + method = comp.GetMember("I.F2"); + Assert.Equal("void I.F2(System.UIntPtr x, nuint y)", method.ToDisplayString(FormatWithSpecialTypes)); + VerifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: false); + } + + /// + /// System.IntPtr and System.UIntPtr definitions from source. + /// + [Fact] + public void TypeDefinitions_FromSource() + { + var sourceA = +@"namespace System +{ + public class Object { } + public abstract class ValueType { } + public struct Void { } + public struct IntPtr { } + public struct UIntPtr { } +}"; + var sourceB = +@"interface I +{ + void F1(System.IntPtr x, nint y); + void F2(System.UIntPtr x, nuint y); +}"; + var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + verify(comp); + + comp = CreateEmptyCompilation(sourceA); + comp.VerifyDiagnostics(); + var ref1 = comp.ToMetadataReference(); + var ref2 = comp.EmitToImageReference(); + + comp = CreateEmptyCompilation(sourceB, references: new[] { ref1 }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + verify(comp); + + comp = CreateEmptyCompilation(sourceB, references: new[] { ref2 }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var type = comp.GetTypeByMetadataName("System.IntPtr"); + VerifyType(type, signed: true, isNativeInt: false); + VerifyType(type.GetPublicSymbol(), signed: true, isNativeInt: false); + + type = comp.GetTypeByMetadataName("System.UIntPtr"); + VerifyType(type, signed: false, isNativeInt: false); + VerifyType(type.GetPublicSymbol(), signed: false, isNativeInt: false); + + var method = comp.GetMember("I.F1"); + Assert.Equal("void I.F1(System.IntPtr x, nint y)", method.ToDisplayString(FormatWithSpecialTypes)); + VerifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: true); + + method = comp.GetMember("I.F2"); + Assert.Equal("void I.F2(System.UIntPtr x, nuint y)", method.ToDisplayString(FormatWithSpecialTypes)); + VerifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: false); + } + } + + private static void VerifyType(NamedTypeSymbol type, bool signed, bool isNativeInt) + { + var specialType = signed ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr; + + Assert.Equal(specialType, type.SpecialType); + Assert.Equal(SymbolKind.NamedType, type.Kind); + Assert.Equal(TypeKind.Struct, type.TypeKind); + Assert.Same(type, type.ConstructedFrom); + Assert.Equal(isNativeInt, type.IsNativeInt); + } + + private static void VerifyType(INamedTypeSymbol type, bool signed, bool isNativeInt) + { + var specialType = signed ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr; + + Assert.Equal(specialType, type.SpecialType); + Assert.Equal(SymbolKind.NamedType, type.Kind); + Assert.Equal(TypeKind.Struct, type.TypeKind); + Assert.Same(type, type.ConstructedFrom); + } + + private static void VerifyTypes(INamedTypeSymbol underlyingType, INamedTypeSymbol nativeIntegerType, bool signed) + { + VerifyType(underlyingType, signed, isNativeInt: false); + VerifyType(nativeIntegerType, signed, isNativeInt: false); + + Assert.Empty(nativeIntegerType.MemberNames); + Assert.Empty(nativeIntegerType.GetTypeMembers()); + Assert.Empty(nativeIntegerType.GetMembers()); + + // PROTOTYPE: Include certain interfaces defined on the underlying underlyingType. + Assert.Empty(nativeIntegerType.Interfaces); + + Assert.NotSame(underlyingType, nativeIntegerType); + Assert.Equal(underlyingType, nativeIntegerType); + Assert.Equal(nativeIntegerType, underlyingType); + Assert.Equal(underlyingType.GetHashCode(), nativeIntegerType.GetHashCode()); + } + + private static void VerifyTypes(NamedTypeSymbol underlyingType, NamedTypeSymbol nativeIntegerType, bool signed) + { + VerifyType(underlyingType, signed, isNativeInt: false); + VerifyType(nativeIntegerType, signed, isNativeInt: true); + + Assert.Empty(nativeIntegerType.MemberNames); + Assert.Empty(nativeIntegerType.GetTypeMembers()); + Assert.Empty(nativeIntegerType.GetMembers()); + + // PROTOTYPE: Include certain interfaces defined on the underlying underlyingType. + Assert.Empty(nativeIntegerType.InterfacesNoUseSiteDiagnostics()); + + Assert.Same(underlyingType, underlyingType.AsNativeInt(false)); + Assert.Same(nativeIntegerType, nativeIntegerType.AsNativeInt(true)); + Assert.Equal(nativeIntegerType, underlyingType.AsNativeInt(true)); + Assert.Equal(underlyingType, nativeIntegerType.AsNativeInt(false)); + VerifyEqualButDistinct(underlyingType, underlyingType.AsNativeInt(true)); + VerifyEqualButDistinct(nativeIntegerType, nativeIntegerType.AsNativeInt(false)); + VerifyEqualButDistinct(underlyingType, nativeIntegerType); + + VerifyTypes(underlyingType.GetPublicSymbol(), nativeIntegerType.GetPublicSymbol(), signed); + } + + private static void VerifyEqualButDistinct(NamedTypeSymbol underlyingType, NamedTypeSymbol nativeIntegerType) + { + Assert.NotSame(underlyingType, nativeIntegerType); + Assert.Equal(underlyingType, nativeIntegerType); + Assert.Equal(nativeIntegerType, underlyingType); + Assert.True(underlyingType.Equals(nativeIntegerType, TypeCompareKind.ConsiderEverything)); + Assert.True(nativeIntegerType.Equals(underlyingType, TypeCompareKind.ConsiderEverything)); + Assert.Equal(underlyingType.GetHashCode(), nativeIntegerType.GetHashCode()); + } + + [Fact] + public void MissingTypes() + { + var sourceA = +@"namespace System +{ + public class Object { } + public abstract class ValueType { } + public struct Void { } +}"; + var sourceB = +@"interface I +{ + void F1(System.IntPtr x, nint y); + void F2(System.UIntPtr x, nuint y); +}"; + var diagnostics = new[] + { + // (3,20): error CS0234: The underlyingType or namespace name 'IntPtr' does not exist in the namespace 'System' (are you missing an assembly reference?) + // void F1(System.IntPtr x, nint y); + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IntPtr").WithArguments("IntPtr", "System").WithLocation(3, 20), + // (3,30): error CS0518: Predefined underlyingType 'System.IntPtr' is not defined or imported + // void F1(System.IntPtr x, nint y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "nint").WithArguments("System.IntPtr").WithLocation(3, 30), + // (4,20): error CS0234: The underlyingType or namespace name 'UIntPtr' does not exist in the namespace 'System' (are you missing an assembly reference?) + // void F2(System.UIntPtr x, nuint y); + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "UIntPtr").WithArguments("UIntPtr", "System").WithLocation(4, 20), + // (4,31): error CS0518: Predefined underlyingType 'System.UIntPtr' is not defined or imported + // void F2(System.UIntPtr x, nuint y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "nuint").WithArguments("System.UIntPtr").WithLocation(4, 31) + }; + + var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(diagnostics); + verify(comp); + + comp = CreateEmptyCompilation(sourceA); + comp.VerifyDiagnostics(); + var ref1 = comp.ToMetadataReference(); + var ref2 = comp.EmitToImageReference(); + + comp = CreateEmptyCompilation(sourceB, references: new[] { ref1 }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(diagnostics); + verify(comp); + + comp = CreateEmptyCompilation(sourceB, references: new[] { ref2 }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(diagnostics); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var method = comp.GetMember("I.F1"); + Assert.Equal("void I.F1(System.IntPtr x, nint y)", method.ToDisplayString(FormatWithSpecialTypes)); + verifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: true); + + method = comp.GetMember("I.F2"); + Assert.Equal("void I.F2(System.UIntPtr x, nuint y)", method.ToDisplayString(FormatWithSpecialTypes)); + verifyTypes((NamedTypeSymbol)method.Parameters[0].Type, (NamedTypeSymbol)method.Parameters[1].Type, signed: false); + } + + static void verifyTypes(NamedTypeSymbol underlyingType, NamedTypeSymbol nativeIntegerType, bool signed) + { + Assert.Equal(SpecialType.None, underlyingType.SpecialType); + Assert.Equal(SymbolKind.ErrorType, underlyingType.Kind); + Assert.Equal(TypeKind.Error, underlyingType.TypeKind); + Assert.False(underlyingType.IsNativeInt); + + var specialType = signed ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr; + Assert.Equal(specialType, nativeIntegerType.SpecialType); + Assert.Equal(SymbolKind.ErrorType, nativeIntegerType.Kind); + Assert.Equal(TypeKind.Error, nativeIntegerType.TypeKind); + Assert.True(nativeIntegerType.IsNativeInt); + Assert.Same(nativeIntegerType, nativeIntegerType.AsNativeInt(true)); + VerifyEqualButDistinct(nativeIntegerType, nativeIntegerType.AsNativeInt(false)); + } + } + // PROTOTYPE: Test: // - @nint // - Type.nint, Namespace.nint // - BindNonGenericSimpleNamespaceOrTypeOrAliasSymbol has the comment "dynamic not allowed as an attribute type". Does that apply to "nint"? // - BindNonGenericSimpleNamespaceOrTypeOrAliasSymbol checks IsViableType(result) // - Use-site diagnostics (basically any use-site diagnostics from IntPtr/UIntPtr) + // - Type unification of I and I [Fact] public void ClassName() @@ -75,17 +317,17 @@ static void verify(CSharpCompilation comp) var tree = comp.SyntaxTrees[0]; var nodes = tree.GetRoot().DescendantNodes().ToArray(); var model = comp.GetSemanticModel(tree); - var type = model.GetDeclaredSymbol(nodes.OfType().Single()); - Assert.Equal("nint", type.ToTestDisplayString()); - Assert.Equal(SpecialType.None, type.SpecialType); + var underlyingType = model.GetDeclaredSymbol(nodes.OfType().Single()); + Assert.Equal("nint", underlyingType.ToTestDisplayString()); + Assert.Equal(SpecialType.None, underlyingType.SpecialType); var method = model.GetDeclaredSymbol(nodes.OfType().Single()); Assert.Equal("nint I.Add(nint x, System.UIntPtr y)", method.ToTestDisplayString()); - var type0 = method.Parameters[0].Type; - var type1 = method.Parameters[1].Type; - Assert.Equal(SpecialType.None, type0.SpecialType); - Assert.False(IsNativeInt(type0)); - Assert.Equal(SpecialType.System_UIntPtr, type1.SpecialType); - Assert.True(IsNativeInt(type1)); + var underlyingType0 = method.Parameters[0].Type.GetSymbol(); + var underlyingType1 = method.Parameters[1].Type.GetSymbol(); + Assert.Equal(SpecialType.None, underlyingType0.SpecialType); + Assert.False(underlyingType0.IsNativeInt); + Assert.Equal(SpecialType.System_UIntPtr, underlyingType1.SpecialType); + Assert.True(underlyingType1.IsNativeInt); } } @@ -112,25 +354,17 @@ interface I static void verify(CSharpCompilation comp) { - var tree = comp.SyntaxTrees[0]; - var nodes = tree.GetRoot().DescendantNodes().ToArray(); - var model = comp.GetSemanticModel(tree); - var method = model.GetDeclaredSymbol(nodes.OfType().Single()); + var method = comp.GetMember("I.Add"); Assert.Equal("System.Int16 I.Add(System.Int16 x, System.UIntPtr y)", method.ToTestDisplayString()); - var type0 = method.Parameters[0].Type; - var type1 = method.Parameters[1].Type; - Assert.Equal(SpecialType.System_Int16, type0.SpecialType); - Assert.False(IsNativeInt(type0)); - Assert.Equal(SpecialType.System_UIntPtr, type1.SpecialType); - Assert.True(IsNativeInt(type1)); + var underlyingType0 = (NamedTypeSymbol)method.Parameters[0].Type; + var underlyingType1 = (NamedTypeSymbol)method.Parameters[1].Type; + Assert.Equal(SpecialType.System_Int16, underlyingType0.SpecialType); + Assert.False(underlyingType0.IsNativeInt); + Assert.Equal(SpecialType.System_UIntPtr, underlyingType1.SpecialType); + Assert.True(underlyingType1.IsNativeInt); } } - private static bool IsNativeInt(ITypeSymbol type) - { - return type.GetSymbol()?.IsNativeInt == true; - } - // PROTOTYPE: nint and nuint should be allowed. [Fact] public void MemberName() @@ -364,15 +598,15 @@ static void verifyOperators(CSharpCompilation comp) _ = operators.Single(op => isNullableNativeInt(op.LeftType, signed: false)); } - static bool isNativeInt(TypeSymbol type, bool signed) + static bool isNativeInt(TypeSymbol underlyingType, bool signed) { - return type is NamedTypeSymbol { IsNativeInt: true } && - type.SpecialType == (signed ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr); + return underlyingType is NamedTypeSymbol { IsNativeInt: true } && + underlyingType.SpecialType == (signed ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr); } - static bool isNullableNativeInt(TypeSymbol type, bool signed) + static bool isNullableNativeInt(TypeSymbol underlyingType, bool signed) { - return type.IsNullableType() && isNativeInt(type.GetNullableUnderlyingType(), signed); + return underlyingType.IsNullableType() && isNativeInt(underlyingType.GetNullableUnderlyingType(), signed); } } } @@ -464,7 +698,7 @@ void conversions(string sourceType, string destType, string expectedImplicitIL, useChecked: true, expectedCheckedIL is null ? ErrorCode.ERR_NoExplicitConv : 0); - static bool usesIntPtrOrUIntPtr(string type) => type.Contains("IntPtr"); + static bool usesIntPtrOrUIntPtr(string underlyingType) => underlyingType.Contains("IntPtr"); } conversions(sourceType: "object", destType: "nint", expectedImplicitIL: null, @@ -1420,12 +1654,12 @@ .maxstack 1 var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var expr = tree.GetRoot().DescendantNodes().OfType().Single().Expression; - var typeInfo = model.GetTypeInfo(expr); + var underlyingTypeInfo = model.GetTypeInfo(expr); if (!skipTypeChecks) { - Assert.Equal(sourceType, typeInfo.Type.ToString()); - Assert.Equal(destType, typeInfo.ConvertedType.ToString()); + Assert.Equal(sourceType, underlyingTypeInfo.Type.ToString()); + Assert.Equal(destType, underlyingTypeInfo.ConvertedType.ToString()); } if (expectedIL != null) @@ -1434,7 +1668,7 @@ .maxstack 1 verifier.VerifyIL("Program.Convert", expectedIL); } - static bool useUnsafe(string type) => type == "void*"; + static bool useUnsafe(string underlyingType) => underlyingType == "void*"; } // PROTOTYPE:Test pre- and postfix increment and decrement. See UnopEasyOut.s_increment. @@ -2654,7 +2888,7 @@ private void BinaryOperator(string op, string leftType, string rightType, string CompileAndVerify(comp); } - static bool useUnsafe(string type) => type == "void*"; + static bool useUnsafe(string underlyingType) => underlyingType == "void*"; } [Fact]